Enable panel-live in HTML and Markdown pane#28
Merged
MarcSkovMadsen merged 3 commits intomainfrom Feb 22, 2026
Merged
Conversation
…into enhancement/html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR: Shadow DOM support, panes reference app, and prerender BrokenPipeError fix
Summary
<panel-live>now works insidepn.pane.HTML,pn.pane.Markdown, andpn.chat.ChatInterface— Panel panes that render inside Bokeh's Shadow DOM.panel-live serveroute documenting the three embedding patterns.BrokenPipeErrortracebacks when geoviews (or any slow import) times out.What changed
Shadow DOM support (
lib/panel-live-element.js)Bokeh's
embed_items()callsdocument.getElementById()on the main document. When<panel-live>is inside a Shadow DOM (as happens withpn.pane.HTMLandpn.pane.Markdown), the output container#el-XXXXis invisible to the main-document lookup and Bokeh's render fails silently.Two module-level helpers fix this:
_patchGetElementByIdForPL(shadowRoot)— wrapsdocument.getElementByIdonce so it falls back to searching all known shadow roots. Idempotent: a second<panel-live>connecting to a different shadow root simply adds that root to the set; the wrapper is not double-applied._injectPanelLiveCSSIntoShadowRoot(shadowRoot)— clones thepanel-live.css<link>from<head>into the shadow root so the component renders correctly. Falls back to deriving the CSS URL from thepanel-live.js<script>src if no<link>is found.Both run synchronously in
connectedCallbackbefore therequestAnimationFramerender block. Detection is automatic — no attribute or configuration needed.How-to guide (
docs/how-to/panel-panes.md)New page documenting three embedding patterns:
pn.pane.HTML— raw<panel-live>string, editor modepn.pane.Markdown— passes raw HTML blocks through unchangedpn.chat.ChatInterface— callback returnspn.pane.HTML('<panel-live ...>')Covers three asset-loading options (GitHub Pages CDN, jsDelivr npm CDN, local
--static-dirs). Added to nav under "Panel Component" inzensical.toml. Cross-referenced frompanel-component.mdandgetting-started-panel.md.Reference app (
src/panel_live/examples/panes.py)Three-section
pn.Accordionapp modelled onshowcase.py, demonstrating HTML pane, Markdown pane, and ChatInterface. Usespn.extension(js_files=..., css_files=...)to load the web component from CDN — noPanelLiveJSComponent import.panel-live servenow serves both:http://localhost:5008/— Showcase (all 6 modes + server RPC)http://localhost:5008/panes— HTML, Markdown & ChatInterface panesPrerender BrokenPipeError fix (
src/panel_live/prerender.py)proc.join(timeout=N)returns without killing the subprocess when the timeout expires. The subprocess keeps running; when it eventually finishes it triesconn.send(), but the parent's pipe end has been closed →BrokenPipeErrorprinted to the terminal (seen with slow/broken imports likeimport geoviews as gv).Fix:
JS unit tests (
tests/js/unit/panel-live-element.test.js)7 new tests covering the shadow DOM helpers, ordered to respect module-level state accumulation:
getElementByIdgetElementById; original lookups still work<head>link clone, script-src fallback, and no-crash when neither presentPython test update (
tests/test_prerender.py)test_pre_render_timeout_forwarded— addsis_alive.return_value = Falseto correctly model a finished processtest_pre_render_timeout_terminates_subprocess— new test: assertsproc.terminate()is called andproc.join(timeout=5)is invoked after a timeoutFiles changed
13 files, ~742 insertions in this increment:
lib/panel-live-element.js— shadow DOM helpers +connectedCallbackdetectiondocs/how-to/panel-panes.md— new how-to guide (created)src/panel_live/examples/panes.py— new reference app (created)src/panel_live/cli.py—/panesroute + updated print outputzensical.toml— nav entry "HTML & Markdown Panes"docs/how-to/panel-component.md— "See also" cross-referencedocs/tutorials/getting-started-panel.md— "Next Steps" cross-referencetests/js/unit/panel-live-element.test.js— 7 new JS tests (created)src/panel_live/prerender.py— terminate subprocess on timeouttests/test_prerender.py— updated + 1 new testTesting
pixi run test— 179 passed, 4 skippedpixi run test-js— 185 passed (13 test files)pixi run lint— all checks passed