Skip to content

feat(chat): progressive per-component GenUI rendering (PR B)#297

Merged
blove merged 8 commits into
mainfrom
claude/genui-per-component-fallback
May 13, 2026
Merged

feat(chat): progressive per-component GenUI rendering (PR B)#297
blove merged 8 commits into
mainfrom
claude/genui-per-component-fallback

Conversation

@blove
Copy link
Copy Markdown
Contributor

@blove blove commented May 13, 2026

Summary

Phase B of the progressive GenUI rendering redesign. Builds on PR #295 (PR A — surface store readiness) to render the A2UI surface progressively: per-component fallbacks swap to real components in place as bindings resolve.

  • New `A2uiDefaultFallbackComponent` (internal) — three shimmer rows, "Building UI…" label, themed via chat tokens. The lib-default skeleton mounted when a catalog entry omits its own `fallback`.
  • New `a2uiSlot` recursive structural directive — monotonic mount gate: while `!ready`, mount the fallback; once `ready=true`, mount the real component and never remount it. Subsequent updates push inputs via `ComponentRef.setInput()`.
  • `` rewritten with a new `state: A2uiSurfaceState` input that takes priority for rendering AND action-message construction; the legacy `surface` + `` path is preserved as a backwards-compat fallback.
  • Chat composition wires `[state]` into `` via a new `a2uiSurfaceStates` signal on the content classifier.

Class-level JSDoc on `A2uiSurfaceComponent` documents the dual-input precedence contract; a dual-mode spec asserts `state` takes priority over `surface` when both are set.

Base: claude/genui-surface-store-readiness (PR #295). After PR #295 merges to main, this PR's effective base becomes main.

Test plan

  • `npx nx test chat` passes
  • `npx nx build chat` passes (strict-TS prod build clean)
  • Live smoke at http://localhost:4200/embed: per-component skeletons swap to real in place, no double-bubble

@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

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

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

Request Review

@blove blove changed the base branch from claude/genui-surface-store-readiness to main May 13, 2026 17:15
blove and others added 6 commits May 13, 2026 10:26
Internal skeleton component mounted by <a2ui-surface> when a
component's bindings are unresolved (and the catalog entry omits
a fallback). Three shimmer rows, 'Building UI' label, themed via
chat tokens.
Internal directive used by <a2ui-surface> to render a single
A2uiComponentView. Mounts views[type].fallback (or the lib-default
fallback) while !ready, then the real component once ready=true.
A monotonic gate stops re-checking ready after the real mounts;
later updates flow as ComponentRef.setInput() calls.
When given the new state input (A2uiSurfaceState), the surface walks
componentViews via a2uiSlot — each node mounts its fallback while
!ready, then the real component once ready, with a monotonic gate
that prevents flicker. The legacy wire-format surface input keeps
working via the existing render-spec path.
Strict TS noUnusedLocals flags it under the production build. The
structural directive only needs ViewContainerRef — TemplateRef was
declared but never read.
The chat composition now passes [state] to <a2ui-surface> so the
progressive per-component renderer activates. ContentClassifier
exposes a new a2uiSurfaceStates signal (parallel to a2uiSurfaces)
sourced from the live A2uiSurfaceStore.

The bubble-level <chat-genui-skeleton> branch was removed in an
earlier commit; the surface now owns its empty-state via the
internal A2uiDefaultFallbackComponent.
… test, JSDoc)

- internalHandlers prefers state.surface over the legacy surface input
  so action messages always reference the rendered surface, even when
  both inputs are set with mismatched surfaceIds.
- Adds a surface.component.spec test asserting state takes priority
  over surface for rendering.
- Adds class-level JSDoc on A2uiSurfaceComponent explaining the
  dual-input precedence contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Rename test-only components in a2ui-slot.directive.spec.ts and
  surface.component.spec.ts to use 'a2ui-test-*' selectors per
  @angular-eslint/component-selector.
- Replace constructor parameter injection with inject() in
  a2ui-slot.directive.ts per @angular-eslint/prefer-inject.
Picks up the new state, surfaceFallback, rootIds members on
A2uiSurfaceComponent and the a2uiSurfaceStates field on the
content classifier.
@blove blove merged commit 8289f77 into main May 13, 2026
14 checks passed
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