Skip to content

feat: render wire fences in the browser with static rendering opt-in - #82

Merged
eduardozf merged 4 commits into
mainfrom
feat/markdown-wire-fences
Sep 6, 2026
Merged

eduardozf merged 4 commits into
mainfrom
feat/markdown-wire-fences

Conversation

@eduardozf

@eduardozf eduardozf commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

Wire fences now render asynchronously in the browser after HTML is ready by default. A documentation site initializes @wire-lang/browser once; authors then write ordinary fenced wire blocks. Sites that need finished SVG without client JavaScript can opt into ahead-of-time rendering through @wire-lang/markdown.

import wire from "@wire-lang/browser";

const { errors } = await wire.initialize();
for (const { error } of errors) console.error(error);

Standard pre > code.language-wire output needs no Wire Markdown plugin. remarkWire and rehypeWire preserve source by default; { mode: "static" } replaces fences with structured inline SVG during the build. MDX supports the same static option, or browser rendering after hydration.

Closes #36

Implementation

  • Adds @wire-lang/browser with asynchronous initialize(), run(), and render(source) APIs. Its standalone ESM bundle includes core and loads without an import map or bundler.
  • Discovers standard Markdown blocks and pre.wire-lang / code.wire-lang. Preserves and hides successfully rendered source, inserting a sibling SVG container.
  • Serializes overlapping runs, skips unchanged diagrams, and supports scoped discovery, edited source, and explicit forced rendering. Per-block errors preserve source and do not prevent other diagrams from rendering.
  • Retains synchronous, DOM-independent core rendering. The browser wrapper yields before each diagram; individual compilation and layout still run on the main thread. Applications call run() after navigation or content updates.
  • Retains static diagnostic mapping, including per-line indentation and nested containers. Browser diagnostics are relative to each source block.
  • Includes both packages in build, pack, publish, dependency pinning, and installed-tarball smoke coverage. All five published packages are aligned at 0.3.1.
  • Updates README guides, domain vocabulary, implementation contracts, authoring skill, and ADR 0020. Prior conflicts with main and uneven-indentation review findings remain resolved.

Validation

  • pnpm release:check: lint, documentation formatting, typecheck, 146 tests across 14 files, build, production dependency audit, package dry runs, and installed-package smoke checks.
  • Nine browser tests cover asynchronous rendering, discovery, source preservation, concurrent/repeated calls, edits, errors and recovery, markup escaping, DOM readiness, and custom selectors.
  • Markdown/MDX tests cover browser defaults and explicit static rendering, retaining the diagnostic regression cases.
  • Chrome loaded the standalone ESM file over local HTTP without a bundler. All seven examples rendered without errors and matched core SVG in the DOM. Repeated rendering created zero duplicates; forced rendering kept seven output containers. No browser console errors.
  • All seven examples match main through core, browser rendering, and both static plugins. Core/browser SVG strings and rendered PNG comparisons match.
  • pnpm skill:check and skill frontmatter validation.
  • GitHub CI check and Security audit passed on c9f467e. GitHub reports the PR clean and mergeable.

Rendered comparisons

The final schematic drawing is unchanged; rendering now happens after HTML loads unless static mode is selected. These images use the bundled SVG renderer on the same source, with main on the left and the browser renderer on this branch on the right. PNGs are hosted separately on demo/browser-render-assets under docs/assets/browser-render/ and are absent from this PR's diff.

LED schematic: main and browser rendering

RC filter: main and browser rendering

Release impact

New browser APIs and the Markdown rendering modes are documented. Configure npm trusted publishing for both @wire-lang/browser and @wire-lang/markdown before the first release tag. Browser mode requires client initialization; plugin installation alone does not inject a runtime. Static mode requires { mode: "static" } explicitly.

Renderer architecture tradeoffs will be investigated separately in #112; that investigation is not a merge prerequisite.

eduardozf added 2 commits July 9, 2026 20:01
- offset Wire diagnostic columns by the fence marker's column so errors
  in fences nested in lists or blockquotes point at the right character
- look up the primary diagnostic once in render-wire
- cover the rehypeWire failure path and indented fences in tests
- make the MDX version assertion survive release bumps
- note the remark-stringify limitation in the README
@eduardozf

Copy link
Copy Markdown
Owner Author

Review

Reviewed the full diff. The design is sound and consistent with the repo's conventions: structured HAST instead of raw HTML matches ADR 0019, the package layout/build config mirrors core/cli exactly (tsup + tsc -b d.ts emission, ESM-only, Node 20), and the release surface (publish loop, pin-workspace-deps, pack:dry-run, smoke test, root tsconfig refs) was updated everywhere packages are enumerated — I checked for missed spots and found none. Diagnostic line mapping is correct given core's 1-based positions, and file.fail with cause/ruleId/source is the right VFile idiom.

Fixed and pushed (044a295)

  • Diagnostic columns ignored fence indentation (render-wire.ts): Markdown strips the indent from fenced code inside lists/blockquotes, so Wire's 1-based columns were passed through raw and pointed at the wrong character. Columns are now offset by fenceStart.column - 1; a test covers an indented fence (col 16 → 18).
  • rehypeWire failure path was untested — only the remark path asserted diagnostic mapping. Added the mirror test; it confirms remark-rehype carries the fence position onto pre.
  • Hardcoded "0.3.0" in the MDX test would break at the next version bump; now matches any semver.
  • Duplicated primary-diagnostic lookup in failDocumentBuild/diagnosticPosition — extracted primaryDiagnostic.
  • README: noted that a pipeline serializing back to Markdown (remark-stringify) can't handle the replaced wireDiagram node — use rehypeWire in that setup.

117 tests pass; lint/typecheck clean.

Notes, no change needed

  • The declare module "mdast" augmentation for wireDiagram ships in the public d.ts and applies to all consumers — same pattern as remark-math, fine, just be aware it's part of the public type surface.
  • @types/hast/@types/mdast/@types/unist in dependencies (not dev) is correct here since the published d.ts references them.
  • vitest.config.ts has no @wire-lang/markdown alias; not needed since only its own tests import it (relatively), but worth adding if another package ever consumes it in tests.
  • Reminder from the PR notes: the npm trusted-publisher config for @wire-lang/markdown must exist before the next v* tag, or the publish job will fail mid-loop after core/cli are already published.

@eduardozf eduardozf changed the title feat: render wire fences in Markdown and MDX feat: render wire fences in the browser with static rendering opt-in Sep 6, 2026
@eduardozf
eduardozf merged commit 1abd724 into main Sep 6, 2026
2 checks passed
@eduardozf
eduardozf deleted the feat/markdown-wire-fences branch September 6, 2026 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Markdown/MDX integration for the wire fenced block

1 participant