Skip to content

feat(a2ui): pluggable defineCatalog API + extractor bin shim fix#2560

Merged
PupilTong merged 2 commits into
lynx-family:mainfrom
PupilTong:claude/a2ui-catalog-extractor
May 6, 2026
Merged

feat(a2ui): pluggable defineCatalog API + extractor bin shim fix#2560
PupilTong merged 2 commits into
lynx-family:mainfrom
PupilTong:claude/a2ui-catalog-extractor

Conversation

@PupilTong
Copy link
Copy Markdown
Collaborator

@PupilTong PupilTong commented May 6, 2026

Summary

  • Adds a runtime defineCatalog API that lets renderer consumers compose per-instance catalogs without depending on the global componentRegistry side-effect path. Bundlers tree-shake unused built-ins because the import surface is per-component.
  • Adds mergeCatalogs, serializeCatalog, and resolveCatalog so app shells can layer brand/page-level catalogs and emit the agent-handshake JSON (with optional per-component schemas) directly.
  • Harden the extractor CLI's bin-entry detection so bin/a2ui-catalog-extractor.{m,c,}js is recognized alongside direct ESM execution.

This PR is additiveexport * from './all.js' is preserved in catalog/index.ts, so existing consumers of the global registry are unaffected.

Companion to the broader headless-renderer refactor in #2536; this slice can land independently because nothing in core/ or chat/ depends on it.

Test plan

  • pnpm -F @lynx-js/a2ui-reactlynx build — extractor still emits dist/catalog/<Name>/catalog.json for all 10 built-ins.
  • pnpm -F @lynx-js/a2ui-catalog-extractor test — 6/6 extractor tests pass.
  • pnpm -F @lynx-js/a2ui-reactlynx exec tsc --noEmit — clean (pre-existing lynx-ui-input error in chat/Conversation.tsx is unrelated).
  • Spot-check defineCatalog([Text]) produces [{ name: 'Text', component: Text }].
  • Spot-check defineCatalog([[Text, textManifest]]) reads the name from the manifest key (survives minification).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a catalog system for composing, merging, resolving, and serializing UI component collections.
  • Documentation

    • Added a comprehensive catalog README with usage, schema guidance, recipes, and public API notes.
  • Refactor

    • Stabilized React renderer and data-binding import paths via compatibility re-exports.
  • Chores

    • Updated internal component imports to the new renderer/store paths; component APIs and behavior unchanged.

Introduce a runtime catalog API alongside the existing global
`componentRegistry`-based path. Composition is per-component so bundlers
tree-shake unused built-ins; the protocol name comes from each manifest
key (or `displayName ?? component.name` for bare components).

- `defineCatalog(inputs)` accepts bare components, `[component, manifest]`
  tuples, or already-resolved entries.
- `mergeCatalogs(...)` last-write-wins for layered overrides.
- `serializeCatalog(...)` emits the agent-handshake JSON, including
  per-component schemas when manifests are paired in.
- `resolveCatalog(...)` builds the name → component map the renderer uses.

Also harden the extractor's bin entry detection: `isEntryScript()` now
recognizes the published `bin/a2ui-catalog-extractor.{m,c,}js` shim so
running via the bin path works alongside direct ESM execution.

The new exports are additive — `export * from './all.js'` is preserved so
existing consumers of the global registry are unaffected.

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

coderabbitai Bot commented May 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 336cc209-cf0f-43c1-9fe5-9874114ab583

📥 Commits

Reviewing files that changed from the base of the PR and between f0c26c6 and 929deed.

📒 Files selected for processing (14)
  • packages/genui/a2ui/src/catalog/Button/index.tsx
  • packages/genui/a2ui/src/catalog/Card/index.tsx
  • packages/genui/a2ui/src/catalog/CheckBox/index.tsx
  • packages/genui/a2ui/src/catalog/Column/index.tsx
  • packages/genui/a2ui/src/catalog/Divider/index.tsx
  • packages/genui/a2ui/src/catalog/Image/index.tsx
  • packages/genui/a2ui/src/catalog/List/index.tsx
  • packages/genui/a2ui/src/catalog/RadioGroup/index.tsx
  • packages/genui/a2ui/src/catalog/Row/index.tsx
  • packages/genui/a2ui/src/catalog/Text/index.tsx
  • packages/genui/a2ui/src/catalog/defineCatalog.ts
  • packages/genui/a2ui/src/react/A2UIRenderer.tsx
  • packages/genui/a2ui/src/react/useDataBinding.ts
  • packages/genui/a2ui/src/store/types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/genui/a2ui/src/catalog/defineCatalog.ts

