Skip to content

feat(a2ui): add loading component instead of loading text#2746

Merged
Sherry-hue merged 1 commit into
lynx-family:mainfrom
Sherry-hue:feat/a2ui-loading-component
May 29, 2026
Merged

feat(a2ui): add loading component instead of loading text#2746
Sherry-hue merged 1 commit into
lynx-family:mainfrom
Sherry-hue:feat/a2ui-loading-component

Conversation

@Sherry-hue
Copy link
Copy Markdown
Collaborator

@Sherry-hue Sherry-hue commented May 28, 2026

Summary by CodeRabbit

  • New Features
    • Added a Loading UI (inline & block) with animated skeleton; registered in the component catalog and used as the default loading/placeholder in rendered and streamed flows.
  • Bug Fixes
    • Image-heavy content now shows Loading placeholders while image URLs are resolved, then swaps in final images; improved fallback handling for unresolved images.
  • Style
    • Primary button visuals updated with stronger surface/border tokens and refined shadow/highlight.
  • Documentation
    • Streaming guidance updated to recommend emitting a root Loading placeholder and handling image resolution as staged updates.

Review Change Stack

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 28, 2026

⚠️ No Changeset found

Latest commit: 923e1d7

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new A2UI Loading skeleton (inline/block) with CSS, exports and registers it in the catalog and playground, uses it as the renderer/stream placeholder, refactors image resolution to emit Loading placeholders and patch-based data updates, centralizes action validation, and updates data-model cloning and button styles.

Changes

Loading Component Feature

