Skip to content

Commit 93b6553

Browse files
Load codehilite.css faster (cache headers, file name hash cache-busting) (#5497)
### Motivation While working on #5493 I also noticed that, full cached, NiceGUI documentation reaches out to: - `index.html` (obviously) - `codehilite.css` The latter we should be able to get rid of? ### Implementation - Filename may change so we do `${this.filename}` with the accompanying props. - Compute the file name and return the response using `_generate_codehilite_css()`, which is shared. - [ ] (Questionable) Apply `@lru_cache(maxsize=1)` to `_generate_codehilite_css()` because it doesn't ever change in runtime (an asusmption, is this right?) ### Progress - [x] I chose a meaningful title that completes the sentence: "If applied, this PR will..." - [x] The implementation is complete. - [x] Pytests have been added (or are not necessary). - [x] Documentation has been added (or is not necessary). ### Results Slow 4G no CPU throttling (because network is the bottleneck) Before: 1.46s load After: 1.00s load --------- Co-authored-by: Falko Schindler <falko@zauberzeug.com>
1 parent cb7325c commit 93b6553

File tree

3 files changed

+42
-28
lines changed

3 files changed

+42
-28
lines changed

nicegui/elements/markdown.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default {
44
template: `<div></div>`,
55
async mounted() {
66
await this.$nextTick(); // NOTE: wait for window.path_prefix to be set
7-
await loadResource(window.path_prefix + `${this.dynamic_resource_path}/codehilite.css`);
7+
await loadResource(window.path_prefix + `${this.dynamic_resource_path}/${this.resource_name}`);
88
if (this.use_mermaid) {
99
this.mermaid = (await import("nicegui-mermaid")).mermaid;
1010
this.mermaid.initialize({ startOnLoad: false });
@@ -54,6 +54,7 @@ export default {
5454
},
5555
props: {
5656
dynamic_resource_path: String,
57+
resource_name: String,
5758
use_mermaid: {
5859
required: false,
5960
default: false,

nicegui/elements/markdown.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import hashlib
12
import os
23
from functools import lru_cache
34

45
import markdown2
56
from fastapi.responses import PlainTextResponse
67
from pygments.formatters import HtmlFormatter # pylint: disable=no-name-in-module
78

9+
from .. import core
810
from .mixins.content_element import ContentElement
911

1012

@@ -27,15 +29,25 @@ def __init__(self,
2729
if 'mermaid' in extras:
2830
self._props['use_mermaid'] = True
2931

32+
codehilite = self._generate_codehilite_css()
33+
self._props['resource_name'] = f'codehilite_{hashlib.sha256(codehilite.encode()).hexdigest()[:32]}.css'
3034
self.add_dynamic_resource(
31-
'codehilite.css',
35+
self._props['resource_name'],
3236
lambda: PlainTextResponse(
33-
HtmlFormatter(nobackground=True).get_style_defs('.codehilite') +
34-
HtmlFormatter(nobackground=True, style='github-dark').get_style_defs('.body--dark .codehilite'),
37+
codehilite,
3538
media_type='text/css',
39+
headers={'Cache-Control': core.app.config.cache_control_directives},
3640
),
3741
)
3842

43+
@staticmethod
44+
@lru_cache(maxsize=1)
45+
def _generate_codehilite_css() -> str:
46+
return (
47+
HtmlFormatter(nobackground=True).get_style_defs('.codehilite') +
48+
HtmlFormatter(nobackground=True, style='github-dark').get_style_defs('.body--dark .codehilite')
49+
)
50+
3951
def _handle_content_change(self, content: str) -> None:
4052
html = prepare_content(content, extras=' '.join(self.extras))
4153
if self._props.get('innerHTML') != html:

tests/test_user_simulation.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -378,22 +378,23 @@ def page():
378378

379379
await user.open('/')
380380
output = str(user.current_layout)
381-
assert output == '''
382-
q-layout
383-
q-page-container
384-
q-page
385-
div
386-
Label [markers=first, text=Hello]
387-
Row
388-
Column
389-
Button [markers=second, label=World]
390-
Icon [markers=third, name=thumbs-up]
391-
Avatar [icon=star]
392-
Input [value=typed, label=some input, for=c10, placeholder=type here, type=text]
393-
Markdown [content=## Markdown...]
394-
Card
395-
Image [src=/image.jpg]
396-
'''.strip()
381+
pattern = textwrap.dedent(r'''
382+
q-layout
383+
q-page-container
384+
q-page
385+
div
386+
Label \[markers=first, text=Hello\]
387+
Row
388+
Column
389+
Button \[markers=second, label=World\]
390+
Icon \[markers=third, name=thumbs-up\]
391+
Avatar \[icon=star\]
392+
Input \[value=typed, label=some input, for=c10, placeholder=type here, type=text\]
393+
Markdown \[content=\#\# Markdown..., resource_name=[^\]]+\]
394+
Card
395+
Image \[src=/image.jpg\]
396+
''').strip()
397+
assert re.fullmatch(pattern, output) is not None
397398

398399

399400
async def test_combined_filter_parameters(user: User) -> None:
@@ -626,14 +627,14 @@ def page():
626627

627628
await user.open('/')
628629
output = str(user.current_layout)
629-
assert output == '''
630-
q-layout
631-
q-page-container
632-
q-page
633-
div
634-
Label [text=Visible]
635-
Label [text=Hidden, visible=False]
636-
'''.strip()
630+
assert output == textwrap.dedent('''
631+
q-layout
632+
q-page-container
633+
q-page
634+
div
635+
Label [text=Visible]
636+
Label [text=Hidden, visible=False]
637+
''').strip()
637638

638639

639640
async def test_typing_to_disabled_element(user: User) -> None:

0 commit comments

Comments
 (0)