📝 Walkthrough

Walkthrough

Adds a typed catalog system and documentation for a2ui, re-exports its API, introduces compatibility barrels for React and store types used by catalog components, and refactors the CLI entry detection into a small isEntryScript() helper.

Changes

CLI Entry Refactor

Layer / File(s) Summary
Helper Extraction
packages/genui/a2ui-catalog-extractor/src/cli.ts
New isEntryScript() helper determines if the module is the invoked entry (checks process.argv[1] vs import.meta.url and published bin shim path).
Integration / Invocation
packages/genui/a2ui-catalog-extractor/src/cli.ts
Top-level entry guard replaced with isEntryScript() call; CLI invocation now delegated to runCli(process.argv.slice(2)) when true.

Catalog System (new API + docs)

Layer / File(s) Summary
Data Shape / Types
packages/genui/a2ui/src/catalog/defineCatalog.ts
Introduces CatalogSchema, CatalogManifest, CatalogComponent, CatalogInput, ResolvedCatalogEntry, Catalog, SerializedCatalog.
Core Helpers & Validation
packages/genui/a2ui/src/catalog/defineCatalog.ts
Adds isResolvedEntry, isTuple, deriveBareName, resolveInput and validation for missing names, empty manifests, duplicate names.
Core Implementation
packages/genui/a2ui/src/catalog/defineCatalog.ts
Adds defineCatalog(), mergeCatalogs(), resolveCatalog(), and serializeCatalog() implementing catalog construction, merging (last-write-wins), resolution to name→component map, and handshake-friendly serialization.
API Wiring
packages/genui/a2ui/src/catalog/index.ts
Re-exports new functions and types from ./defineCatalog.js.
Documentation
packages/genui/a2ui/src/catalog/README.md
New README describing catalog composition, minimal usage, schema pairing for agent handshake, built-ins recipe, custom component guidance, and the public API surface.

Compatibility / Import Surface Updates

