mcp: project-plan — Dispatch Render Upgrade
This commit is contained in:
parent
4be2c247be
commit
fccc0da46f
64
Sources/Dev/dispatch-render-upgrade.md
Normal file
64
Sources/Dev/dispatch-render-upgrade.md
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
created: '2026-07-02'
|
||||
path: Sources/Dev
|
||||
project: dispatch-render-upgrade
|
||||
tags:
|
||||
- dispatch
|
||||
- python
|
||||
type: project-plan
|
||||
---
|
||||
|
||||
# Dispatch Render Upgrade
|
||||
|
||||
## Goal
|
||||
|
||||
Upgrade the petal-dispatch text interface to allow for greater user interaction and rendering capability. Concretely: replace the plain markdown-to-text flow in the web client with a content-aware renderer that recognizes distinct content *kinds* and renders each appropriately. Tables were the surfacing example; the actual goal is a renderer that handles prose, tables, code, structured data, and diagrams — and slots in new kinds without a rewrite each time. Plus copy affordances: a per-code-block copy box and per-message (bubble) copy. The renderer and the copy boxes are both explicit v1 wants.
|
||||
|
||||
Constraint that shapes everything: the client is a single self-contained `src/petal_dispatch/static/index.html` (~2881 lines), Tailwind via CDN, vanilla inline JS, **no build step**. All additions must be CDN `<script>` tags. No toolchain, no bundler, no restart — served live on next load.
|
||||
|
||||
## Locked Decisions
|
||||
|
||||
- **Parser: `markdown-it`** via CDN. Framework-agnostic, plugin architecture, cheap to re-render a growing buffer. GFM tables via core (kills the ASCII-table problem).
|
||||
- **Highlighter: `highlight.js`** via CDN. Better language auto-detect than Prism; integrates with markdown-it's highlight hook.
|
||||
- **Fence-dispatch table is the extensibility core.** The parser tags every code fence with its language. Register widget handlers keyed by language string. Known lang → intercept, mount widget. Unknown → normal highlighted code block. New kind later = one table entry, zero changes to the stream loop.
|
||||
- **Streaming model: buffer-and-re-render.** Each message holds its raw markdown string. On each SSE tick: append chunk to buffer, re-render that message from the buffer. The buffer is the throwaway view; DB stays truth. Ship the naive per-tick full re-render first; only add newline-boundary optimization if flicker or mid-stream selection-loss actually bothers in use.
|
||||
- **One data model powers three features.** Per-message raw markdown buffer is the single invariant. Streaming re-render, per-block copy, and bubble copy are all views over that same stored string — not three subsystems.
|
||||
- **Copy semantics = raw, not rendered.** Per-code-block copy returns the raw fence contents. Bubble copy returns the accumulated markdown buffer (source), not `innerText` of the DOM.
|
||||
- **Bubble-copy mid-stream = accepted partial.** Copying a still-streaming message yields the current (partial) buffer. This is intended behavior, not a bug — copy grabs current buffer state. Do not "fix" it into something that waits for stream completion.
|
||||
- **`html` rendering: out of scope.** Arbitrary HTML/JS from message content is a self-XSS surface. Not in v1. If ever added, sandboxed iframe only.
|
||||
|
||||
## Open Items
|
||||
|
||||
- **Charts (`chart` fence → Chart.js):** hatch only if trivial to wire alongside the other handlers; otherwise deferred. Don't build ahead of a chart spec actually showing up more than once. Decide at Phase 2 based on effort, not commitment.
|
||||
- **Flicker / selection-loss on per-tick re-render:** unknown until real use. If it bites, the fix is re-rendering only on block-closing token boundaries. Not pre-optimized.
|
||||
|
||||
## Build Phases
|
||||
|
||||
### Phase 0 — Parser swap (foundation)
|
||||
- Add `markdown-it` + `highlight.js` CDN `<script>` tags to `index.html` head.
|
||||
- Wire markdown-it with GFM tables on and the highlight hook pointed at hljs.
|
||||
- Convert the render loop from text-append to buffer-and-re-render: each message stores raw markdown, re-renders from buffer on SSE tick.
|
||||
- Style the output via existing Tailwind + inline `<style>`: tables (headers, zebra, overflow scroll), code blocks, lists, blockquotes.
|
||||
- **Outcome:** prose, tables, code, lists, blockquotes render properly. Screenshot problem fixed as a side effect.
|
||||
|
||||
### Phase 1 — Fence-dispatch table + first widgets
|
||||
- Build the handler registry: `{ lang: (rawFenceContents) => mountWidget }`.
|
||||
- Hook it into markdown-it's fence rendering: known lang → handler, unknown → default code block.
|
||||
- Add `json` handler → collapsible tree.
|
||||
- Add `mermaid` handler → Mermaid.js (CDN) SVG render. High-signal for infra/topology content.
|
||||
- **Outcome:** structured JSON and diagrams render as widgets; registry proven extensible.
|
||||
|
||||
### Phase 2 — Copy affordances
|
||||
- Per-code-block copy button: injected into each fence's rendered block, copies raw fence contents.
|
||||
- Per-message bubble copy: copies that message's raw markdown buffer.
|
||||
- Both read from the per-message raw-markdown model established in Phase 0 — no new state.
|
||||
- **Decide here:** wire `chart`/Chart.js if trivial, else leave the Open Item deferred.
|
||||
- **Outcome:** one-click copy of any code block and any full message, as source.
|
||||
|
||||
## Notes
|
||||
|
||||
- **A vs B (the render architecture choice):** *A-path* = client parses markdown; server keeps streaming raw markdown over SSE (what it does today). *B-path* = server emits typed content blocks (`{type: "table", ...}`) and the client is a dumb switch over block types. This plan is A. Rationale: markdown streams token-by-token cleanly, whereas B must buffer a whole block before it can emit the envelope — killing incremental paint. A also matches DB-as-truth / stream-as-display: the parser is a display concern, not a protocol change; markdown-over-SSE is already the transport. B stays available as a future move only if a genuinely non-streamable kind proves it needs it.
|
||||
- **The fence-dispatch table is the whole extensibility bet.** Everything below the "streamable" line (charts, diagrams, future kinds) is a spec you buffer until the fence closes, then mount. Uniform seam, one mechanism.
|
||||
- **Extensibility discipline:** only `json` + `mermaid` at v1. Charts gated on real recurrence. `html` out. Don't add handlers for content that hasn't appeared.
|
||||
- **No-build-step is a hard constraint, not a preference** — it's why the whole plan is CDN scripts and inline JS edits, and why there's no framework migration anywhere in scope.
|
||||
- Execution routes to Lovebug against `src/petal_dispatch/static/index.html`; static changes serve live, no restart.
|
||||
Loading…
Reference in New Issue
Block a user