feat(web-astro): Astro + Starlight bake-off slice (MIC-126)#484
Open
mtiller wants to merge 9 commits into
Open
feat(web-astro): Astro + Starlight bake-off slice (MIC-126)#484mtiller wants to merge 9 commits into
mtiller wants to merge 9 commits into
Conversation
Rebuilds the first-order chapter on Astro + Starlight, parallel to the
mystmd slice (web/), to compare platforms for MIC-84.
The decisive contrast is the interactive figure. In mystmd it can't be a
drop-in component (no MDX; renderer plugin unimplemented; theme sanitizes
raw HTML) so it needs a theme fork or post-build hack. Here it's a native
MDX component:
<SimFigure id="FO" interactive caption="..." />
Verified in dist/first-order/index.html:
- structured contract preserved as data-case on the figure (mystmd stripped
it) — Astro doesn't sanitize component output;
- island <script> bundled + shipped by Astro (no injection step);
- 4 static plots, KaTeX math, and Modelica code sourced from the real .mo
files via ?raw (single source of truth, parity with literalinclude);
- full Jony skeleton + data-skin x data-mode token system.
No theme fork, no post-build step. Math via remark-math/rehype-katex
(npm + config, not a fork). Honest gaps documented in README: auto
figure/eqn numbering + :ref: cross-refs need a remark plugin (heaviest
gap); Modelica has no Shiki grammar (mystmd no better).
editable-param contract carried over: explicit `params` with editable
computed as variability=='parameter'; FO/FOE parameter-free -> read-only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…caffold (MIC-126, MIC-92) FEATURE-MAP.md: 1:1 mapping of every Sphinx feature the book uses (by count) to Astro/Starlight. Native/one-plugin for the bulk (code, literalinclude, math, figures, admonitions, toctree, search, conditional, raw); component for interactive figures (done). Two real work items flagged: :ref: cross-refs + auto-numbering (310 uses -> custom remark plugin, heaviest gap) and i18n. Decisions, not work: index->search, todo->drop, print export (MIC-85). i18n/ — gettext-reuse strategy so NO human re-translation is needed: - migrate-gettext.mjs: one-time RST-markup -> Markdown transform of existing .po files (msgid + msgstr). Proven on cn/first_order (fully translated, 35 units): 20 prose units match exactly + untouched, 15 marked-up re-keyed, 25 msgstrs rewritten, 0 re-translations. :math:/literal/\ -escapes handled; :ref: with translated link text is a known fuzzy (ties to the xref plugin). - po4a.cfg + README: forward workflow keeping one English source + per-lang .po overlays (today's gettext model), generating translated MDX for Starlight as build artifacts. msgmerge reconciles; near-misses = fuzzy confirm, not re-translate. Platform-independent across MDX frameworks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…line (MIC-126, MIC-85)
:ref: cross-reference parity (the heaviest gap, 310 uses):
- plugins/remark-xref.mjs: records {/* #label */} heading anchors (RST
labels; MDX-safe comment syntax since bare {..} is a JS expression),
numbers sections + figures, resolves empty-text [](#label) refs to the
target TITLE (Sphinx :ref: default) or 'Figure N'.
- Verified in build: [](#first-order-doc)->'Adding Some Documentation',
[](#FO)->'Figure 1'; figures auto-numbered 1-4; RST labels survive as
heading ids.
PDF/eBook print pipeline (correcting the earlier 'gap' mislabel — print is
not Astro's job, it's Pandoc's, from the same Markdown source):
- print/build-print-md.mjs lowers the MDX chapter to print Markdown:
<SimFigure> -> static plot image (interactive island degrades to its base
case, ideal for print), <Code> -> fenced modelica inlined from the .mo
files, {/* #id */} -> Pandoc {#id}, :::note -> fenced div. Verified: 4
figures + 5 code blocks lowered, 0 leftover MDX.
- print/pandoc-defaults.yaml + README: pandoc -> PDF (Typst/xelatex) + EPUB;
pandoc-crossref for numbered refs. Toolchain is a print-side dep exactly
as xelatex is today.
FEATURE-MAP.md updated: both items moved from gap to resolved.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… now build (MIC-85)
Running the print pipeline for real surfaced two bugs the scaffold hid:
- [](#id) cross-refs weren't resolved in print (only the web remark plugin
did) → typst PDF failed on missing labels. Preprocessor now numbers/labels
figures and resolves empty-text refs to title/'Figure N', emitting figure
images with {#id}. Parity with remark-xref.
- image paths resolved from the wrong dir → EPUB shipped without plots. Fixed
path + added resource-path to pandoc-defaults; dropped pandoc-crossref (we
resolve refs ourselves). Added link_attributes extension for figure ids.
Verified: PDF ~110KB via typst; EPUB ~38KB with all 4 SVG plots embedded;
[Figure 1](#FO) and [Adding Some Documentation](#first-order-doc) resolve.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regenerate the first-order MDX from the canonical RST prose so the text matches what the existing gettext catalogs were translated against. This makes translation reuse deterministic: exact msgid match, no fuzzy, no re-translation. - first-order.mdx: prose restored verbatim from the source RST (was a paraphrased demo that diverged from the catalog, causing false "missing translation" gaps). - apply-translation.mjs: fix relative imports when writing into a locale subdir (docs/<lang>/), which previously broke the build with UNRESOLVED_IMPORT; add opt-in fuzzy fallback (off by default). - Generated kr/cn pages committed as a review snapshot. Korean reuses 31/32 units (the 1 English holdout, "is equivalent to:", is untranslated in the source .po itself); Chinese reuses 32/32. Refs MIC-126, MIC-130. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds tools/rst2mdx.mjs — a prose-preserving RST→MDX converter that maps the book's directive/role surface (math incl. aligned/array, literalinclude with :lines: ranges + :emphasize-lines:, code-block, topic/note, index/toctree drop, :ref:/:math: inline). Unknown/raw directives pass through as flagged comments. Piloted on behavior/arrays/state_space.rst (the hard case: aligned matrix math, partial-line includes, 8 cross-refs): builds green, 50 KaTeX nodes, 41 matrix envs, code sourced from 7 .mo files, all refs anchored. Chinese reuse 35/35 exact, zero fuzzy. migrate-gettext.mjs: add RST external-hyperlink rule (`text <url>`_ → [text](url)). This was surfaced by the full-corpus i18n dry run and confirmed live — the state-space FMI-link paragraph was the one holdout (34/35) until the rule landed, then 35/35. Refs MIC-126, MIC-130. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Converts all 91 chapters and builds the full book (98 pages) green. The
generated MDX tree and sidebar are build artifacts (gitignored, regenerated
by `npm run convert` / prebuild), so only the tooling is committed here.
- tools/convert-book.mjs: orchestrator — scans every .rst for a global label
map, converts each chapter route-aware, and derives the Starlight sidebar
from the book's toctrees (5 groups / 82 leaves, real titles + nesting).
- tools/rst2mdx.mjs: route-aware converter. Handles only/raw/ifconfig
(builder-conditionals), footnote-ref stripping, unique .mo import identifiers
(fixes SecondOrderSystem-from-two-paths collision), string-form emphasize
lines (fixes mark={[10-12]} evaluating as subtraction), and cross-document
:ref: resolution (same-page #anchor vs other-page /route/#anchor).
- astro.config.mjs: sidebar sourced from tools/sidebar.gen.json.
- package.json: predev/prebuild run the converter; `npm run convert` on demand.
Unhandled directives across the corpus down to 1 (custom cooling-if-expr).
plot/figure remain placeholders pending the asset pipeline (MIC-87).
Refs MIC-126, MIC-131.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The converter now maps `.. plot::` to <SimFigure id=... [interactive]/> (id from the plot script name, interactive from :class: interactive) and `.. figure::` to a captioned image, instead of TODO placeholders. Chapters are now figure-ready: only the image/JSON artifacts remain (asset pipeline), the wiring is done. SimFigure no longer throws when an interactive figure lacks case data — it degrades to the static figure and lights up automatically once src/cases/<id>.json exists. Keeps the full-book build green (98 pages) while the artifacts are still being generated. Refs MIC-131, MIC-132, MIC-87. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…folding Closes/advances the non-blocked Astro Conversion issues surfaced by the build: - MIC-128 Modelica syntax highlighting: register a Modelica TextMate grammar with Expressive Code (src/langs/modelica.tmLanguage.json) and emit lang="modelica". - MIC-136 Index: collect .. index:: terms into a global map and emit a generated genindex page (196 terms) with a sidebar "Reference → Index" entry. - MIC-134 Dedication image: converter now recurses into only::/admonition bodies (nested image/figure/math no longer dropped) + copies source/_static images into public/figures. Fixes the missing Aaron.jpg and nested-directive images. - MIC-131 Converter residuals: drop the typo'd `.. cooling-if-expr:` marker quietly (0 unhandled directives now); add footnote + hyperlink migration rules to migrate-gettext so those translation units match. Tab-expansion fixes a raw iframe leak exposed by the recursion. - MIC-137 (pipeline half): apply-translation now translates admonition bodies, list items, image captions, and table cells (was paragraphs/headings only). - MIC-135 (part 1): SimFigure renders parameter-free "interactive" figures as static instead of showing an empty "no adjustable parameters" panel + dead Run. - MIC-129 Foldable annotations: client-side progressive enhancement (public/annotation-fold.js) collapsing single-line annotation(...) to a "(…)" toggle, matching the old site. Multi-line is a documented follow-up. - MIC-130 i18n verification: documented in i18n/VERIFICATION.md. Whole book still builds green (883 pages, 8 locales). Generated genindex + figures are gitignored (regenerated by convert-book on prebuild). Refs MIC-128 MIC-129 MIC-130 MIC-131 MIC-134 MIC-135 MIC-136 MIC-137. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Rebuilds the first-order chapter on Astro + Starlight, parallel to the mystmd slice (PR #483,
web/), so the MIC-84 platform choice can be judged head-to-head. Tracked in MIC-126.Why: the mystmd weakness this addresses
The mystmd slice proved content works but exposed that interactive figures have no out-of-the-box path in mystmd — markdown isn't MDX, the "renderer" plugin type is unimplemented, and the theme sanitizes raw HTML. Options there were a theme fork or a post-build injection hack.
Result: the island is a native MDX component
Verified in
dist/first-order/index.html:data-caseon the figure — Astro doesn't sanitize component output (this is exactly what mystmd stripped);<script>is bundled and shipped by Astro — no injection step;.mofiles via?raw(single source of truth, parity withliteralinclude);data-skin×data-modetoken system.No theme fork. No post-build step. No sanitizer fight. More island types later = more components you import.
Honest gaps vs. mystmd (bridgeable via plugins, not forks)
:ref:cross-references — native in mystmd; here needs a remark plugin (build-time, not a fork). The heaviest gap; validate before committing to full migration.Translation impact (the question that prompted this)
Would all RST become Markdown? Yes — source content converts RST→MDX (one-time, English source). Same conversion the mystmd decision already accepted.
Would translation become a parallel tree of MD files (vs. today's "English → X string" gettext model)? Starlight's native i18n is indeed a parallel per-locale file tree — a real regression from gettext's string-delta model (one English source +
.pooverlays, fuzzy-marking on change, translators only fill strings).But we don't have to accept that regression. Keep
.poas the source of truth and usepo4a(PO-for-anything, Markdown-aware) to generate the per-locale MDX at build time. Translators keep the exact gettext workflow (string deltas, fuzzy tracking); Starlight's parallel tree becomes a build artifact, not hand-maintained. This preserves today's superior translation UX.Key point: this i18n question is platform-independent across MDX frameworks (Docusaurus/Astro/Nextra all use file-based i18n natively) and is the crux of MIC-92. It is not a reason to prefer mystmd — mystmd's own i18n story is weaker than gettext too. The gettext-preserving
po4abridge is the recommended path regardless of framework.How to review
Compare the figure ergonomics + authoring against PR #483 (mystmd).
Refs MIC-126, MIC-84, MIC-92.
🤖 Generated with Claude Code