Code Block Runtime
This page documents the optional stream-diffs runtime used by CodeBlockNode. In 2.0 the stream-monaco fallback is removed and stream-diffs is the only enhanced code-block surface. Supported configuration is exposed through the renderer-neutral CodeBlockOptions API.
Install
pnpm add stream-diffsNo worker plugin or package-specific CSS import is required.
If stream-diffs is not installed, the loader keeps the <pre> fallback and renders the code block without an enhanced surface.
Runtime boundary
markstream-vue stream-diffs
--------------- ------------
CodeBlockNode controller + DOM surface
- Vue props / unmount - HTMLElement target
- streaming completion - code or diff data
- viewport decision - File / FileDiff rendering
- header and toolbar - syntax highlightingThe stream-diffs root entry is framework-agnostic. It does not import Vue or own a Vue lifecycle. The package also exposes an optional stream-diffs/vue convenience entry for direct Vue consumers, but markstream-vue does not use that entry.
CodeBlockNode handoff
CodeBlockNode uses one stable visual path:
- While code is streaming, Vue renders the shared
PreCodeBlock;renderCodeBlocksAsPreuses the same component and resolved visual defaults. - After the block is complete and visible, the component dynamically imports the
stream-diffsroot runtime and mounts one File or FileDiff surface into its existing container. - The component applies the active theme to that surface and removes the temporary
<pre>only when the surface is ready. Before reveal, both surfaces already share font metrics, padding, gutter geometry, overflow, and theme background; a streaming plain block never inherits a stale restored-height floor. - On component unmount, the Vue adapter disposes the controller.
Completion, visibility, and unmount are CodeBlockNode concerns. They are not stream-diffs lifecycle hooks.
CodeBlockShell owns the title and action bar. The File surface is created with its internal data-diffs-header disabled so the DOM has one header.
Theming
Use theme with either a registered string name or { dark, light }. The themes prop is the [dark, light] name pair available to the runtime. CodeBlockNode sends theme changes to its mounted surface without recreating the Vue component.
Monaco JSON theme objects are not renamed into the 2.0 API. Use registerCustomTheme from stream-diffs/pierre, then pass the registered name.
Options handoff
Direct CodeBlockNode usage and the top-level NodeRenderer / MarkdownRender API both accept codeBlockOptions. The same CodeBlockOptions contract is available across Vue 3, React, Octane, Svelte, Angular, and Vue 2.
The supported surface includes:
- host-managed typography and layout:
fontSize,lineHeight,fontFamily, numeric-pixelmaxHeight, numeric-pixel symmetricpadding, andtabSize; - File options such as
disableLineNumbers,overflow, highlighting limits, and virtualization/highlighter controls;overflow: 'wrap'is passed to the compatibility runtime aswordWrap: 'on', whileoverflow: 'scroll'is passed aswordWrap: 'off'; the default isoverflow: 'wrap'. - FileDiff layout, indicators, unchanged-region folding, and line-diff controls;
- line/token interactions, selection callbacks, annotations,
onController, andworkerManager.
Markstream applies the host-managed fields to both the streaming <pre> and finalized surface, then forwards the remaining supported fields to stream-diffs. Theme, language/content, streaming state, the single header, mount/reveal timing, and disposal remain host-owned and take precedence over conflicting runtime values.
Optional preload
If a route is known to contain completed, visible code blocks, preload the module during idle time:
import { preloadCodeBlockRuntime } from 'markstream-vue'
void preloadCodeBlockRuntime()This only warms the optional module. It does not create a surface, finalize a streaming block, or bypass the completion-and-visibility gate.
Worker pool (off-thread highlighting)
stream-diffs highlights with Shiki on the main thread by default. Rendering a code block with tens of thousands of lines blocks the UI for the whole highlight pass. @pierre/diffs ships an experimental WorkerPoolManager that moves Shiki tokenization into Web Workers; markstream-vue forwards an injected pool to every enhanced surface as the workerManager runtime option.
markstream-vue deliberately does not bundle or spawn the worker itself — worker assets are bundler-specific and fragile inside a multi-bundler library. Instead, the host app creates the pool with its own bundler and injects it once:
import { getOrCreateWorkerPoolSingleton } from '@pierre/diffs/worker'
// vite.config.ts / any module evaluated once before code blocks render
import DiffsWorker from '@pierre/diffs/worker/worker.js?worker'
import { setStreamDiffsWorkerPool } from 'markstream-vue'
const pool = getOrCreateWorkerPoolSingleton({
poolOptions: {
poolSize: 4,
workerFactory: () => new DiffsWorker(),
},
highlighterOptions: {
// Align with the theme(s) used by the code blocks.
theme: { dark: 'pierre-dark', light: 'pierre-light' },
},
})
setStreamDiffsWorkerPool(pool)Use the equivalent worker import for your bundler (webpack 5, Rollup, etc.). The same manager is shared across all code blocks. Lifecycle and termination stay under application control:
import { clearStreamDiffsWorkerPool, terminateStreamDiffsWorkerPool } from 'markstream-vue'
terminateStreamDiffsWorkerPool() // calls pool.terminate() (if available) and clears it
clearStreamDiffsWorkerPool() // clears without terminating (host keeps ownership)Behavior notes:
- No pool injected — highlighting stays on the main thread, exactly as before. This is the default.
- Theme sync —
CodeBlockNodeforwards the active theme to the pool viasetRenderOptionson every theme change, so worker-generated tokens match the requested theme. No host wiring needed. - Per-block override — a
codeBlockOptions.workerManagerpassed to an individual block wins over the shared injected pool. - Fallback is automatic — if the pool reports itself unavailable (
isWorkingPool() === false),@pierre/diffsfalls back to main-thread highlighting. A broken or terminated pool never blocks rendering.
Diff interactions
Diff blocks keep the same adapter boundary. The enhanced diff surface uses stream-diffs defaults unless the corresponding codeBlockOptions fields are supplied. For example, diffStyle, expandUnchanged, collapsedContextThreshold, hunkSeparators, lineDiffType, and parseDiffOptions configure layout and folding.
The fallback applies the same collapsedContextThreshold decision as the finalized surface: an unchanged region is folded only when its hidden line count is greater than the threshold. Unified and split fallbacks derive No newline at end of file from each source's actual final-newline state, render it with the same neutral metadata palette as the finalized surface, and preserve the same visible height during handoff. Added/removed line fill is composited once per visual region so its effective color matches the finalized surface. In wrap mode, content fill, gutter marker, and line-number fill span the complete measured logical-row height. Switching between wrap and scroll clears the old synchronized row height before the next layout is painted. The finalized host preserves every row below maxHeight and uses scrollable overflow above it; it never hides overflowing diff rows behind a larger shell.