{"_id":"@a5omic/flowdown","_rev":"5-a74323ac6522a21a81616153cf19cc20","name":"@a5omic/flowdown","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.0":{"name":"@a5omic/flowdown","version":"0.1.0","keywords":["markdown","streaming","llm","ai","parser","renderer","incremental","chat","openai","claude","gemini","react-markdown","virtual-scroll"],"author":{"name":"Tom"},"license":"MIT","_id":"@a5omic/flowdown@0.1.0","maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"homepage":"https://github.com/Atomics-hub/flowdown#readme","bugs":{"url":"https://github.com/Atomics-hub/flowdown/issues"},"dist":{"shasum":"174cf6183e6dd3a65eb2204abc88e0dac9b50ba3","tarball":"https://registry.npmjs.org/@a5omic/flowdown/-/flowdown-0.1.0.tgz","fileCount":21,"integrity":"sha512-5hr2cQrVgq/SCG62r0Cmd/0HAL9LeOFpU50f0MDt6pZmbhFxD5ZhcLmRzgrGfs7QzyYeNmTQ9ittzm0Pz3D3EQ==","signatures":[{"sig":"MEYCIQCgwvfavfXNz2yPa0ZggAa8ccWjTWPmQsbM0l8jUe5waQIhAP2nWFjIOvm/xhoEzesP0rYo2srn3fpCJFL3e4QEoLim","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":88725},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"81c12d0f84fd1602096d2550334a8d4ac65ca7f9","scripts":{"dev":"tsc --watch","build":"tsc"},"_npmUser":{"name":"a5omic","email":"therekishianime@gmail.com"},"repository":{"url":"git+https://github.com/Atomics-hub/flowdown.git","type":"git"},"_npmVersion":"11.6.0","description":"O(1) streaming markdown renderer for the AI era. Zero dependencies.","directories":{},"sideEffects":false,"_nodeVersion":"24.8.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/flowdown_0.1.0_1775190160374_0.8698312553752758","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@a5omic/flowdown","version":"0.1.1","keywords":["markdown","streaming","llm","ai","parser","renderer","incremental","chat","openai","claude","gemini","react-markdown","virtual-scroll"],"author":{"name":"Tom"},"license":"MIT","_id":"@a5omic/flowdown@0.1.1","maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"homepage":"https://github.com/Atomics-hub/flowdown#readme","bugs":{"url":"https://github.com/Atomics-hub/flowdown/issues"},"dist":{"shasum":"5d9b116e6b2cab70d4ef6f7d26b724862170a2c9","tarball":"https://registry.npmjs.org/@a5omic/flowdown/-/flowdown-0.1.1.tgz","fileCount":22,"integrity":"sha512-MMRku3zVQl6wiYmnC++frPt4w7CiSVunPFVR6tVnN8jDw6cZBTM6wKpRJiGpxcxSdUmch26n1UQXbGeozqO0eQ==","signatures":[{"sig":"MEUCIBMhrAgZLWyGLLXECIqaDgllpuNODKC+v0F28Q64GPGoAiEAk4RvjfZK+OUAUx2W/+yeToJyVlUp6wfFYiVOJ4ysBHc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":93403},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"81c12d0f84fd1602096d2550334a8d4ac65ca7f9","scripts":{"dev":"tsc --watch","build":"tsc"},"_npmUser":{"name":"a5omic","email":"therekishianime@gmail.com"},"repository":{"url":"git+https://github.com/Atomics-hub/flowdown.git","type":"git"},"_npmVersion":"11.6.0","description":"O(1) streaming markdown renderer for the AI era. Zero dependencies.","directories":{},"sideEffects":false,"_nodeVersion":"24.8.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/flowdown_0.1.1_1775190225975_0.9047583937387453","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@a5omic/flowdown","version":"0.1.2","keywords":["markdown","streaming","llm","ai","parser","renderer","incremental","chat","openai","claude","gemini","react-markdown","virtual-scroll"],"author":{"name":"Tom"},"license":"MIT","_id":"@a5omic/flowdown@0.1.2","maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"homepage":"https://github.com/Atomics-hub/flowdown#readme","bugs":{"url":"https://github.com/Atomics-hub/flowdown/issues"},"dist":{"shasum":"4a9f1e6fc44ea1b0b204696bd4586249916735d8","tarball":"https://registry.npmjs.org/@a5omic/flowdown/-/flowdown-0.1.2.tgz","fileCount":26,"integrity":"sha512-sRKh+Zo/UtULiFs6gJcXVcCU29D8VZZyNYx8LDPDbrIVG5RVPOOoUhDN6ayTUFlI0mKMCUQhPsnp8MhMcThfOA==","signatures":[{"sig":"MEUCIHjLVZzVNpsS1vMdrjUI/fGWNM/sQu9G4q9U7YQzoET7AiEA3wE75T06m16Px0IXDT2KJYSBY1LVFGQzWENpTTr6Eqc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":110100},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"7add02e16ae8188a58f279df7732f93fcd2adfce","scripts":{"dev":"tsc --watch","build":"tsc"},"_npmUser":{"name":"a5omic","email":"therekishianime@gmail.com"},"repository":{"url":"git+https://github.com/Atomics-hub/flowdown.git","type":"git"},"_npmVersion":"11.6.0","description":"O(1) streaming markdown renderer for the AI era. Zero dependencies.","directories":{},"sideEffects":false,"_nodeVersion":"24.8.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/flowdown_0.1.2_1775625985507_0.2952533186803188","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@a5omic/flowdown","version":"0.1.3","keywords":["markdown","streaming","llm","ai","parser","renderer","incremental","chat","openai","claude","gemini","react-markdown","virtual-scroll"],"author":{"name":"Tom"},"license":"MIT","_id":"@a5omic/flowdown@0.1.3","maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"homepage":"https://github.com/Atomics-hub/flowdown#readme","bugs":{"url":"https://github.com/Atomics-hub/flowdown/issues"},"dist":{"shasum":"52b6e268f471fb302dd34856adce2ee25e1a608b","tarball":"https://registry.npmjs.org/@a5omic/flowdown/-/flowdown-0.1.3.tgz","fileCount":26,"integrity":"sha512-k4QEnTpb2PXvUnofLytmGgezmp8fm3/xp6bpexVCuF6Me7NcnrXQv0kUDL4jwOUtoAE9odTqq+2AWUas/UYk0g==","signatures":[{"sig":"MEQCIDuApmG8NA/YhMebmKtZygEepg6XAvbZo6Hb07rDsqOAAiBDBbhiwhUKfHjEHr020Hkmn2WjFLndJJmTfZhDAjQvMg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":110901},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4ccd53f6bd23032590b9957fee7b76f52f4c082c","scripts":{"dev":"tsc --watch","build":"tsc"},"_npmUser":{"name":"a5omic","email":"therekishianime@gmail.com"},"repository":{"url":"git+https://github.com/Atomics-hub/flowdown.git","type":"git"},"_npmVersion":"11.6.0","description":"O(1) streaming markdown renderer for the AI era. Zero dependencies.","directories":{},"sideEffects":false,"_nodeVersion":"24.8.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/flowdown_0.1.3_1777826562121_0.8395829356346107","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@a5omic/flowdown","version":"0.1.4","description":"O(1) streaming markdown renderer for the AI era. Zero dependencies.","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"sideEffects":false,"scripts":{"build":"tsc","dev":"tsc --watch"},"keywords":["markdown","streaming","llm","ai","parser","renderer","incremental","chat","openai","claude","gemini","react-markdown","virtual-scroll"],"license":"MIT","author":{"name":"Tom"},"homepage":"https://github.com/Atomics-hub/flowdown#readme","repository":{"type":"git","url":"git+https://github.com/Atomics-hub/flowdown.git"},"bugs":{"url":"https://github.com/Atomics-hub/flowdown/issues"},"engines":{"node":">=18.0.0"},"devDependencies":{"typescript":"^5.7.0"},"_id":"@a5omic/flowdown@0.1.4","gitHead":"58ef3eeefaf36dba4bbd7ac1d17e3b16b1e67a59","_nodeVersion":"24.8.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-LUXpp7Bgm8v8K3TsPSnVas3bzjFWKkpPwCVfzuS5NdDiDWr4veeSdYHeSMAeS+4JqgCn+lSPsEtJrWwYhJFe0Q==","shasum":"552e807a48c6dade19d6b46d7fb9b95c0edc714b","tarball":"https://registry.npmjs.org/@a5omic/flowdown/-/flowdown-0.1.4.tgz","fileCount":26,"unpackedSize":110961,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCpQeoYtP7ycWe5+cJFpM8sAsATa5tX/2w4B+und6BOpQIgAP88yRCvmUsPcBgGIMyN3j2PHUpX7XPeYGcRopmrWY8="}]},"_npmUser":{"name":"a5omic","email":"therekishianime@gmail.com"},"directories":{},"maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/flowdown_0.1.4_1777849479801_0.2790090525631397"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-03T04:22:40.265Z","modified":"2026-05-03T23:04:40.052Z","0.1.0":"2026-04-03T04:22:40.507Z","0.1.1":"2026-04-03T04:23:46.111Z","0.1.2":"2026-04-08T05:26:25.649Z","0.1.3":"2026-05-03T16:42:42.266Z","0.1.4":"2026-05-03T23:04:39.950Z"},"bugs":{"url":"https://github.com/Atomics-hub/flowdown/issues"},"author":{"name":"Tom"},"license":"MIT","homepage":"https://github.com/Atomics-hub/flowdown#readme","keywords":["markdown","streaming","llm","ai","parser","renderer","incremental","chat","openai","claude","gemini","react-markdown","virtual-scroll"],"repository":{"type":"git","url":"git+https://github.com/Atomics-hub/flowdown.git"},"description":"O(1) streaming markdown renderer for the AI era. Zero dependencies.","maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"readme":"# flowdown\n\n[![npm version](https://img.shields.io/npm/v/@a5omic/flowdown)](https://www.npmjs.com/package/@a5omic/flowdown)\n[![license](https://img.shields.io/npm/l/@a5omic/flowdown)](https://github.com/Atomics-hub/flowdown/blob/main/LICENSE)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/@a5omic/flowdown)](https://bundlephobia.com/package/@a5omic/flowdown)\n\nO(1) streaming markdown renderer for the AI era. Zero dependencies. ~4KB gzipped.\n\nEvery AI chat app using `react-markdown` re-parses the **entire conversation** on every token. That's O(n²). Flowdown processes each token exactly once. That's O(n).\n\n## Performance\n\n| Benchmark | Flowdown | marked | markdown-it |\n|-----------|----------|--------|-------------|\n| Streaming (2K tokens) | **7.8ms** | 16,765ms | — |\n| DOM output (9KB) | **7.3ms** | 13.4ms | 12.9ms |\n| String output (9KB) | **0.77ms** | 1.00ms | 0.87ms |\n| Bundle (gzipped) | **~4KB** | 12KB | 51KB |\n\nFlowdown is **2,146x faster** than marked for streaming the same document.\n\n## Hosted Flowdown\n\nFlowdown stays MIT and local-first. Hosted Flowdown is an early-access commercial version for teams that want help around private model streams or server-side chat rendering.\n\n| Tier | Price | For | Includes |\n|------|-------|----------|----------|\n| Open Source | Free | Apps that render in the browser | Core package, React wrapper, virtualization, static rendering |\n| Pro | $49/mo | Teams using private models | Private model setup, hosted preview links, team API keys, usage logs |\n| Cloud Render | $199/mo | Chat UIs that need server-side rendering | Render API, cached transcript HTML, priority support |\n\nFlowdown has 579 monthly npm downloads before any real launch. Start hosted checkout at https://flowdown-hosted-gejo3xsy3a-uc.a.run.app/ or read [HOSTED.md](https://github.com/Atomics-hub/flowdown/blob/main/HOSTED.md).\n\n## Install\n\n```bash\nnpm install @a5omic/flowdown\n```\n\n## Usage\n\n```typescript\nimport { Flowdown } from '@a5omic/flowdown';\n\nconst renderer = new Flowdown({\n  container: document.getElementById('output'),\n});\n\n// Stream tokens as they arrive from the LLM\nfor await (const chunk of stream) {\n  renderer.push(chunk);\n}\nrenderer.end();\n```\n\n### React\n\n```bash\nnpm install @a5omic/flowdown @a5omic/flowdown-react\n```\n\n```tsx\nimport { StreamMarkdown } from '@a5omic/flowdown-react';\n\nfunction ChatMessage({ content }: { content: string }) {\n  return <StreamMarkdown content={content} />;\n}\n```\n\n### Static Rendering\n\n```typescript\nconst html = Flowdown.renderToString('# Hello **world**');\n// → '<h1>Hello <strong>world</strong></h1>'\n```\n\n### With Syntax Highlighting\n\n```typescript\nimport { Flowdown } from '@a5omic/flowdown';\nimport hljs from 'highlight.js';\n\nconst renderer = new Flowdown({\n  container: document.getElementById('output'),\n  highlight: (code, lang) => {\n    if (lang && hljs.getLanguage(lang)) {\n      return hljs.highlight(code, { language: lang }).value;\n    }\n    return code;\n  },\n});\n```\n\n### Viewport Virtualization\n\nFor long LLM responses, enable virtualization to keep DOM node count flat:\n\n```typescript\nconst renderer = new Flowdown({\n  container: document.getElementById('output'),\n  virtualize: true,\n  overscan: 2,\n});\n```\n\nOff-screen blocks are replaced with height-matched spacers. Scrolling back materializes them from cache.\n\n## How It Works\n\nTraditional markdown renderers parse the entire input into an AST, then render the full AST to DOM on every update. During LLM streaming, this means O(n²) total work.\n\nFlowdown uses an **incremental state machine parser** that:\n\n1. Only processes new characters (never re-parses old content)\n2. Emits minimal DOM operations directly (no intermediate AST)\n3. Batches DOM writes per animation frame\n4. Handles incomplete markdown gracefully (FOIM prevention)\n5. Virtualizes off-screen blocks to keep DOM node count constant\n\nRead [RESEARCH.md](RESEARCH.md) for the full technical deep dive.\n\n## API\n\n### `new Flowdown(options)`\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `container` | `HTMLElement` | required | DOM element to render into |\n| `highlight` | `(code, lang) => string \\| Promise<string>` | — | Syntax highlighter for completed code blocks |\n| `onCodeBlock` | `(code, lang) => void` | — | Callback when a code block completes |\n| `sanitize` | `boolean` | `true` | Sanitize URLs (XSS prevention) |\n| `virtualize` | `boolean` | `false` | Enable viewport virtualization |\n| `overscan` | `number` | `2` | Blocks to keep rendered outside viewport |\n\n### Instance Methods\n\n- **`push(chunk)`** — Process a chunk of streaming markdown\n- **`flush()`** — Materialize the unfinished trailing line without ending the stream\n- **`end()`** — Signal end of stream, flush buffered content\n- **`reset()`** — Clear all state and output, ready for reuse\n- **`destroy()`** — Clean up all resources\n- **`getViewportStats()`** — Returns `{ total, visible, virtualized }` block counts\n\n### Static Methods\n\n- **`Flowdown.renderToString(markdown)`** — Parse markdown to HTML string (no DOM required)\n\n## Supported Markdown\n\nFlowdown targets \"LLM markdown\" — the subset that AI models actually output:\n\n- Headings (`# ## ### ####`)\n- Bold, italic, strikethrough, inline code\n- Fenced code blocks with language hints\n- Links and images (inline syntax)\n- Unordered and ordered lists\n- Blockquotes (including nested code blocks)\n- GFM pipe tables with column alignment\n- Horizontal rules\n\nIntentionally not supported (not used by LLMs): reference links, setext headings, indented code blocks, HTML blocks.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}