Add Markdown fenced block support for embedding charts#8
Merged
Conversation
…mponent Adds an HTML-in-Markdown authoring path for the Zensical docs: an author writes a plain Vega-Lite spec (the existing intermediate language) and it renders live as an interactive chart, with the unified preset and light/dark theme tracking injected automatically. No bespoke config schema. Two authoring syntaxes, both producing the same element: - Raw `<molplot-chart>` custom element with the spec in a nested `<script type="application/json">` block (works via md_in_html). - A `pymdownx.superfences` `molplot` fenced block that compiles to that element at build time (a pure text transform; it does not draw the chart). Web component (core/src/element.ts): - `<molplot-chart>` wraps the existing RawChart; light DOM; lifecycle wired to connected/disconnected/attributeChanged. `preset`/`theme`/`spec` attributes. The class is defined lazily inside `defineMolplotChart()` so importing the library never evaluates `class extends HTMLElement`, keeping it import-side-effect-free and SSR-safe. - New self-contained browser bundle: a bundled `elements` rslib entry (autoExternal off) that self-registers the element; vega-embed stays a lazily code-split chunk, so registration is cheap and the runtime only downloads when a chart mounts. Exposed as the `./elements` package export. - RawChart gains optional `preset`/`theme` config fields (backward compatible) so a named preset can be injected while preserving dark-mode tracking. Markdown fence (python/src/molplot/mdx.py): - `molplot_fence` formatter emits the `<molplot-chart>` element (wrapped in a block-level `<div class="molplot">` so md_in_html leaves it intact), and a `molplot_validator` that whitelists `preset`/`theme`/`type` options. - zensical.toml loads the elements bundle via extra_javascript and registers the fence; because declaring markdown_extensions replaces Zensical's defaults, the full default set is re-listed alongside the new fence. Tests: headless element/RawChart coverage (core) and formatter/validator coverage (python); new docs page getting-started/markdown.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwRZiMJXPcHWZLDUhduxmC
The preset's categorical palette drives both the web Vega range and the
matplotlib prop_cycle, but it shipped d3's category20 — which the generated
mplstyle then layered *over* scienceplots' own colours, so browser and paper
figures didn't actually match. Re-derive the tokens from scienceplots so the
"one preset, two renderers" promise holds.
- Categorical palette → scienceplots' standard seven-colour "science" cycle
(0c5da5, 00b945, ff9500, ff2c00, 845b97, 474747, 9e9e9e), shared by both
presets so web and paper stay colour-consistent.
- `molplot` default now matches the science base: serif type, 1.0 pt lines,
black-on-white axes. `molplot-paper` keeps its tighter nature-sized figure
and higher DPI, with markers at 3 pt.
- The Python side still layers this overlay on the scienceplots base, so it
also inherits science's tick geometry (direction in, minor ticks, top/right).
Regenerated core/src/presets/generated.ts, the Python _generated.py, and the
four .mplstyle files from the two hand-edited presets/*.json. Updated the
palette assertions in the TS/Python tests and the palette references in the
docs. Full stack verified: molplot.use("molplot") renders lines in the science
cycle with serif type and 1.0 pt strokes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FwRZiMJXPcHWZLDUhduxmC
The test-python CI job installs `.[dev]`, but pyyaml lived only in the PEP-735 `doc` dependency group, so `import yaml` failed in molplot.mdx during CI and the YAML-fed test_mdx.py cases hit the error branch (3 failed, 40 passed). It passed locally only because pyyaml happened to be installed system-wide. Declare pyyaml where it belongs: a new `mdx` extra (the fence formatter's runtime dep) and add it to `dev` so the test env installs it. The `doc` group already lists pyyaml for the Zensical build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwRZiMJXPcHWZLDUhduxmC
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.
Summary
Adds a
molplotfenced code block for Markdown documentation, allowing authors to embed live Vega-Lite charts directly in docs by writing a spec in YAML or JSON. The fence is a build-time text transform that compiles to a<molplot-chart>Web Component, which renders in the browser via the self-registering custom element.Key Changes
Python Markdown formatter (
python/src/molplot/mdx.py): New module implementingpymdownx.superfencescustom fence supportrender_element()— converts a Vega-Lite spec (YAML/JSON) to a<molplot-chart>HTML element with the spec embedded in a<script type="application/json">blockmolplot_fence()— superfences formatter entry pointmolplot_validator()— validates fence-header options (preset,theme,type)Web Component (
core/src/element.ts): New<molplot-chart>custom elementdefineMolplotChart()— registers the element (idempotent, browser-only, no side effects on import)parseSpec()— reads spec from child<script type="application/json">orspecattributeRawChartto inject unified preset and theme trackingmolplot:readyevent when chart mountsDocumentation (
docs/getting-started/markdown.md): New guide coveringpreset,theme)<molplot-chart>element for hand-written HTMLzensical.toml)Build configuration
core/rslib.config.ts: Newelementslibrary export (self-contained bundle with vega-embed bundled, vega lazy-loaded)core/src/element_entry.ts: Entry point for elements bundlecore/package.json: Export map for./elementssubpathzensical.toml: Load elements bundle via CDN, register molplot fence with validatorPreset refinements: Updated descriptions and typography
molplotpreset: Changed default font to serif (Times New Roman), reduced line width from 2 to 1 pt, updated categorical palette to scienceplots' standard 7-colour cycle, adjusted light/dark theme colors for better contrastmolplot-paperpreset: Updated description to clarify layering on both 'science' and 'nature' basescore/src/presets/generated.ts,python/src/molplot/presets/_generated.py,.mplstylefiles) regeneratedTests
python/tests/test_mdx.py: Comprehensive fence formatter tests (YAML/JSON parsing, option forwarding, error handling, validator)core/tests/element.test.ts:parseSpec()andRawChartintegration tests with fake VegaAPI exports:
core/src/index.tsnow exportsdefineMolplotChart,parseSpec, andRawChartconstructor options for preset/themeImplementation Details
<molplot-chart>element (loaded via CDN) handles rendering.<script type="application/json">to survive Markdown/HTML sanitization of special characters.<div class="molplot">to preventmd_in_htmlfrom wrapping in<p>tags or reprocessing children.https://claude.ai/code/session_01FwRZiMJXPcHWZLDUhduxmC