feat(a2ui-playground): fullscreen preview for mobile#2557
Conversation
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (2)
📝 WalkthroughWalkthroughThe PR adds preview mode selection ( ChangesPreview Mode & Live Component Stack
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 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 unit tests (beta)
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. Review rate limit: 4/8 reviews remaining, refill in 28 minutes and 28 seconds.Comment |
There was a problem hiding this comment.
Pull request overview
Adds a fullscreen-capable Lynx preview experience in a2ui-playground, primarily to make mobile viewports usable by expanding the preview into a viewport-covering overlay (with auto-expand on small screens).
Changes:
- Add fullscreen overlay behavior for the preview panel (including auto-fullscreen on render when
window.innerWidth <= 980). - Add “Phone / Full” preview mode switch, including a new full-panel iframe mode.
- Update theming/title UI plumbing (dark theme via
data-theme, theme toggle button, and HTML title).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/genui/a2ui-playground/src/styles.css | Adds styling for fullscreen preview overlay, preview mode switch, and theme toggle; refactors phone frame styles. |
| packages/genui/a2ui-playground/src/pages/DemosPage.tsx | Implements preview fullscreen toggle + mobile auto-fullscreen + “Phone/Full” mode rendering. |
| packages/genui/a2ui-playground/src/components/MobilePreview.tsx | Simplifies phone preview markup to a single framed iframe. |
| packages/genui/a2ui-playground/src/App.tsx | Introduces data-theme dark-mode toggle and updates the top-bar brand text. |
| packages/genui/a2ui-playground/rsbuild.config.ts | Sets the generated HTML document title. |
Comments suppressed due to low confidence (1)
packages/genui/a2ui-playground/src/styles.css:388
.previewMetaTagsalso appears to be unused (no references in TS/TSX undersrc/). Consider removing it (or restoring the associated markup) to keep styles in sync with the DOM.
.previewMetaTags {
display: flex;
gap: 4px;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ? 'previewModeBtn active' | ||
| : 'previewModeBtn'} | ||
| onClick={() => setPreviewMode('full')} | ||
| title='Full panel' | ||
| > | ||
| Full | ||
| </button> | ||
| </div> |
| </div> | ||
|
|
||
| {/* Preview Panel */} | ||
| <div className='previewPanel'> | ||
| <div | ||
| className={fullscreen | ||
| ? 'previewPanel previewPanelFullscreen' | ||
| : 'previewPanel'} | ||
| > | ||
| <div className='previewPanelHeader'> | ||
| <span className='previewPanelTitle'>Lynx Preview</span> | ||
| {currentScenario | ||
| ? ( | ||
| <div className='previewPanelMeta'> | ||
| <div className='previewMetaTags'> | ||
| {currentScenario.tags.map((t) => <Chip key={t}>{t}</Chip>)} | ||
| </div> | ||
| </div> | ||
| ) | ||
| : null} | ||
| <div className='spacer' /> | ||
| <div className='previewModeSwitch'> | ||
| <button | ||
| type='button' | ||
| className={previewMode === 'phone' | ||
| ? 'previewModeBtn active' | ||
| : 'previewModeBtn'} | ||
| onClick={() => setPreviewMode('phone')} | ||
| title='Phone frame' | ||
| > | ||
| Phone | ||
| </button> | ||
| <button | ||
| type='button' | ||
| className={previewMode === 'full' |
| .previewPanelMeta { | ||
| flex: 1; | ||
| min-width: 0; | ||
| } | ||
|
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx`:
- Around line 386-415: In DemosPage.tsx update the preview control buttons to
include explicit ARIA semantics: for the Phone and Full buttons inside the
previewModeSwitch (the buttons that call setPreviewMode and read previewMode)
add an aria-pressed attribute that evaluates to true when previewMode ===
'phone' or previewMode === 'full' respectively; and for the fullscreen toggle
button with class previewExpandBtn (the one that calls setFullscreen and reads
fullscreen) add an aria-label that reflects the action (e.g., "Exit fullscreen"
when fullscreen is true, otherwise "Expand preview") so screen readers get a
meaningful description.
🪄 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: 7ba0bef6-07da-4698-9059-b932e228df73
📒 Files selected for processing (5)
packages/genui/a2ui-playground/rsbuild.config.tspackages/genui/a2ui-playground/src/App.tsxpackages/genui/a2ui-playground/src/components/MobilePreview.tsxpackages/genui/a2ui-playground/src/pages/DemosPage.tsxpackages/genui/a2ui-playground/src/styles.css
| <div className='previewModeSwitch'> | ||
| <button | ||
| type='button' | ||
| className={previewMode === 'phone' | ||
| ? 'previewModeBtn active' | ||
| : 'previewModeBtn'} | ||
| onClick={() => setPreviewMode('phone')} | ||
| title='Phone frame' | ||
| > | ||
| Phone | ||
| </button> | ||
| <button | ||
| type='button' | ||
| className={previewMode === 'full' | ||
| ? 'previewModeBtn active' | ||
| : 'previewModeBtn'} | ||
| onClick={() => setPreviewMode('full')} | ||
| title='Full panel' | ||
| > | ||
| Full | ||
| </button> | ||
| </div> | ||
| <button | ||
| type='button' | ||
| className='previewExpandBtn' | ||
| onClick={() => setFullscreen((v) => !v)} | ||
| title={fullscreen ? 'Exit fullscreen' : 'Expand preview'} | ||
| > | ||
| {fullscreen ? '\u2715' : '\u2922'} | ||
| </button> |
There was a problem hiding this comment.
Add explicit accessibility semantics to preview controls.
At Line 408, the fullscreen icon button should have an explicit aria-label.
At Lines 387-403, expose selected state on Phone/Full buttons via aria-pressed.
Suggested patch
<div className='previewModeSwitch'>
<button
type='button'
className={previewMode === 'phone'
? 'previewModeBtn active'
: 'previewModeBtn'}
onClick={() => setPreviewMode('phone')}
title='Phone frame'
+ aria-pressed={previewMode === 'phone'}
>
Phone
</button>
<button
type='button'
className={previewMode === 'full'
? 'previewModeBtn active'
: 'previewModeBtn'}
onClick={() => setPreviewMode('full')}
title='Full panel'
+ aria-pressed={previewMode === 'full'}
>
Full
</button>
</div>
<button
type='button'
className='previewExpandBtn'
onClick={() => setFullscreen((v) => !v)}
title={fullscreen ? 'Exit fullscreen' : 'Expand preview'}
+ aria-label={fullscreen ? 'Exit fullscreen preview' : 'Expand preview to fullscreen'}
>
{fullscreen ? '\u2715' : '\u2922'}
</button>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className='previewModeSwitch'> | |
| <button | |
| type='button' | |
| className={previewMode === 'phone' | |
| ? 'previewModeBtn active' | |
| : 'previewModeBtn'} | |
| onClick={() => setPreviewMode('phone')} | |
| title='Phone frame' | |
| > | |
| Phone | |
| </button> | |
| <button | |
| type='button' | |
| className={previewMode === 'full' | |
| ? 'previewModeBtn active' | |
| : 'previewModeBtn'} | |
| onClick={() => setPreviewMode('full')} | |
| title='Full panel' | |
| > | |
| Full | |
| </button> | |
| </div> | |
| <button | |
| type='button' | |
| className='previewExpandBtn' | |
| onClick={() => setFullscreen((v) => !v)} | |
| title={fullscreen ? 'Exit fullscreen' : 'Expand preview'} | |
| > | |
| {fullscreen ? '\u2715' : '\u2922'} | |
| </button> | |
| <div className='previewModeSwitch'> | |
| <button | |
| type='button' | |
| className={previewMode === 'phone' | |
| ? 'previewModeBtn active' | |
| : 'previewModeBtn'} | |
| onClick={() => setPreviewMode('phone')} | |
| title='Phone frame' | |
| aria-pressed={previewMode === 'phone'} | |
| > | |
| Phone | |
| </button> | |
| <button | |
| type='button' | |
| className={previewMode === 'full' | |
| ? 'previewModeBtn active' | |
| : 'previewModeBtn'} | |
| onClick={() => setPreviewMode('full')} | |
| title='Full panel' | |
| aria-pressed={previewMode === 'full'} | |
| > | |
| Full | |
| </button> | |
| </div> | |
| <button | |
| type='button' | |
| className='previewExpandBtn' | |
| onClick={() => setFullscreen((v) => !v)} | |
| title={fullscreen ? 'Exit fullscreen' : 'Expand preview'} | |
| aria-label={fullscreen ? 'Exit fullscreen preview' : 'Expand preview to fullscreen'} | |
| > | |
| {fullscreen ? '\u2715' : '\u2922'} | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx` around lines 386 -
415, In DemosPage.tsx update the preview control buttons to include explicit
ARIA semantics: for the Phone and Full buttons inside the previewModeSwitch (the
buttons that call setPreviewMode and read previewMode) add an aria-pressed
attribute that evaluates to true when previewMode === 'phone' or previewMode ===
'full' respectively; and for the fullscreen toggle button with class
previewExpandBtn (the one that calls setFullscreen and reads fullscreen) add an
aria-label that reflects the action (e.g., "Exit fullscreen" when fullscreen is
true, otherwise "Expand preview") so screen readers get a meaningful
description.
b27f091 to
d5553e7
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/genui/a2ui-playground/src/pages/DemosPage.tsx (1)
133-177:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPending replay timers survive failed renders and
Clear.Timeouts are only cancelled after
JSON.parsesucceeds. If the user clicks Render with invalid JSON—or hits Clear mid-replay—the previous callbacks keep firing and can repopulateliveComponentsagainst an empty/stale preview.Suggested patch
const doRender = useCallback( (json: string, scenario: Scenario | undefined) => { setError(''); setRenderQrError(''); + for (const t of liveTimersRef.current) clearTimeout(t); + liveTimersRef.current = []; + setLiveComponents([]); let parsed: unknown; try { parsed = JSON.parse(json); } catch (e) { setError(`Invalid JSON: ${String(e)}`); @@ - for (const t of liveTimersRef.current) clearTimeout(t); - liveTimersRef.current = []; - setLiveComponents([]); const perMsg = componentsByMessage(parsed); const delayMs = 800 / (speed || 1); let accumulated: string[] = []; @@ const handleClear = useCallback(() => { + for (const t of liveTimersRef.current) clearTimeout(t); + liveTimersRef.current = []; + setLiveComponents([]); setCustomJson('[]'); setRenderUrl(''); setLynxDevUrl(''); setRenderQrError(''); setError('');Also applies to: 309-316
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx` around lines 133 - 177, In doRender, existing replay timers in liveTimersRef.current must be cleared before attempting JSON.parse and also when clearing the preview; move the for/clearTimeout loop and liveTimersRef.current = [] plus setLiveComponents([]) to run at the start of doRender (before parsing) and ensure the Clear handler (the function that empties preview/state) also performs the same cleanup; on JSON.parse failure return after this cleanup so no stale timeouts can later repopulate liveComponents.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/genui/a2ui-playground/src/styles.css`:
- Around line 440-452: The keyframes name tagAppear violates the
keyframes-name-pattern; rename the `@keyframes` rule to a kebab-case name (e.g.,
tag-appear) and update any references to it (the animation property on the
selector that currently uses "tagAppear 300ms ease-out") to use the new
kebab-case identifier so Stylelint passes.
---
Outside diff comments:
In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx`:
- Around line 133-177: In doRender, existing replay timers in
liveTimersRef.current must be cleared before attempting JSON.parse and also when
clearing the preview; move the for/clearTimeout loop and liveTimersRef.current =
[] plus setLiveComponents([]) to run at the start of doRender (before parsing)
and ensure the Clear handler (the function that empties preview/state) also
performs the same cleanup; on JSON.parse failure return after this cleanup so no
stale timeouts can later repopulate liveComponents.
🪄 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: f56763bb-6b5f-4287-8518-c545bd8c307f
📒 Files selected for processing (3)
packages/genui/a2ui-playground/src/demos.tspackages/genui/a2ui-playground/src/pages/DemosPage.tsxpackages/genui/a2ui-playground/src/styles.css
| animation: tagAppear 300ms ease-out; | ||
| } | ||
|
|
||
| @keyframes tagAppear { | ||
| from { | ||
| opacity: 0; | ||
| transform: translateY(4px) scale(0.9); | ||
| } | ||
| to { | ||
| opacity: 1; | ||
| transform: translateY(0) scale(1); | ||
| } | ||
| } |
There was a problem hiding this comment.
Rename tagAppear to kebab-case so Stylelint passes.
The current keyframe name violates the configured keyframes-name-pattern, so this stylesheet will fail lint as-is.
Suggested patch
.liveComponentTag {
padding: 2px 8px;
border-radius: 4px;
background: var(--geist-surface);
border: 1px solid var(--geist-border);
font-size: 11px;
font-weight: 500;
color: var(--geist-foreground);
white-space: nowrap;
- animation: tagAppear 300ms ease-out;
+ animation: tag-appear 300ms ease-out;
}
-@keyframes tagAppear {
+@keyframes tag-appear {
from {
opacity: 0;
transform: translateY(4px) scale(0.9);
}🧰 Tools
🪛 Stylelint (17.9.0)
[error] 443-443: Expected keyframe name "tagAppear" to be kebab-case (keyframes-name-pattern)
(keyframes-name-pattern)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/genui/a2ui-playground/src/styles.css` around lines 440 - 452, The
keyframes name tagAppear violates the keyframes-name-pattern; rename the
`@keyframes` rule to a kebab-case name (e.g., tag-appear) and update any
references to it (the animation property on the selector that currently uses
"tagAppear 300ms ease-out") to use the new kebab-case identifier so Stylelint
passes.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
packages/genui/a2ui-playground/src/styles.css (1)
430-442:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRename
tagAppearto kebab-case so Stylelint passes.The keyframe name
tagAppearviolates thekeyframes-name-patternrule. Rename totag-appearfor consistency with the project's linting configuration.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/genui/a2ui-playground/src/styles.css` around lines 430 - 442, The keyframes name tagAppear violates the project's keyframes-name-pattern; rename the keyframe to tag-appear and update any uses accordingly (e.g., the animation property that currently references tagAppear) so `@keyframes` tag-appear { ... } matches the animation: tag-appear 300ms ease-out; in the CSS, preserving the same animation timing and transforms.packages/genui/a2ui-playground/src/pages/DemosPage.tsx (1)
404-433:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd explicit accessibility semantics to preview controls.
The fullscreen toggle button and Phone/Full mode buttons lack ARIA attributes for assistive technology users.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx` around lines 404 - 433, Add explicit ARIA semantics to the preview controls in DemosPage.tsx: for the Phone and Full mode buttons (rendered inside the previewModeSwitch that call setPreviewMode and read previewMode) add aria-pressed set to true when active/false otherwise and an aria-label like "Phone preview" / "Full preview"; also wrap them in a container with role="tablist" or add aria-controls pointing to the preview panel id if one exists. For the fullscreen toggle button (class previewExpandBtn using setFullscreen and reading fullscreen) add an aria-pressed attribute reflecting fullscreen state and a descriptive aria-label such as "Enter fullscreen" / "Exit fullscreen", and include aria-controls pointing to the preview panel if applicable so screen readers can associate the control with the preview.
🧹 Nitpick comments (1)
packages/genui/a2ui-playground/src/pages/DemosPage.tsx (1)
168-175: 💤 Low valuePotential stale closure issue with
accumulatedvariable.The
accumulatedvariable is declared withletoutside theforEachcallback and reassigned inside thesetTimeoutcallback. Because timers fire asynchronously and all closures share the sameaccumulatedbinding, each timer will see the mutations from previously-fired timers—which appears to be the intended behavior here. However, this pattern can be fragile if timer ordering ever changes.Consider using functional state updates for clarity and robustness:
♻️ Alternative using functional updates
- let accumulated: string[] = []; perMsg.forEach((newNames, i) => { if (newNames.length === 0) return; const timer = setTimeout(() => { - accumulated = [...accumulated, ...newNames]; - setLiveComponents([...accumulated]); + setLiveComponents((prev) => [...prev, ...newNames]); }, delayMs * (i + 1)); liveTimersRef.current.push(timer); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx` around lines 168 - 175, The code uses an outer mutable variable `accumulated` captured by multiple `setTimeout` callbacks which can lead to fragile ordering; replace that pattern by removing `accumulated` and using a functional state update inside each timeout: in the `perMsg.forEach` callback, call setTimeout(() => setLiveComponents(prev => [...prev, ...newNames]), delayMs * (i + 1)) so each timer appends `newNames` based on the latest state (use `perMsg`, `delayMs`, `newNames`, and `setLiveComponents` from the diff).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx`:
- Around line 162-177: The liveTimersRef timers are only cleared when doRender
runs, so on unmount pending timeouts can still call setLiveComponents; add a
React cleanup effect that on unmount iterates liveTimersRef.current and calls
clearTimeout for each to prevent stale updates. Place this useEffect (with an
empty deps array) near the component's state/hooks initialization (after the
existing state declarations) and reference liveTimersRef and setLiveComponents
so any outstanding timers are cleared when the component unmounts.
---
Duplicate comments:
In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx`:
- Around line 404-433: Add explicit ARIA semantics to the preview controls in
DemosPage.tsx: for the Phone and Full mode buttons (rendered inside the
previewModeSwitch that call setPreviewMode and read previewMode) add
aria-pressed set to true when active/false otherwise and an aria-label like
"Phone preview" / "Full preview"; also wrap them in a container with
role="tablist" or add aria-controls pointing to the preview panel id if one
exists. For the fullscreen toggle button (class previewExpandBtn using
setFullscreen and reading fullscreen) add an aria-pressed attribute reflecting
fullscreen state and a descriptive aria-label such as "Enter fullscreen" / "Exit
fullscreen", and include aria-controls pointing to the preview panel if
applicable so screen readers can associate the control with the preview.
In `@packages/genui/a2ui-playground/src/styles.css`:
- Around line 430-442: The keyframes name tagAppear violates the project's
keyframes-name-pattern; rename the keyframe to tag-appear and update any uses
accordingly (e.g., the animation property that currently references tagAppear)
so `@keyframes` tag-appear { ... } matches the animation: tag-appear 300ms
ease-out; in the CSS, preserving the same animation timing and transforms.
---
Nitpick comments:
In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx`:
- Around line 168-175: The code uses an outer mutable variable `accumulated`
captured by multiple `setTimeout` callbacks which can lead to fragile ordering;
replace that pattern by removing `accumulated` and using a functional state
update inside each timeout: in the `perMsg.forEach` callback, call setTimeout(()
=> setLiveComponents(prev => [...prev, ...newNames]), delayMs * (i + 1)) so each
timer appends `newNames` based on the latest state (use `perMsg`, `delayMs`,
`newNames`, and `setLiveComponents` from the diff).
🪄 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: f18c8268-56fc-4cf9-b959-31b4e0ab8438
📒 Files selected for processing (4)
packages/genui/a2ui-playground/src/App.tsxpackages/genui/a2ui-playground/src/demos.tspackages/genui/a2ui-playground/src/pages/DemosPage.tsxpackages/genui/a2ui-playground/src/styles.css
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/genui/a2ui-playground/src/App.tsx
| // Live component stack: reveal component names as they would appear | ||
| // during streaming, synced with the replay speed. | ||
| for (const t of liveTimersRef.current) clearTimeout(t); | ||
| liveTimersRef.current = []; | ||
| setLiveComponents([]); | ||
| const perMsg = componentsByMessage(parsed); | ||
| const delayMs = 800 / (speed || 1); | ||
| let accumulated: string[] = []; | ||
| perMsg.forEach((newNames, i) => { | ||
| if (newNames.length === 0) return; | ||
| const timer = setTimeout(() => { | ||
| accumulated = [...accumulated, ...newNames]; | ||
| setLiveComponents([...accumulated]); | ||
| }, delayMs * (i + 1)); | ||
| liveTimersRef.current.push(timer); | ||
| }); |
There was a problem hiding this comment.
Clear timers on component unmount to prevent memory leaks and stale state updates.
The timers stored in liveTimersRef.current are cleared when doRender is called again, but they are not cleared when the component unmounts. This can cause setLiveComponents to be called on an unmounted component.
🛡️ Proposed fix: Add cleanup effect
Add a cleanup effect after the existing state declarations (around line 106):
// Clean up live component timers on unmount
useEffect(() => {
return () => {
for (const t of liveTimersRef.current) clearTimeout(t);
};
}, []);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/genui/a2ui-playground/src/pages/DemosPage.tsx` around lines 162 -
177, The liveTimersRef timers are only cleared when doRender runs, so on unmount
pending timeouts can still call setLiveComponents; add a React cleanup effect
that on unmount iterates liveTimersRef.current and calls clearTimeout for each
to prevent stale updates. Place this useEffect (with an empty deps array) near
the component's state/hooks initialization (after the existing state
declarations) and reference liveTimersRef and setLiveComponents so any
outstanding timers are cleared when the component unmounts.
Merging this PR will improve performance by 18.84%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | 008-many-use-state-destroyBackground |
9.5 ms | 8 ms | +18.84% |
| ⚡ | transform 1000 view elements |
47 ms | 40 ms | +17.58% |
Comparing Huxpro:Huxpro/a2ui-mobile-preview (4748494) with main (f4f2d13)
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. ↩
Web Explorer#9340 Bundle Size — 900.03KiB (0%).4748494(current) vs f4f2d13 main#9339(baseline) Bundle metrics
Bundle size by type
|
| Current #9340 |
Baseline #9339 |
|
|---|---|---|
495.9KiB |
495.9KiB |
|
401.92KiB |
401.92KiB |
|
2.22KiB |
2.22KiB |
Bundle analysis report Branch Huxpro:Huxpro/a2ui-mobile-previe... Project dashboard
Generated by RelativeCI Documentation Report issue
React MTF Example#899 Bundle Size — 196.68KiB (0%).4748494(current) vs f4f2d13 main#898(baseline) Bundle metrics
|
| Current #899 |
Baseline #898 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
3 |
3 |
|
174 |
174 |
|
66 |
66 |
|
44.05% |
44.05% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #899 |
Baseline #898 |
|
|---|---|---|
111.23KiB |
111.23KiB |
|
85.45KiB |
85.45KiB |
Bundle analysis report Branch Huxpro:Huxpro/a2ui-mobile-previe... Project dashboard
Generated by RelativeCI Documentation Report issue
React External#882 Bundle Size — 680.82KiB (0%).4748494(current) vs f4f2d13 main#881(baseline) Bundle metrics
|
| Current #882 |
Baseline #881 |
|
|---|---|---|
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 Huxpro:Huxpro/a2ui-mobile-previe... Project dashboard
Generated by RelativeCI Documentation Report issue
React Example#7767 Bundle Size — 225.52KiB (0%).4748494(current) vs f4f2d13 main#7766(baseline) Bundle metrics
|
| Current #7767 |
Baseline #7766 |
|
|---|---|---|
0B |
0B |
|
0B |
0B |
|
0% |
0% |
|
0 |
0 |
|
4 |
4 |
|
180 |
180 |
|
69 |
69 |
|
44.54% |
44.54% |
|
2 |
2 |
|
0 |
0 |
Bundle size by type no changes
| Current #7767 |
Baseline #7766 |
|
|---|---|---|
145.76KiB |
145.76KiB |
|
79.77KiB |
79.77KiB |
Bundle analysis report Branch Huxpro:Huxpro/a2ui-mobile-previe... Project dashboard
Generated by RelativeCI Documentation Report issue
React Example (Element Template)#32 Bundle Size — 198.61KiB (0%).d5553e7(current) vs 4fc29ee main#31(baseline) Bundle metrics
Bundle size by type
|
| Current #32 |
Baseline #31 |
|
|---|---|---|
145.76KiB |
145.76KiB |
|
52.85KiB |
52.85KiB |
Bundle analysis report Branch Huxpro:Huxpro/a2ui-mobile-previe... Project dashboard
Generated by RelativeCI Documentation Report issue
- Add expand button in preview panel header to toggle fullscreen - Fullscreen overlay covers entire viewport with close button - Auto-enter fullscreen on mobile (<=980px) when rendering starts - Improves mobile UX where preview panel had limited height
Move the component chips (Card, Column, Image, etc.) from the preview panel header into the sidebar under each scenario name, so users can see which A2UI components each demo showcases before selecting it.
Show a live "Components" bar at the bottom of the preview panel that reveals component names (Card, Column, Image, etc.) as they appear during streaming simulation. Each tag animates in, synced with the replay speed. Uses componentsByMessage() to extract per-message component introductions from the demo JSON data.
d5553e7 to
4748494
Compare
React Example (Element Template)#35 Bundle Size — 198.61KiB (0%).4748494(current) vs f4f2d13 main#34(baseline) Bundle metrics
Bundle size by type
|
| Current #35 |
Baseline #34 |
|
|---|---|---|
145.76KiB |
145.76KiB |
|
52.85KiB |
52.85KiB |
Bundle analysis report Branch Huxpro:Huxpro/a2ui-mobile-previe... Project dashboard
Generated by RelativeCI Documentation Report issue
Summary
Depends on #2556.
On mobile viewports, the Lynx Preview panel has severely limited height due to the stacked column layout (sidebar + code panel + preview all compete for space). This makes it impossible to actually enjoy the rendered output.
Solution: Fullscreen preview overlay
Test plan
Summary by CodeRabbit
New Features
Style