Layer / File(s) Summary
Compatibility Barrels
packages/genui/a2ui/src/react/A2UIRenderer.tsx, packages/genui/a2ui/src/react/useDataBinding.ts, packages/genui/a2ui/src/store/types.ts
Added re-export barrels: A2UIRenderer & NodeRenderer map to core implementation; useDataBinding and store/types re-export from ../core to stabilize import paths.
Component Imports / Wiring
packages/genui/a2ui/src/catalog/*/index.tsx (Button, Card, CheckBox, Column, Divider, Image, List, RadioGroup, Row, Text)
Updated many catalog components to import A2UIRenderer/NodeRenderer from the new react barrel and GenericComponentProps from the new store barrel; Card also adds a CSS import for styles.
Package Manifests
packages/genui/a2ui-catalog-extractor/package.json, packages/genui/a2ui/.../package.json
Implicit/related package metadata changes referenced by diffs (manifest references present in summaries).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • HuJean
  • Sherry-hue
  • gaoachao
  • fzx2666-fz

Poem

🐰 In code I hop and tidy the trail,
Catalogs sprout where components prevail.
A helper stands guard at the CLI gate,
Barrels align paths — tidy and straight.
Merge, resolve, serialize — a rabbit's small fête 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: introducing a pluggable defineCatalog API for catalog composition and fixing the extractor CLI's entry-point detection logic.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/genui/a2ui/src/catalog/defineCatalog.ts (1)

72-112: ⚡ Quick win

Tighten runtime validation in isTuple / resolveInput.

Two small robustness gaps in the tuple path:

  1. isTuple only checks Array.isArray, so a malformed input like [Component] or [] is treated as a tuple and the destructure on line 97 yields manifest === undefined. The next line then throws a generic TypeError: Cannot convert undefined or null to object instead of the friendly [a2ui] … error users get for the empty-manifest case.
  2. The manifest is documented as a single top-level key ({ <ComponentName>: schema }), but keys[0]! silently picks an arbitrary key if more than one is present (e.g. if the extractor ever adds metadata or someone hand-merges manifests). A check would surface the mistake at defineCatalog time rather than producing a wrong protocol name.
♻️ Proposed tightening
 function isTuple(
   input: CatalogInput,
 ): input is readonly [CatalogComponent, CatalogManifest] {
-  return Array.isArray(input);
+  return Array.isArray(input) && input.length === 2;
 }
@@
   if (isTuple(input)) {
     const [component, manifest] = input;
+    if (typeof manifest !== 'object' || manifest === null) {
+      throw new Error(
+        '[a2ui] Tuple input expects `[component, manifest]`; '
+          + 'received a non-object manifest.',
+      );
+    }
     const keys = Object.keys(manifest);
     if (keys.length === 0) {
       throw new Error(
         '[a2ui] Empty manifest passed to defineCatalog; expected '
           + '`{ <ComponentName>: schema }`.',
       );
     }
+    if (keys.length > 1) {
+      throw new Error(
+        `[a2ui] Manifest passed to defineCatalog has multiple top-level `
+          + `keys (${keys.join(', ')}); expected exactly one `
+          + `\`{ <ComponentName>: schema }\`.`,
+      );
+    }
     const name = keys[0]!;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/genui/a2ui/src/catalog/defineCatalog.ts` around lines 72 - 112, The
tuple-detection and tuple-handling are too permissive: tighten isTuple to only
return true for a two-element array whose second element is a non-null object
(so malformed inputs like [] or [Component] aren’t treated as tuples), and in
resolveInput avoid destructuring before validation and explicitly validate the
manifest object and its keys count — throw a clear error if the manifest has no
keys or if it has more than one key (don’t silently pick keys[0]). Update
isTuple and resolveInput (referencing isTuple, resolveInput and deriveBareName)
to perform these checks and emit the friendly “[a2ui] …” errors for
empty/multi-key manifests instead of runtime TypeErrors or silent wrong-name
selection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/genui/a2ui/src/catalog/defineCatalog.ts`:
- Around line 72-112: The tuple-detection and tuple-handling are too permissive:
tighten isTuple to only return true for a two-element array whose second element
is a non-null object (so malformed inputs like [] or [Component] aren’t treated
as tuples), and in resolveInput avoid destructuring before validation and
explicitly validate the manifest object and its keys count — throw a clear error
if the manifest has no keys or if it has more than one key (don’t silently pick
keys[0]). Update isTuple and resolveInput (referencing isTuple, resolveInput and
deriveBareName) to perform these checks and emit the friendly “[a2ui] …” errors
for empty/multi-key manifests instead of runtime TypeErrors or silent wrong-name
selection.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f5a7a1be-f6fc-43d5-be49-f4549dda4d3d

📥 Commits

Reviewing files that changed from the base of the PR and between c6159f7 and f0c26c6.

📒 Files selected for processing (4)
  • packages/genui/a2ui-catalog-extractor/src/cli.ts
  • packages/genui/a2ui/src/catalog/README.md
  • packages/genui/a2ui/src/catalog/defineCatalog.ts
  • packages/genui/a2ui/src/catalog/index.ts

@PupilTong PupilTong self-assigned this May 6, 2026
Move the catalog `<Name>/index.tsx` files (and `defineCatalog.ts`) to
import from `store/types` and `react/A2UIRenderer` so they sit on the
post-refactor module layout.

Add three thin compatibility re-exports so the new paths resolve
against the still-present `core/` implementation:

- `src/store/types.ts` → re-exports `core/types.js`
- `src/react/A2UIRenderer.tsx` → re-exports `A2UIRender as A2UIRenderer`
  and `NodeRenderer` from `core/A2UIRender.jsx`
- `src/react/useDataBinding.ts` → re-exports `core/useDataBinding.js`

The full headless-renderer refactor (lynx-family#2536) replaces these shims with
real implementations; this PR only relocates the catalog import surface
so that follow-up rebases shrink to a no-op delete of the shims.

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

changeset-bot Bot commented May 6, 2026

⚠️ No Changeset found

Latest commit: 929deed

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 6, 2026

Merging this PR will improve performance by 18.85%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
✅ 79 untouched benchmarks
⏩ 26 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
008-many-use-state-destroyBackground 9.5 ms 8 ms +18.85%
transform 1000 view elements 47 ms 39.9 ms +17.72%

Comparing PupilTong:claude/a2ui-catalog-extractor (929deed) with main (0c6e660)

Open in CodSpeed

Footnotes

  1. 26 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 6, 2026

Web Explorer

#9368 Bundle Size — 900.03KiB (0%).

929deed(current) vs 0c6e660 main#9366(baseline)

Bundle metrics  no changes
                 Current
#9368
     Baseline
#9366
No change  Initial JS 44.46KiB 44.46KiB
No change  Initial CSS 2.22KiB 2.22KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 9 9
No change  Assets 11 11
No change  Modules 229 229
No change  Duplicate Modules 11 11
No change  Duplicate Code 27.28% 27.28%
No change  Packages 10 10
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#9368
     Baseline
#9366
No change  JS 495.9KiB 495.9KiB
No change  Other 401.92KiB 401.92KiB
No change  CSS 2.22KiB 2.22KiB

Bundle analysis reportBranch PupilTong:claude/a2ui-catalog-ex...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 6, 2026

React Example with Element Template

#62 Bundle Size — 198.12KiB (0%).

929deed(current) vs 0c6e660 main#60(baseline)

Bundle metrics  Change 2 changes
                 Current
#62
     Baseline
#60
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
Change  Modules 78(-1.27%) 79
No change  Duplicate Modules 23 23
Change  Duplicate Code 40.57%(+0.05%) 40.55%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#62
     Baseline
#60
No change  IMG 145.76KiB 145.76KiB
No change  Other 52.36KiB 52.36KiB

Bundle analysis reportBranch PupilTong:claude/a2ui-catalog-ex...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 6, 2026

React MTF Example

#927 Bundle Size — 196.68KiB (0%).

929deed(current) vs 0c6e660 main#925(baseline)

Bundle metrics  no changes
                 Current
#927
     Baseline
#925
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 174 174
No change  Duplicate Modules 66 66
No change  Duplicate Code 44.05% 44.05%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#927
     Baseline
#925
No change  IMG 111.23KiB 111.23KiB
No change  Other 85.45KiB 85.45KiB

Bundle analysis reportBranch PupilTong:claude/a2ui-catalog-ex...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 6, 2026

React Example

#7795 Bundle Size — 225.52KiB (0%).

929deed(current) vs 0c6e660 main#7793(baseline)

Bundle metrics  no changes
                 Current
#7795
     Baseline
#7793
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 180 180
No change  Duplicate Modules 69 69
No change  Duplicate Code 44.54% 44.54%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#7795
     Baseline
#7793
No change  IMG 145.76KiB 145.76KiB
No change  Other 79.77KiB 79.77KiB

Bundle analysis reportBranch PupilTong:claude/a2ui-catalog-ex...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 6, 2026

React External

#910 Bundle Size — 680.82KiB (0%).

929deed(current) vs 0c6e660 main#907(baseline)

Bundle metrics  no changes
                 Current
#910
     Baseline
#907
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 17 17
No change  Duplicate Modules 5 5
No change  Duplicate Code 8.59% 8.59%
No change  Packages 0 0
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#910
     Baseline
#907
No change  Other 680.82KiB 680.82KiB

Bundle analysis reportBranch PupilTong:claude/a2ui-catalog-ex...Project dashboard


Generated by RelativeCIDocumentationReport issue

@PupilTong PupilTong merged commit 5f3b6eb into lynx-family:main May 6, 2026
82 of 85 checks passed
PupilTong added a commit to PupilTong/lynx-stack that referenced this pull request May 6, 2026
Reshape `@lynx-js/a2ui-reactlynx` so the renderer, the protocol message
buffer, and the catalog are independently composable.

- Renderer (`src/react/`) is headless: `<A2UI>` / `<A2UIRenderer>` ship
  no styles or chrome, and consumers wrap surfaces themselves via
  `wrapSurface`. The previous `core/A2UIRender` + global
  `componentRegistry` path is removed.
- Message buffer (`src/store/MessageStore.ts`) is a dumb append-only
  buffer. The developer's IO module pushes raw v0.9 protocol messages
  into it; `<A2UI>` subscribes via `useSyncExternalStore`, owns its
  own `MessageProcessor`, and processes new tail messages each render.
- Catalog API (`src/catalog/defineCatalog.ts`, landed earlier in lynx-family#2560)
  is now the only registration path; the side-effect-based
  `componentRegistry` is gone, and built-ins re-export from
  `<Name>/index.tsx` with a paired `catalog.json` manifest.
- Examples relocated under `a2ui-playground/examples/` (chat shell,
  mock IO, SSE IO) so they're discoverable without bloating the
  package surface.
- web-core: stop redeclaring `fetch` as a chunk-scope binding so BTS
  chunks reuse `window.fetch` (needed by the playground IO module).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PupilTong added a commit to PupilTong/lynx-stack that referenced this pull request May 6, 2026
Reshape `@lynx-js/a2ui-reactlynx` so the renderer, the protocol message
buffer, and the catalog are independently composable.

- Renderer (`src/react/`) is headless: `<A2UI>` / `<A2UIRenderer>` ship
  no styles or chrome, and consumers wrap surfaces themselves via
  `wrapSurface`. The previous `core/A2UIRender` + global
  `componentRegistry` path is removed.
- Message buffer (`src/store/MessageStore.ts`) is a dumb append-only
  buffer. The developer's IO module pushes raw v0.9 protocol messages
  into it; `<A2UI>` subscribes via `useSyncExternalStore`, owns its
  own `MessageProcessor`, and processes new tail messages each render.
- Catalog API (`src/catalog/defineCatalog.ts`, landed earlier in lynx-family#2560)
  is now the only registration path; the side-effect-based
  `componentRegistry` is gone, and built-ins re-export from
  `<Name>/index.tsx` with a paired `catalog.json` manifest.
- Examples relocated under `a2ui-playground/examples/` (chat shell,
  mock IO, SSE IO) so they're discoverable without bloating the
  package surface.
- web-core: stop redeclaring `fetch` as a chunk-scope binding so BTS
  chunks reuse `window.fetch` (needed by the playground IO module).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PupilTong added a commit to PupilTong/lynx-stack that referenced this pull request May 6, 2026
Reshape `@lynx-js/a2ui-reactlynx` so the renderer, the protocol message
buffer, and the catalog are independently composable.

- Renderer (`src/react/`) is headless: `<A2UI>` / `<A2UIRenderer>` ship
  no styles or chrome, and consumers wrap surfaces themselves via
  `wrapSurface`. The previous `core/A2UIRender` + global
  `componentRegistry` path is removed.
- Message buffer (`src/store/MessageStore.ts`) is a dumb append-only
  buffer. The developer's IO module pushes raw v0.9 protocol messages
  into it; `<A2UI>` subscribes via `useSyncExternalStore`, owns its
  own `MessageProcessor`, and processes new tail messages each render.
- Catalog API (`src/catalog/defineCatalog.ts`, landed earlier in lynx-family#2560)
  is now the only registration path; the side-effect-based
  `componentRegistry` is gone, and built-ins re-export from
  `<Name>/index.tsx` with a paired `catalog.json` manifest.
- Examples relocated under `a2ui-playground/examples/` (chat shell,
  mock IO, SSE IO) so they're discoverable without bloating the
  package surface.
- web-core: stop redeclaring `fetch` as a chunk-scope binding so BTS
  chunks reuse `window.fetch` (needed by the playground IO module).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PupilTong added a commit to PupilTong/lynx-stack that referenced this pull request May 6, 2026
Reshape `@lynx-js/a2ui-reactlynx` so the renderer, the protocol message
buffer, and the catalog are independently composable.

- Renderer (`src/react/`) is headless: `<A2UI>` / `<A2UIRenderer>` ship
  no styles or chrome, and consumers wrap surfaces themselves via
  `wrapSurface`. The previous `core/A2UIRender` + global
  `componentRegistry` path is removed.
- Message buffer (`src/store/MessageStore.ts`) is a dumb append-only
  buffer. The developer's IO module pushes raw v0.9 protocol messages
  into it; `<A2UI>` subscribes via `useSyncExternalStore`, owns its
  own `MessageProcessor`, and processes new tail messages each render.
- Catalog API (`src/catalog/defineCatalog.ts`, landed earlier in lynx-family#2560)
  is now the only registration path; the side-effect-based
  `componentRegistry` is gone, and built-ins re-export from
  `<Name>/index.tsx` with a paired `catalog.json` manifest.
- Examples relocated under `a2ui-playground/examples/` (chat shell,
  mock IO, SSE IO) so they're discoverable without bloating the
  package surface.
- web-core: stop redeclaring `fetch` as a chunk-scope binding so BTS
  chunks reuse `window.fetch` (needed by the playground IO module).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PupilTong added a commit to PupilTong/lynx-stack that referenced this pull request May 6, 2026
Reshape `@lynx-js/a2ui-reactlynx` so the renderer, the protocol message
buffer, and the catalog are independently composable.

- Renderer (`src/react/`) is headless: `<A2UI>` / `<A2UIRenderer>` ship
  no styles or chrome, and consumers wrap surfaces themselves via
  `wrapSurface`. The previous `core/A2UIRender` + global
  `componentRegistry` path is removed.
- Message buffer (`src/store/MessageStore.ts`) is a dumb append-only
  buffer. The developer's IO module pushes raw v0.9 protocol messages
  into it; `<A2UI>` subscribes via `useSyncExternalStore`, owns its
  own `MessageProcessor`, and processes new tail messages each render.
- Catalog API (`src/catalog/defineCatalog.ts`, landed earlier in lynx-family#2560)
  is now the only registration path; the side-effect-based
  `componentRegistry` is gone, and built-ins re-export from
  `<Name>/index.tsx` with a paired `catalog.json` manifest.
- Examples relocated under `a2ui-playground/examples/` (chat shell,
  mock IO, SSE IO) so they're discoverable without bloating the
  package surface.
- web-core: stop redeclaring `fetch` as a chunk-scope binding so BTS
  chunks reuse `window.fetch` (needed by the playground IO module).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PupilTong added a commit to PupilTong/lynx-stack that referenced this pull request May 6, 2026
Reshape `@lynx-js/a2ui-reactlynx` so the renderer, the protocol message
buffer, and the catalog are independently composable.

- Renderer (`src/react/`) is headless: `<A2UI>` / `<A2UIRenderer>` ship
  no styles or chrome, and consumers wrap surfaces themselves via
  `wrapSurface`. The previous `core/A2UIRender` + global
  `componentRegistry` path is removed.
- Message buffer (`src/store/MessageStore.ts`) is a dumb append-only
  buffer. The developer's IO module pushes raw v0.9 protocol messages
  into it; `<A2UI>` subscribes via `useSyncExternalStore`, owns its
  own `MessageProcessor`, and processes new tail messages each render.
- Catalog API (`src/catalog/defineCatalog.ts`, landed earlier in lynx-family#2560)
  is now the only registration path; the side-effect-based
  `componentRegistry` is gone, and built-ins re-export from
  `<Name>/index.tsx` with a paired `catalog.json` manifest.
- Examples relocated under `a2ui-playground/examples/` (chat shell,
  mock IO, SSE IO) so they're discoverable without bloating the
  package surface.
- web-core: stop redeclaring `fetch` as a chunk-scope binding so BTS
  chunks reuse `window.fetch` (needed by the playground IO module).
PupilTong added a commit to PupilTong/lynx-stack that referenced this pull request May 7, 2026
Reshape `@lynx-js/a2ui-reactlynx` so the renderer, the protocol message
buffer, and the catalog are independently composable.

- Renderer (`src/react/`) is headless: `<A2UI>` / `<A2UIRenderer>` ship
  no styles or chrome, and consumers wrap surfaces themselves via
  `wrapSurface`. The previous `core/A2UIRender` + global
  `componentRegistry` path is removed.
- Message buffer (`src/store/MessageStore.ts`) is a dumb append-only
  buffer. The developer's IO module pushes raw v0.9 protocol messages
  into it; `<A2UI>` subscribes via `useSyncExternalStore`, owns its
  own `MessageProcessor`, and processes new tail messages each render.
- Catalog API (`src/catalog/defineCatalog.ts`, landed earlier in lynx-family#2560)
  is now the only registration path; the side-effect-based
  `componentRegistry` is gone, and built-ins re-export from
  `<Name>/index.tsx` with a paired `catalog.json` manifest.
- Examples relocated under `a2ui-playground/examples/` (chat shell,
  mock IO, SSE IO) so they're discoverable without bloating the
  package surface.
- web-core: stop redeclaring `fetch` as a chunk-scope binding so BTS
  chunks reuse `window.fetch` (needed by the playground IO module).
This was referenced May 13, 2026
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.

2 participants