Skip to content

chat 0.0.20 — swap <chat-streaming-md> to @cacheplane/partial-markdown#193

Merged
blove merged 15 commits into
mainfrom
claude/chat-streaming-md-partial-markdown
May 4, 2026
Merged

chat 0.0.20 — swap <chat-streaming-md> to @cacheplane/partial-markdown#193
blove merged 15 commits into
mainfrom
claude/chat-streaming-md-partial-markdown

Conversation

@blove

@blove blove commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces ``'s `marked` + `innerHTML` rendering pipeline with an Angular template walking a `@cacheplane/partial-markdown@0.1.0` AST through the existing `@ngaf/render` view registry.

What's new

  • 18 per-node-type Angular components (one per MarkdownNode type) under `libs/chat/src/lib/markdown/views/`.
  • `cacheplaneMarkdownViews` default `ViewRegistry` mapping each node type to its component. Override via `withViews(cacheplaneMarkdownViews, { heading: MyHeading })`.
  • New optional `[viewRegistry]` input on `` for per-instance overrides.
  • Internal: `` keeps a `createPartialMarkdownParser()` instance across signal changes; pushes deltas (or resets on divergence) and renders the resulting tree.
  • New `MARKDOWN_VIEW_REGISTRY` DI token + `` recursive dispatcher for registry-based child rendering.

Identity preservation

The component returns `materialize(this.parser.root)` — partial-markdown's structural-shared snapshot. Same JS reference for unchanged subtrees (Angular `track`/CD short-circuits), new reference whenever any descendant changed. The DOM-node identity assertions in the identity-preservation spec verify this works: appending a paragraph keeps the prior paragraph's `

` element identity-stable.

Documented regressions

  • Tables regress. Markdown tables render as paragraphs with literal pipe characters in 0.0.20. Restored when partial-markdown v0.3 ships table support.
  • GFM task lists regress. `- [x]` renders as a literal text bullet rather than a checkbox. Restored at the same time.

No other rendered surface changes.

Sanitization

All `href`/`src` flows through Angular's built-in URL sanitization. No `bypassSecurityTrustHtml` calls. No `innerHTML` bindings. Verified by a `javascript:` URL test in the link spec.

Testing

  • 18 per-component unit specs (one per node type)
  • `` registry-dispatch spec (4 tests)
  • `cacheplaneMarkdownViews` registry shape spec (2 tests)
  • Integration corpus for `` (8 canonical samples × 2 push patterns = 16 tests)
  • Identity preservation spec (2 tests, real DOM-node reference equality)

`@ngaf/chat` 0.0.19 → `0.0.20`. `@cacheplane/partial-markdown@0.1.0` added as a runtime dep (chat-internal; not a peer dep). `marked` retained — still used by `messageContent` shared util elsewhere; cleanup is a follow-up.

🤖 Generated with Claude Code

blove and others added 14 commits May 4, 2026 10:38
Used by <chat-streaming-md> for streaming markdown AST. Implementation
detail (not a peer dep) — chat consumers don't need to install it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…link)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eading/blockquote/list/list-item/code-block)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the marked + innerHTML rendering pipeline with an Angular
template walking a @cacheplane/partial-markdown AST through @ngaf/render's
view registry. Stable parser identity → Angular track-by-id keeps DOM
stable across pushes. The default cacheplaneMarkdownViews registry
covers all 18 v0.1 node types; consumers override per-type via
withViews().

