Custom Pipeline
The document pipeline is an additive API introduced in 0.4.0 in the repository.
Existing blocks, fromMarkdown, asSliver, builders, and selection controllers
keep their compatibility path. The published
Dart API reference
may not contain these additions until a package release.
Choose how much to replace
| Layer | Extend a default | Replace the engine |
|---|---|---|
| Parser | DefaultMarkdownParser(rules: ...) | MarkdownParser and MarkdownParserSession |
| Renderer | RendererRegistry(definitions: ...) | MarkdownRenderer |
| Animator | Configure DefaultMarkdownAnimator | MarkdownAnimator and MarkdownAnimatorSession |
The choices are independent. A custom parser can use handwritten rules, another library, or an external AST; it does not have to initialize tree-sitter. Its output is authoritative. A renderer handles node kinds and produces static fragments; an animator schedules those fragments without interpreting Markdown. Unknown node kinds use a literal-source renderer fallback.
import 'package:animated_streaming_markdown/animated_streaming_markdown.dart';
// Supply your implementations of the three public contracts.
final parser = MarkdownStreamParser(parser: myParser);
await parser.start();
final result = await parser.append(chunk);
final view = AnimatedStreamingMarkdown.fromDocument(
document: result.document!,
renderer: myRenderer,
animator: myAnimator,
enableSelection: true,
);
Keep the parser in application state and dispose it when the document is no
longer needed. The widget owns its animator session, not the caller's parser.
Use finish() for input EOF; animation completion is a separate event.
Custom async parser operations are queued without dropping chunks. Errors are
reported instead of switching to another parser; custom sessions should commit
snapshots only after successful parsing.
Parser output and lazy inner parsers
MarkdownDocumentSnapshot holds source, revision, hierarchy, opaque node IDs,
completion, and change information. Preserve identity when content continues.
MarkdownSourceRange uses UTF-16 source offsets; legacy startByte and
endByte use UTF-8. SourceOffsetIndex converts between them.
The Pipeline Lab source shows a custom parser with demand-driven inner factories for fenced code and Mermaid. The first matching fence creates a handler; unchanged block results are cached. Ordinary prose does not create those handlers.
This registry is example code, not a package-wide parser requirement. It is
synchronous lazy construction, not asynchronous module loading or viewport-based
parsing. C/C++ provides lexical highlighting and limited checks; Mermaid supports
a flowchart subset. Replace either handler for a broader grammar, or implement
an async MarkdownParserSession for loading and parsing with futures.
Renderer fragments and animation
A MarkdownRendererDefinition describes one node kind. Its
MarkdownRenderComponent contains semantic fragments and a widget factory that
runs only when materialized. Full MarkdownRenderer implementations can replace
registry lookup and composition entirely.
Fragments declare selectable text, source mappings, and copy metadata before
widgets mount. Register geometry with component.selectable. Transformed text
needs boundary mappings or atomic selection; decorative widgets need no logical
text. Text and Text.rich preserve span styling when split for animation.
Other widgets can provide sliceBuilder or remain atomic.
A custom MarkdownAnimatorSession controls segmentation, scheduling, progress,
pause/resume, and disposal. It returns slices in the original fragment's UTF-16
coordinates; it must not change the logical text or mapping. Atomic fragments
remain whole. The default animator supports word, grapheme, fragment, and
input-arrival segmentation; arrival chunks are not assumed to be LLM tokens.
The lab's AnswerAnimator(tokenize: ...) demonstrates an application tokenizer
callback for characters, words, or lines. This callback belongs to the example;
implement the public animator contract when segmentation needs document context.
Selection, copy, and Sliver
Copy strategies consume captured document/static metadata, including offscreen fragments. Animation progress does not change copy output. A custom copy provider should respect its selected slice; exact-source copy requires an accurate source snapshot. Binary image clipboard transport is an optional adapter, separate from HTML image markup.
SliverAnimatedStreamingMarkdown.fromDocument shares the document pipeline.
Place AnimatedStreamingMarkdownSelectionArea outside CustomScrollView for
selectable Slivers. Virtualization is by outer component, so a large table or
code block can remain one large child.
The document path's selection controller uses snapshot source text. The legacy blocks path retains its reconstructed projection. This migration does not promise identical visual defaults between the two paths or exact scrolling to unmeasured content.
Try the example
Open Pipeline Lab from the chat example's science button, or run:
cd example
flutter run -t lib/pipeline_lab.dart
Choose Parser, Renderer, Animator, and Tokenization independently. Replay local source without credentials, or use the original chat API settings for a streamed response. The inner-parser counters show when handlers load and parse. JetBrains Mono and syntax palettes are bundled/configured by the example; ASM does not impose them on applications.
See the step-by-step lab guide and document contract guide for extension examples and current limitations.