Layer / File(s) Summary
Loading component implementation
packages/genui/a2ui/src/catalog/Loading/index.tsx, packages/genui/a2ui/styles/catalog/Loading.css
LoadingProps with optional variant ('inline'
Module export surface
packages/genui/a2ui/src/catalog/index.ts, packages/genui/a2ui/src/index.ts, packages/genui/a2ui/package.json
Re-exports and package.json exports mappings added for ./catalog/Loading and its catalog.json.
Catalog schema and registration
packages/genui/server/agent/catalog/Loading/catalog.json, packages/genui/server/agent/a2ui-catalog.ts
Adds JSON schema for Loading.variant, imports loadingManifest, includes it in CATALOG_MANIFESTS, and registers Loading in COMPONENT_SUMMARIES.
A2UIRenderer default loading state
packages/genui/a2ui/src/react/A2UIRenderer.tsx
DefaultLoading now returns <Loading variant='block' /> instead of an inline text placeholder.
Playground catalog exposure
packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx, packages/genui/a2ui-playground/src/catalog/a2ui.ts
Playground imports Loading, registers it among builtins, and documents inline/block usage examples in the component catalog.
Public API & repo instructions
packages/genui/a2ui/etc/genui-a2ui.api.md, .github/a2ui-catalog.instructions.md
API report includes Loading in exported signatures and warnings; instructions updated to emit root Loading after createSurface and to use Loading placeholders while resolving Image URLs.
Button style update
packages/genui/a2ui/styles/catalog/Button.css
.button-primary now uses strong surface/border/on-surface tokens and overlay-based shadow with adjusted inset highlight alpha.

Streaming image resolution & action validation

Layer / File(s) Summary
Stream parser: pending-image tracking & conversion
packages/genui/server/agent/a2ui-stream-parser.ts
Adds per-surface pending image path sets, pointer utilities, and converts Image components to Loading (variant:'block') when the image path is pending; updateDataModel updates pending-paths and createSurface emits surface-loading.
Image resolver: patch-based resolution & loading replacement
packages/genui/server/agent/image-resolver.ts
Refactors resolution to replace non-loadable Images with Loading messages, schedule URL resolutions as ImageDataPatch promises, dedupe patches, append resolved updateComponents and updateDataModel patch messages, and generate SVG fallback images. Exposes isLoadableImageSource and pending-image replacement helper.
Routes: streaming post-processing & enqueueing
packages/genui/server/app/a2ui/action/stream/route.ts, packages/genui/server/app/a2ui/stream/route.ts
Introduces resolveMessagesForStreaming helper that enqueues intermediate Loading messages when replacements occur, then resolves final image URLs and returns protocol-split messages; action streaming endpoints use this helper.
Action validation shared types & usage
packages/genui/server/app/a2ui/_shared.ts, packages/genui/server/app/a2ui/action/route.ts, packages/genui/server/app/a2ui/action/stream/route.ts
Adds A2UIDispatchAction/ValidatedAction types and validateAction(); routes validate incoming action payloads and use the normalized validated action in upstream payloads and logs.
Validator: remove requiresAction enforcement
packages/genui/server/agent/a2ui-validator.ts
Removes the hasDispatchableAction helper and the catalog-driven "requiresAction" enforcement from validateA2UIOutput; action-related gating is no longer applied.

Playground hook & small fixes

Layer / File(s) Summary
Playground hook: clone data safety
packages/genui/a2ui-playground/src/hooks/useConversation.ts
Adds cloneDataValue and ensures applyDataModel clones root and per-path assigned values to avoid retaining references to input objects.
Playground app registration
packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
Adds Loading import and registers Loading in ALL_BUILTINS via manifestEntry.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • lynx-family/lynx-stack#2709: Related work on A2UI streaming/parser logic and protocol splitting; overlaps with stream parser changes.

Suggested reviewers

  • gaoachao
  • fzx2666-fz

Poem

🐰 I stitched a shimmer, tiny and bright,
Inline or block, I hop into sight,
While images wander, I calm the display,
Actions get checked, data clones stay,
Playground and streams hum soft through the night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 PR title accurately summarizes the main objective: replacing plain loading text with a dedicated Loading component across the a2ui catalog and renderer.
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

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.

PupilTong
PupilTong previously approved these changes May 28, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 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/styles/catalog/Button.css (1)

35-36: 💤 Low value

Consider increasing the shadow visibility for clearer button elevation

Button.css uses 0 10px 24px var(--a2ui-color-overlay) for the shadow. --a2ui-color-overlay is rgba(0, 0, 0, 0.05) (light) and rgba(248, 248, 248, 0.08) (dark) in theme.css, so the shadow may read quite subtle/flat despite the large spread.

0 10px 24px var(--a2ui-color-overlay),
0 1px 0 rgba(255, 255, 255, 0.4) inset;

If this is intended for a minimal aesthetic, keep it; otherwise consider bumping the overlay alpha (or reducing the spread) to better match the desired elevation for primary CTAs.

🤖 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/styles/catalog/Button.css` around lines 35 - 36, The
box-shadow in Button.css (the rule using "0 10px 24px var(--a2ui-color-overlay),
0 1px 0 rgba(255, 255, 255, 0.4) inset") is too subtle given the current
--a2ui-color-overlay values; either increase the alpha for --a2ui-color-overlay
in theme.css or adjust the Button.css shadow to reduce spread and/or increase
opacity so the elevation reads stronger (e.g., target the primary CTA shadow).
Update the --a2ui-color-overlay definition or the specific Button.css rule
(referencing --a2ui-color-overlay and the box-shadow declaration) to achieve
clearer elevation.
🤖 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/styles/catalog/Button.css`:
- Around line 35-36: The box-shadow in Button.css (the rule using "0 10px 24px
var(--a2ui-color-overlay), 0 1px 0 rgba(255, 255, 255, 0.4) inset") is too
subtle given the current --a2ui-color-overlay values; either increase the alpha
for --a2ui-color-overlay in theme.css or adjust the Button.css shadow to reduce
spread and/or increase opacity so the elevation reads stronger (e.g., target the
primary CTA shadow). Update the --a2ui-color-overlay definition or the specific
Button.css rule (referencing --a2ui-color-overlay and the box-shadow
declaration) to achieve clearer elevation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 60562344-d8b9-401a-8d4b-f90dc3e1a46b

📥 Commits

Reviewing files that changed from the base of the PR and between 076fc63 and b768fc8.

📒 Files selected for processing (12)
  • packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
  • packages/genui/a2ui-playground/src/catalog/a2ui.ts
  • packages/genui/a2ui/package.json
  • packages/genui/a2ui/src/catalog/Loading/index.tsx
  • packages/genui/a2ui/src/catalog/index.ts
  • packages/genui/a2ui/src/index.ts
  • packages/genui/a2ui/src/react/A2UIRenderer.tsx
  • packages/genui/a2ui/styles/catalog/Button.css
  • packages/genui/a2ui/styles/catalog/Loading.css
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-stream-parser.ts
  • packages/genui/server/agent/catalog/Loading/catalog.json

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 28, 2026

Merging this PR will improve performance by 11.41%

⚡ 1 improved benchmark
✅ 80 untouched benchmarks
⏩ 26 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
transform 1000 view elements 44.8 ms 40.3 ms +11.41%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Sherry-hue:feat/a2ui-loading-component (923e1d7) with main (9b1792c)

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.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@Sherry-hue Sherry-hue force-pushed the feat/a2ui-loading-component branch from 62dce13 to 6d857f8 Compare May 28, 2026 10:34
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

@Sherry-hue Sherry-hue force-pushed the feat/a2ui-loading-component branch 2 times, most recently from 3492b27 to 46bc53a Compare May 28, 2026 13:04
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.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/genui/server/agent/image-resolver.ts (1)

173-235: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep the latest image value authoritative within a batch.

If one batch first emits a non-loadable image value and then later emits a real URL for the same surfaceId/path, this code still keeps the earlier path marked as pending and still appends the earlier async patch afterward. Because dedupeImageDataPatches only removes identical {surfaceId, path, value} triples, the stale resolution can overwrite the later real URL and also force an unnecessary Loading/restore cycle. Please reconcile pending state against the final value seen for each surfaceId/path, and dedupe last-wins by path rather than by path+value.

Also applies to: 246-304, 410-479

🤖 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/server/agent/image-resolver.ts` around lines 173 - 235, The
batch can produce a stale async resolution that overwrites a later in-batch
value because dedupeImageDataPatches currently de-duplicates by
{surfaceId,path,value}; change the flow so pendingImagePathsBySurface and
appended pending restores are reconciled against the final resolved value per
path and deduping is last-wins by path: collect all promises in dataResolutions
as you already do, then after awaiting Promise.all(dataResolutions) build a Map
keyed by `${surfaceId}|${path}` keeping the last resolution for each key
(overwriting earlier ones), use that map instead of dedupeImageDataPatches (or
change dedupeImageDataPatches to implement last-wins by path), and remove any
pendingImagePathsBySurface entries (used by replacePendingPathImagesWithLoading)
for keys that now have a non-loading final value so pending restores are not
emitted for paths that were resolved later; update usages of
dedupeImageDataPatches, pendingImagePathsBySurface, and
replacePendingPathImagesWithLoading accordingly.
🤖 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.

Outside diff comments:
In `@packages/genui/server/agent/image-resolver.ts`:
- Around line 173-235: The batch can produce a stale async resolution that
overwrites a later in-batch value because dedupeImageDataPatches currently
de-duplicates by {surfaceId,path,value}; change the flow so
pendingImagePathsBySurface and appended pending restores are reconciled against
the final resolved value per path and deduping is last-wins by path: collect all
promises in dataResolutions as you already do, then after awaiting
Promise.all(dataResolutions) build a Map keyed by `${surfaceId}|${path}` keeping
the last resolution for each key (overwriting earlier ones), use that map
instead of dedupeImageDataPatches (or change dedupeImageDataPatches to implement
last-wins by path), and remove any pendingImagePathsBySurface entries (used by
replacePendingPathImagesWithLoading) for keys that now have a non-loading final
value so pending restores are not emitted for paths that were resolved later;
update usages of dedupeImageDataPatches, pendingImagePathsBySurface, and
replacePendingPathImagesWithLoading accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d5f1e67-d07a-482d-86fc-b9d0829870e6

📥 Commits

Reviewing files that changed from the base of the PR and between 6d857f8 and 46bc53a.

📒 Files selected for processing (21)
  • .github/a2ui-catalog.instructions.md
  • packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
  • packages/genui/a2ui-playground/src/catalog/a2ui.ts
  • packages/genui/a2ui-playground/src/hooks/useConversation.ts
  • packages/genui/a2ui/etc/genui-a2ui.api.md
  • packages/genui/a2ui/package.json
  • packages/genui/a2ui/src/catalog/Loading/index.tsx
  • packages/genui/a2ui/src/catalog/index.ts
  • packages/genui/a2ui/src/index.ts
  • packages/genui/a2ui/src/react/A2UIRenderer.tsx
  • packages/genui/a2ui/styles/catalog/Button.css
  • packages/genui/a2ui/styles/catalog/Loading.css
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-stream-parser.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/agent/catalog/Loading/catalog.json
  • packages/genui/server/agent/image-resolver.ts
  • packages/genui/server/app/a2ui/_shared.ts
  • packages/genui/server/app/a2ui/action/route.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts
💤 Files with no reviewable changes (1)
  • packages/genui/server/agent/a2ui-validator.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
  • packages/genui/a2ui/etc/genui-a2ui.api.md
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/genui/server/agent/catalog/Loading/catalog.json
  • packages/genui/a2ui-playground/src/catalog/a2ui.ts
  • packages/genui/a2ui/styles/catalog/Button.css
  • packages/genui/a2ui/styles/catalog/Loading.css
  • packages/genui/a2ui/src/react/A2UIRenderer.tsx
  • packages/genui/a2ui/src/index.ts
  • packages/genui/a2ui/package.json
  • packages/genui/a2ui/src/catalog/Loading/index.tsx

@Sherry-hue Sherry-hue enabled auto-merge (squash) May 28, 2026 13:24
HuJean
HuJean previously approved these changes May 28, 2026
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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@packages/genui/a2ui-playground/src/hooks/useConversation.ts`:
- Around line 83-90: The catch in cloneDataValue currently returns the original
reference on JSON cloning failure which allows shared references; change it to
return a safe, non-referential fallback (consistent with cloneDataModel) instead
of returning value—i.e., when value is an object and JSON.parse/JSON.stringify
throws, return an empty plain object (or an empty array if you detect the
original was an array) so callers of cloneDataValue do not receive the original
reference; update cloneDataValue accordingly and keep primitive handling
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 24fc7c1d-3176-4103-862e-c0386f078efc

📥 Commits

Reviewing files that changed from the base of the PR and between 46bc53a and 923e1d7.

📒 Files selected for processing (21)
  • .github/a2ui-catalog.instructions.md
  • packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
  • packages/genui/a2ui-playground/src/catalog/a2ui.ts
  • packages/genui/a2ui-playground/src/hooks/useConversation.ts
  • packages/genui/a2ui/etc/genui-a2ui.api.md
  • packages/genui/a2ui/package.json
  • packages/genui/a2ui/src/catalog/Loading/index.tsx
  • packages/genui/a2ui/src/catalog/index.ts
  • packages/genui/a2ui/src/index.ts
  • packages/genui/a2ui/src/react/A2UIRenderer.tsx
  • packages/genui/a2ui/styles/catalog/Button.css
  • packages/genui/a2ui/styles/catalog/Loading.css
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-stream-parser.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/agent/catalog/Loading/catalog.json
  • packages/genui/server/agent/image-resolver.ts
  • packages/genui/server/app/a2ui/_shared.ts
  • packages/genui/server/app/a2ui/action/route.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts
💤 Files with no reviewable changes (1)
  • packages/genui/server/agent/a2ui-validator.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/genui/a2ui/package.json
  • packages/genui/a2ui/src/index.ts
  • packages/genui/a2ui/styles/catalog/Button.css
  • packages/genui/a2ui/etc/genui-a2ui.api.md
🚧 Files skipped from review as they are similar to previous changes (14)
  • packages/genui/a2ui/src/catalog/index.ts
  • packages/genui/server/agent/a2ui-catalog.ts
  • .github/a2ui-catalog.instructions.md
  • packages/genui/server/agent/catalog/Loading/catalog.json
  • packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
  • packages/genui/a2ui-playground/src/catalog/a2ui.ts
  • packages/genui/a2ui/src/catalog/Loading/index.tsx
  • packages/genui/server/app/a2ui/stream/route.ts
  • packages/genui/a2ui/styles/catalog/Loading.css
  • packages/genui/server/app/a2ui/_shared.ts
  • packages/genui/server/agent/a2ui-stream-parser.ts
  • packages/genui/server/app/a2ui/action/route.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/agent/image-resolver.ts

Comment thread packages/genui/a2ui-playground/src/hooks/useConversation.ts
@Sherry-hue Sherry-hue merged commit 17f5102 into lynx-family:main May 29, 2026
62 of 71 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.

3 participants