Tables and task lists regress (not in partial-markdown v0.1) — restored
when partial-markdown v0.3 ships them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-string)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The parser's root reference is stable across pushes (partial-markdown's
identity guarantee), so Angular's Object.is signal equality short-circuits
mid-stream content updates. Calling materialize() at the boundary surfaces
a structurally-shared snapshot whose root reference changes when any
descendant changes (via partial-markdown's per-node version fingerprint),
while unchanged subtrees keep their references stable. This is exactly
the property Angular signal CD needs.

The integration test's chunked path now exercises per-chunk detectChanges
(matching production usage), and the identity-preservation spec asserts
real DOM-node reference equality for unchanged subtrees.

Also adds @cacheplane/partial-markdown to allowedNonPeerDependencies in
ng-package.json (was already a runtime dependency but missing from the
ng-packagr allowlist, causing build failures).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cacheplane Ready Ready Preview, Comment May 4, 2026 6:21pm

Request Review

…rule

Renames all md-* Angular component selectors to chat-md-* (md-children,
md-document, md-paragraph, md-heading, md-blockquote, md-list, md-list-item,
md-code-block, md-thematic-break, md-text, md-emphasis, md-strong,
md-strikethrough, md-inline-code, md-link, md-autolink, md-image,
md-soft-break, md-hard-break) to satisfy the @angular-eslint/component-selector
rule requiring the 'chat' or 'a2ui' prefix. Updates all template usages and
spec files accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@blove blove merged commit 2e42776 into main May 4, 2026
14 checks passed
@blove blove deleted the claude/chat-streaming-md-partial-markdown branch May 4, 2026 18:27
blove added a commit that referenced this pull request Jun 9, 2026
#193)

* chore(chat): add @cacheplane/partial-markdown@0.1.0 dependency

Used by <chat-streaming-md> for streaming markdown AST. Implementation
detail (not a peer dep) — chat consumers don't need to install it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat/markdown): MARKDOWN_VIEW_REGISTRY DI token

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat/markdown): <md-children> recursive registry dispatcher

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat/markdown): leaf node renderers (text/breaks/code/image/autolink)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat/markdown): inline containers (em/strong/strike/link)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat/markdown): block-level node renderers (document/paragraph/heading/blockquote/list/list-item/code-block)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat/markdown): cacheplaneMarkdownViews default registry

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat): swap <chat-streaming-md> to @cacheplane/partial-markdown

Replaces the marked + innerHTML rendering pipeline with an Angular
template walking a @cacheplane/partial-markdown AST through @ngaf/render's
view registry. Stable parser identity → Angular track-by-id keeps DOM
stable across pushes. The default cacheplaneMarkdownViews registry
covers all 18 v0.1 node types; consumers override per-type via
withViews().

Tables and task lists regress (not in partial-markdown v0.1) — restored
when partial-markdown v0.3 ships them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(chat): update streaming-markdown spec for component-tree DOM

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(chat): integration corpus for chat-streaming-md (chunked + whole-string)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(chat): identity preservation across <chat-streaming-md> pushes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(chat): chat-streaming-md uses materialize() for streaming reactivity

The parser's root reference is stable across pushes (partial-markdown's
identity guarantee), so Angular's Object.is signal equality short-circuits
mid-stream content updates. Calling materialize() at the boundary surfaces
a structurally-shared snapshot whose root reference changes when any
descendant changes (via partial-markdown's per-node version fingerprint),
while unchanged subtrees keep their references stable. This is exactly
the property Angular signal CD needs.

The integration test's chunked path now exercises per-chunk detectChanges
(matching production usage), and the identity-preservation spec asserts
real DOM-node reference equality for unchanged subtrees.

Also adds @cacheplane/partial-markdown to allowedNonPeerDependencies in
ng-package.json (was already a runtime dependency but missing from the
ng-packagr allowlist, causing build failures).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(chat): export markdown view registry + per-node-type components

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: bump @ngaf/chat 0.0.19 → 0.0.20

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(chat): prefix markdown view selectors with chat- to satisfy lint rule

Renames all md-* Angular component selectors to chat-md-* (md-children,
md-document, md-paragraph, md-heading, md-blockquote, md-list, md-list-item,
md-code-block, md-thematic-break, md-text, md-emphasis, md-strong,
md-strikethrough, md-inline-code, md-link, md-autolink, md-image,
md-soft-break, md-hard-break) to satisfy the @angular-eslint/component-selector
rule requiring the 'chat' or 'a2ui' prefix. Updates all template usages and
spec files accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant