feat(genui/playground): introduce OpenUI#2561
Conversation
📝 WalkthroughWalkthroughThis PR implements protocol-aware routing and dual-protocol support (a2ui/openui) in the a2ui-playground app. The refactoring replaces a single hardcoded protocol version with a Protocol object model, adds protocol-scoped hash routes, introduces new OpenUI demo and component pages, and updates all existing pages to be protocol-aware. ChangesProtocol-Aware A2UI Playground
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will degrade performance by 16.22%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | 008-many-use-state-destroyBackground |
8 ms | 9.5 ms | -16.22% |
Comparing feat/introduce-openui (f65bb7d) with main (dcd0b98)2
Footnotes
-
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. ↩
-
No successful run was found on
main(736ba38) during the generation of this report, so dcd0b98 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
React External#1040 Bundle Size — 690.27KiB (0%).f65bb7d(current) vs dcd0b98 main#1010(baseline) Bundle metrics
|
| Current #1040 |
Baseline #1010 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
3 |
3 |
|
17 |
17 |
|
5 |
5 |
|
8.59% |
8.59% |
|
0 |
0 |
|
0 |
0 |
Bundle analysis report Branch feat/introduce-openui Project dashboard
Generated by RelativeCI Documentation Report issue
React Example#7925 Bundle Size — 235.77KiB (0%).f65bb7d(current) vs dcd0b98 main#7895(baseline) Bundle metrics
|
| Current #7925 |
Baseline #7895 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
4 |
4 |
|
197 |
197 |
|
80 |
80 |
|
44.85% |
44.85% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #7925 |
Baseline #7895 |
|
|---|---|---|
145.76KiB |
145.76KiB |
|
90.01KiB |
90.01KiB |
Bundle analysis report Branch feat/introduce-openui Project dashboard
Generated by RelativeCI Documentation Report issue
React MTF Example#1055 Bundle Size — 206.69KiB (0%).f65bb7d(current) vs dcd0b98 main#1025(baseline) Bundle metrics
|
| Current #1055 |
Baseline #1025 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
3 |
3 |
|
192 |
192 |
|
77 |
77 |
|
44.36% |
44.36% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #1055 |
Baseline #1025 |
|
|---|---|---|
111.23KiB |
111.23KiB |
|
95.46KiB |
95.46KiB |
Bundle analysis report Branch feat/introduce-openui Project dashboard
Generated by RelativeCI Documentation Report issue
Web Explorer#9497 Bundle Size — 900.02KiB (0%).f65bb7d(current) vs dcd0b98 main#9467(baseline) Bundle metrics
|
| Current #9497 |
Baseline #9467 |
|
|---|---|---|
44.46KiB |
44.46KiB |
|
2.22KiB |
2.22KiB |
|
0% |
0% |
|
9 |
9 |
|
11 |
11 |
|
229 |
229 |
|
11 |
11 |
|
27.28% |
27.28% |
|
10 |
10 |
|
0 |
0 |
Bundle size by type no changes
| Current #9497 |
Baseline #9467 |
|
|---|---|---|
495.88KiB |
495.88KiB |
|
401.92KiB |
401.92KiB |
|
2.22KiB |
2.22KiB |
Bundle analysis report Branch feat/introduce-openui Project dashboard
Generated by RelativeCI Documentation Report issue
React Example with Element Template#190 Bundle Size — 197.77KiB (-0.54%).f65bb7d(current) vs dcd0b98 main#160(baseline) Bundle metrics
Bundle size by type
Bundle analysis report Branch feat/introduce-openui Project dashboard Generated by RelativeCI Documentation Report issue |
1c125c3 to
5dcfa19
Compare
ebbd89a to
c89b2f8
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
packages/genui/a2ui-playground/src/pages/OpenUIDemosPage.tsx (3)
53-66: ⚡ Quick win
buildOpenUIRenderUrlbypassessrc/utils/renderUrl.ts, violating the project coding guideline.The guideline for
packages/genui/a2ui-playground/src/**/*.tsxstates: "The web preview must encode the initData payload as base64 in the render URL using the utility insrc/utils/renderUrl.ts." This local function builds the render URL directly and passesrawTextas a plain query string instead of using the shared utility.If
renderUrl.tsdoesn't yet support OpenUI'srawTextformat, the right path is to extend it (e.g., add arawTextoption) so this page can go through the same utility. As per coding guidelines, the playground's web preview encoding should be centralised insrc/utils/renderUrl.ts.🤖 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-playground/src/pages/OpenUIDemosPage.tsx` around lines 53 - 66, buildOpenUIRenderUrl builds the render URL directly and passes rawText as a plain query param, violating the guideline to centralize web preview encoding in src/utils/renderUrl.ts; update the shared utility to accept a rawText (or initData.rawText) option that base64-encodes the payload and returns the proper render URL, then replace usage of buildOpenUIRenderUrl in OpenUIDemosPage.tsx with a call to that utility (keep the same protocol/openui and speed handling via the utility options) and remove the local buildOpenUIRenderUrl function.
130-152: ⚡ Quick winDead sequence-counter code should be removed.
lynxUrlSeqRefandseqare copied fromDemosPage.tsx, where they guard against stale async callbacks (the/__a2ui_payloadPOST). There is no async callback indoRenderhere, so the counter is never actually checked andvoid seqmerely silences the linter. This misleading pattern should be removed.🧹 Proposed cleanup
- const lynxUrlSeqRef = useRef(0); + // (no payload-store async path, so no sequence ref needed) ... const doRender = useCallback( (rawText: string) => { const url = buildOpenUIRenderUrl(rawText, networkBaseUrl, speed); setRenderUrl(url); - // Native Lynx dev URL - const seq = ++lynxUrlSeqRef.current; + // Native Lynx dev URL if (rspeedyDevUrl) { ... - void seq; // suppress unused warning } },🤖 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-playground/src/pages/OpenUIDemosPage.tsx` around lines 130 - 152, Remove the dead sequence-counter pattern: in doRender (or the effect shown) delete the increment and local var (const seq = ++lynxUrlSeqRef.current), the unused void seq statement, and any remaining references to lynxUrlSeqRef; if lynxUrlSeqRef becomes unused elsewhere in this module, remove its declaration/import as well so there’s no misleading stale-callback guard left in OpenUIDemosPage.tsx.
21-51: ⚡ Quick win
useRspeedyDevUrlandformatUrlForDisplayare duplicated fromDemosPage.tsx.Both functions are byte-for-byte identical to their counterparts in
DemosPage.tsx(lines 39–62 and 31–37 respectively). Any future change to the URL-fetching logic or display truncation must now be made in two places.Consider extracting them into shared utility modules, e.g.
src/hooks/useRspeedyDevUrl.tsandsrc/utils/urlDisplay.ts.🤖 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-playground/src/pages/OpenUIDemosPage.tsx` around lines 21 - 51, These two functions (useRspeedyDevUrl and formatUrlForDisplay) are duplicated; extract useRspeedyDevUrl into a shared hook (e.g., a new useRspeedyDevUrl hook) and extract formatUrlForDisplay into a shared utility (e.g., url display util), replace the local definitions in OpenUIDemosPage.tsx and DemosPage.tsx with imports from those new modules, and ensure the hook preserves the same behavior (state, effect, fetch to '/__rspeedy_url', cancellation flag) and the util preserves the same truncation logic (44-char head, 24-char tail, ellipsis when >80 chars).
🤖 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/lynx.config.ts`:
- Around line 22-26: The source.entry in lynx.config.ts is missing an entry for
OpenUI so the build never emits openui.web.js/openui.lynx.js; update
source.entry to include openui by adding an entry key like openui:
'./lynx-src/openui/index.tsx' (or alternatively confirm that OpenUI should reuse
the a2ui bundle and then update DEFAULT_OPENUI_DEMO_URL and
OpenUIDemosPage/OpenUIComponentsPage references to point at the a2ui bundle) so
that the OpenUI bundles are generated and <lynx-view url="./openui.web.js"> can
load at runtime.
In `@packages/genui/a2ui-playground/src/App.tsx`:
- Around line 149-154: The <select> for protocol lacks a programmatic label; add
an explicit association by either wrapping the text element with a <label> that
has htmlFor matching a new id on the select (e.g., give the select an id like
"protocolSelect" and use the existing protocolLabel as a <label
htmlFor="protocolSelect">) or add an aria-label/aria-labelledby to the select;
update the JSX around the protocolLabel/protocolSelect elements and keep
handleProtocolSelect and ProtocolName usage unchanged so the control remains
accessible to assistive tech.
- Around line 62-67: The current route resolution in App.tsx returns { protocol,
tab: 'create' } whenever rest[0] === 'chat' || rest[0] === 'create', which still
allows `#/openui/create` to produce an invalid tab; update the branch so that when
protocol.name === 'openui' you return { protocol, tab: 'examples' } instead of
'create' (i.e., when handling rest[0] === 'create' or 'chat', first check
protocol.name === 'openui' and normalize to 'examples'), ensuring the final
return and this conditional both map OpenUI to the 'examples' tab.
In `@packages/genui/a2ui-playground/src/pages/OpenUIDemosPage.tsx`:
- Around line 131-142: The pathname substitution using
u.pathname.replace('a2ui.lynx','openui.lynx') is fragile and can leave the URL
pointing to the wrong bundle; update the logic in OpenUIDemosPage.tsx where
rspeedyDevUrl is parsed: perform a safe replacement using the full segment (e.g.
'/a2ui.lynx') or test with a regex/wildcard to avoid partial matches, detect
whether the replace actually changed the pathname, and if it did not, either
construct the correct pathname by appending or replacing the final path segment
with '/openui.lynx' (or clear lynxDevUrl) before calling setLynxDevUrl;
reference variables/functions: rspeedyDevUrl, u.pathname, setLynxDevUrl.
In `@packages/genui/a2ui-playground/src/utils/renderUrl.ts`:
- Line 20: The protocol value sent by buildRenderUrl uses init.protocol.name
(e.g., 'a2ui' or 'openui') but render.tsx still checks for '0.9', causing
initData.protocol to be parsed as undefined; update the parsing logic in
render.tsx (where it computes protocolValue / initData.protocol) to accept
'a2ui' and 'openui' in addition to '0.9' (e.g., treat 'a2ui'|'openui'|'0.9' as
the legacy '0.9' value) or remove the protocol query param emitted by
buildRenderUrl (the url.searchParams.set('protocol', init.protocol.name) call)
if initData.protocol remains unused—modify either buildRenderUrl or render.tsx
accordingly, referencing buildRenderUrl and render.tsx (protocolValue /
initData.protocol) to keep formats consistent.
---
Nitpick comments:
In `@packages/genui/a2ui-playground/src/pages/OpenUIDemosPage.tsx`:
- Around line 53-66: buildOpenUIRenderUrl builds the render URL directly and
passes rawText as a plain query param, violating the guideline to centralize web
preview encoding in src/utils/renderUrl.ts; update the shared utility to accept
a rawText (or initData.rawText) option that base64-encodes the payload and
returns the proper render URL, then replace usage of buildOpenUIRenderUrl in
OpenUIDemosPage.tsx with a call to that utility (keep the same protocol/openui
and speed handling via the utility options) and remove the local
buildOpenUIRenderUrl function.
- Around line 130-152: Remove the dead sequence-counter pattern: in doRender (or
the effect shown) delete the increment and local var (const seq =
++lynxUrlSeqRef.current), the unused void seq statement, and any remaining
references to lynxUrlSeqRef; if lynxUrlSeqRef becomes unused elsewhere in this
module, remove its declaration/import as well so there’s no misleading
stale-callback guard left in OpenUIDemosPage.tsx.
- Around line 21-51: These two functions (useRspeedyDevUrl and
formatUrlForDisplay) are duplicated; extract useRspeedyDevUrl into a shared hook
(e.g., a new useRspeedyDevUrl hook) and extract formatUrlForDisplay into a
shared utility (e.g., url display util), replace the local definitions in
OpenUIDemosPage.tsx and DemosPage.tsx with imports from those new modules, and
ensure the hook preserves the same behavior (state, effect, fetch to
'/__rspeedy_url', cancellation flag) and the util preserves the same truncation
logic (44-char head, 24-char tail, ellipsis when >80 chars).
🪄 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: 2e044444-27fa-49bc-a257-5abbc7e0df89
📒 Files selected for processing (20)
.github/a2ui-catalog.instructions.mdpackages/genui/a2ui-playground/lynx-src/a2ui/App.tsxpackages/genui/a2ui-playground/lynx-src/a2ui/index.csspackages/genui/a2ui-playground/lynx-src/a2ui/index.tsxpackages/genui/a2ui-playground/lynx.config.tspackages/genui/a2ui-playground/src/App.tsxpackages/genui/a2ui-playground/src/componentCatalog.tspackages/genui/a2ui-playground/src/components/ProtocolSwitch.tsxpackages/genui/a2ui-playground/src/mock/openui-scenarios.tspackages/genui/a2ui-playground/src/pages/AIChatPage.tsxpackages/genui/a2ui-playground/src/pages/ComponentsPage.tsxpackages/genui/a2ui-playground/src/pages/DemosPage.tsxpackages/genui/a2ui-playground/src/pages/Home.tsxpackages/genui/a2ui-playground/src/pages/OpenUIComponentsPage.tsxpackages/genui/a2ui-playground/src/pages/OpenUIDemosPage.tsxpackages/genui/a2ui-playground/src/render.tsxpackages/genui/a2ui-playground/src/styles.csspackages/genui/a2ui-playground/src/utils/demoUrl.tspackages/genui/a2ui-playground/src/utils/protocol.tspackages/genui/a2ui-playground/src/utils/renderUrl.ts
Summary by CodeRabbit
New Features
Chores
Checklist