Add "Rust MDX" live embeddable component blocks

Add "Rust MDX" live embeddable component blocks

#11 in tonybierman/dx-blog — merged 2026-05-29

Summary

Implements roadmap #4 — the "identity-defining" feature: post Markdown can contain [[component:name key=val]] blocks that the renderer mounts as real, interactive Dioxus components (WASM, signal-driven) instead of iframes.

Previously the body rendered as one inert dangerous_inner_html blob (reader.rs:180), opaque to Dioxus' VDOM. Now the body is split into a sequence of rendered-markdown runs and live embed blocks, interleaved in the reader and the editor preview.

What's included

  • src/mdx.rs (shared, server + web) — parse_body splits a body into Segment::Html runs (each passed through the existing render_markdown, byte-for-byte identical for plain prose) and Segment::Embed { name, props }. Prop tokenizer handles bare and quoted values.
  • src/embeds.rsEmbedBlock registry dispatching to typed components:
    • counter — signal-driven counter
    • chart — inline SVG bar/line from data="3,7,2,9"
    • tweak — slider-driven live SVG curve
    • livechart — timer-streamed scrolling feed
    • stockchart / ticker — live candlestick chart with volume sub-band ("market open")
    • unknown names → safe inline fallback
  • reader.rs + admin/posts.rs — render the segment stream in the reader and the editor's live preview.
  • seed.rs — one demo post per component (rust-mdx-*).
  • justfilereseed recipe (wipe dev DB + serve with DX_SEED=1).

Design notes

  • Embed components are not feature-gated: they SSR-render their initial state and hydrate on the client (the whole point vs. an iframe). Streaming components seed their initial window deterministically (no rand/clock) so SSR and the client's first paint match — animation begins after hydration.
  • Props are typed strings parsed in EmbedBlock, never injected as HTML — strictly safer than iframe embeds.
  • use_interval timers cancel on unmount, so leaving a post stops its chart; embeds aren't mounted on feed/list pages (body-only).

Verification

  • cargo fmt clean
  • cargo check --no-default-features --features server,sqlite
  • cargo check --no-default-features --features web --target wasm32-unknown-unknown
  • cargo test --no-default-features --features server,sqlite → 16 passed (incl. 5 new parse_body tests)

Run just reseed, then open any rust-mdx-* post (animated charts start once the wasm client hydrates).

🤖 Generated with Claude Code

Last updated 2026-05-30