feat(ascii): add @streamdown/ascii plugin for ASCII/box-drawing diagrams - #614
Open
sleitor wants to merge 1 commit into
Open
feat(ascii): add @streamdown/ascii plugin for ASCII/box-drawing diagrams#614sleitor wants to merge 1 commit into
sleitor wants to merge 1 commit into
Conversation
Adds a new workspace package, @streamdown/ascii, that renders
agent-generated ASCII and Unicode box-drawing diagrams (e.g. `┌─┐`,
`│ │`, `└─┘`, `──►`) as stable preformatted blocks.
- Binds to ```ascii, ```diagram, and ```chart fences by default
(configurable via createAsciiPlugin({ languages }))
- Disables font ligatures (font-variant-ligatures: none,
font-feature-settings liga/calt off) so sequences like --> are never
collapsed into a stylized arrow glyph
- Uses an advance-consistent monospace font stack, overridable via
createAsciiPlugin({ fontFamily })
- Renders a single unbroken text node inside a <pre> (no per-line span
wrappers, no syntax highlighting) so streaming appends never reflow
existing rows
- Surfaces isIncomplete only via a stable data-incomplete attribute;
tree shape never changes across streaming ticks
Plugs into the existing plugins.renderers / CustomRenderer extension
point, so no core changes are required.
Closes vercel#610
Contributor
|
@sleitor is attempting to deploy a commit to the Vercel Team on Vercel. A member of the Team first needs to authorize it. |
lofcz
added a commit
to lofcz/streamdown-ng
that referenced
this pull request
Sep 7, 2026
Port vercel/streamdown#614 as a workspace package that renders ```ascii / ```diagram / ```chart fences as ligature-free, single-text-node <pre> blocks so agent-generated diagrams stay aligned while streaming.
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.
Closes #610
Problem
LLMs and coding agents default to ASCII and Unicode box-drawing characters (
┌─┐,│ │,└─┘,──►) for architecture diagrams and flowcharts instead of Mermaid. Rendered as plain Markdown code, these break in a browser for a few concrete reasons:-->renders as a single stylized arrow glyph, destroying column alignment─│┌┐└┘) versus plain ASCII, so columns driftThis PR adds
@streamdown/ascii, a plugin package that renders these fences as plain, unstyled-by-highlighter<pre>blocks with ligatures disabled and an advance-consistent monospace stack, so the diagram looks the same as when the model generated it.Usage
Advanced configuration via the factory:
Options
languagesstring[]["ascii", "diagram", "chart"]classNamestring<pre>fontFamilystringui-monospace, "SF Mono", "Cascadia Mono", "DejaVu Sans Mono", "Liberation Mono", Menlo, Consolas, monospaceStreaming-safety guarantee
<pre>always contains exactly one text child ({code}) — no per-line<span>wrappers, no syntax highlighting — so appending characters during streaming never causes existing rows to reflow. Covered by a test that assertspre.childNodes.length === 1and the single child is a text node.data-incompleteattribute, never by adding/removing/swapping elements — so React never remounts the block mid-stream (verified by a test asserting the<pre>DOM node reference is identical before/after anisIncomplete: true -> falsererender).font-variant-ligatures: none+font-feature-settings: "liga" 0, "calt" 0(kills arrow-ligature collapsing),white-space: pre(no wrapping, which would destroy ASCII art alignment), andoverflow-x: auto(wide diagrams scroll instead).Why no core changes are required
Streamdown already has a
plugins.renderers/CustomRendererextension point ({ component, language }, resolved inpackages/streamdown/lib/plugin-context.tsx) built for exactly this kind of arbitrary-fence-language rendering, and it's already documented at/docs/custom-renderers.@streamdown/asciiis a sibling package (mirroring the layout of@streamdown/math,@streamdown/mermaid,@streamdown/cjk) that plugs into that existing point —PluginConfig,plugin-context.tsx, and every other core file are untouched. This is a purely additive change.What's included
packages/streamdown-ascii— new package (index.tsx,package.json,tsup.config.ts,tsconfig.json,vitest.config.ts, tests), mirroring the sibling plugin package layout (author, license,peerDependencies.react, scripts, etc.)__tests__/index.test.tsx— 11 tests: default/custom language binding, independent instances, single-text-child assertion, ligature/white-space/overflow/tab-size styling, stable tree shape acrossisIncompletetrue→false, no-throw on partial content, customclassName/fontFamily.changeset/add-ascii-plugin.md— minor version changeset for the new packageapps/website/content/docs/plugins/ascii.mdx+plugins/meta.json— docs page matching the structure of the existing math/cjk/mermaid plugin pagesScope note: the website has a larger interactive plugin playground (
app/[lang]/playground) that lets users togglecode/mermaid/math/cjklive. I did not wireasciiinto that (593-line component) to keep this PR focused and reviewable — happy to follow up if maintainers want that too.Verification
Run from a clean
upstream/maincheckout plus this branch:pnpm installpnpm build(all packages/apps build;@streamdown/asciibuilds cleanly via tsup, ESM +.d.ts)pnpm check-types— no-op repo-wide today (no package defines acheck-typesscript yet); manually rantsc --noEmitinside the new package with zero errorspnpm check(biome/ultracite, zero errors afterpnpm fixreformatted one line in the new file)pnpm test— 7/7 package test suites pass, including the new@streamdown/asciisuite (11/11)Pre-existing, unrelated failure:
apps/test#buildfails on this branch withGatewayAuthenticationError: AI Gateway authentication failed(apps/test/app/page.tsxcallsgateway.getAvailableModels()at build time, which needsAI_GATEWAY_API_KEY/Vercel OIDC). I confirmed this is not caused by this PR by building a cleanupstream/mainworktree the same way — it fails identically there with no local changes at all.No breaking changes. No existing tests modified or weakened.