Context
We migrated the documentation of our tool from
Material for MkDocs to Zensical. The homepage embeds an interactive Plotly figure generated at build time by markdown-exec's documented pattern (fig.to_html(full_html=False, include_plotlyjs="cdn")), which emits an external <script src="https://cdn.plot.ly/..."> followed by an inline <script>Plotly.newPlot(...)</script> in the page content. After the migration the figure was reported as "gone" — it turned out to render fine on hard loads but to disappear whenever the page was reached through instant navigation.
Bug description
[thank you to Claude for helping me write this up]
When instant navigation injects a new page, author scripts inside the content container are re-evaluated by cloning each <script> element (inject() in src/assets/javascripts/integrations/instant/index.ts). Two defects in that block break common content patterns:
-
Execution order is not preserved. The script list is piped through concat(...).pipe(switchMap(...)). Because the list is emitted synchronously, switchMap unsubscribes the script.onload completion of an external (src) script the moment the next element is emitted — and a dynamically inserted script loads asynchronously anyway. A following inline script therefore always executes before a preceding external script has loaded (deterministically so, since the external script cannot run until the current task yields). For the Plotly pattern above this throws ReferenceError: Plotly is not defined and leaves an empty figure.
-
Inline clones lose all attributes. The src branch copies attributes onto the clone, but the inline branch copies only textContent. Inert data payloads such as <script type="application/json">…</script> are therefore re-injected as plain executable scripts: the JSON is parsed as JavaScript and throws right inside inject() (Failed to execute 'replaceWith' on 'Element': Unexpected token ':'), and selectors like script[type="application/json"] no longer match after the swap.
Both defects only manifest on instant navigation; a hard load of the same page behaves correctly, which makes the breakage easy to miss and confusing to reproduce ("sometimes the chart is gone").
Related links
Reproduction
zensical-repro.zip
Steps to reproduce
- Unzip the attached reproduction (the three files above),
pip install zensical==0.0.50 (zensical-ui 0.0.22), zensical serve.
- Open
http://localhost:8000/chart/ directly (hard load): the Plotly chart renders, console is clean.
- Open
http://localhost:8000/ and click Chart in the navigation (instant navigation): the chart is missing and the console shows ReferenceError: Plotly is not defined (defect 1) and Failed to execute 'replaceWith' on 'Element': Unexpected token ':' (defect 2, from the application/json script losing its type).
Browser
No response
Before submitting
Context
We migrated the documentation of our tool from
Material for MkDocs to Zensical. The homepage embeds an interactive Plotly figure generated at build time by markdown-exec's documented pattern (
fig.to_html(full_html=False, include_plotlyjs="cdn")), which emits an external<script src="https://cdn.plot.ly/...">followed by an inline<script>Plotly.newPlot(...)</script>in the page content. After the migration the figure was reported as "gone" — it turned out to render fine on hard loads but to disappear whenever the page was reached through instant navigation.Bug description
[thank you to Claude for helping me write this up]
When instant navigation injects a new page, author scripts inside the content container are re-evaluated by cloning each
<script>element (inject()insrc/assets/javascripts/integrations/instant/index.ts). Two defects in that block break common content patterns:Execution order is not preserved. The script list is piped through
concat(...).pipe(switchMap(...)). Because the list is emitted synchronously,switchMapunsubscribes thescript.onloadcompletion of an external (src) script the moment the next element is emitted — and a dynamically inserted script loads asynchronously anyway. A following inline script therefore always executes before a preceding external script has loaded (deterministically so, since the external script cannot run until the current task yields). For the Plotly pattern above this throwsReferenceError: Plotly is not definedand leaves an empty figure.Inline clones lose all attributes. The
srcbranch copies attributes onto the clone, but the inline branch copies onlytextContent. Inert data payloads such as<script type="application/json">…</script>are therefore re-injected as plain executable scripts: the JSON is parsed as JavaScript and throws right insideinject()(Failed to execute 'replaceWith' on 'Element': Unexpected token ':'), and selectors likescript[type="application/json"]no longer match after the swap.Both defects only manifest on instant navigation; a hard load of the same page behaves correctly, which makes the breakage easy to miss and confusing to reproduce ("sometimes the chart is gone").
Related links
document$GLightbox is not re-bound after instant navigation — images open the raw URL instead of a lightbox #157 (GLightbox not re-bound after instant navigation) — related
instant-navigation re-initialization issues
mkdocs-plotlyplugin functionality backlog#113 (Plotly plugin support request)Reproduction
zensical-repro.zip
Steps to reproduce
pip install zensical==0.0.50(zensical-ui 0.0.22),zensical serve.http://localhost:8000/chart/directly (hard load): the Plotly chart renders, console is clean.http://localhost:8000/and click Chart in the navigation (instant navigation): the chart is missing and the console showsReferenceError: Plotly is not defined(defect 1) andFailed to execute 'replaceWith' on 'Element': Unexpected token ':'(defect 2, from theapplication/jsonscript losing itstype).Browser
No response
Before submitting