feat(a2ui-playground): preview QR pane — inline on desktop, drawer on mobile, draggable divider#2791
Conversation
…bile The container query `@container preview-panel (max-width: 660px)` always matched — the panel is fixed at `width: min(520px, 100%)`, so even on a wide desktop the inline COMPONENTS chips + Web/Native QR cards were hidden and the Vaul drawer behind a Smartphone icon was the only channel. Replace the container-width guard with a viewport-level `useMediaQuery` hook at the existing 720px mobile breakpoint, and conditionally mount either the inline `.previewPanelExtras` (desktop) or the `previewInfoBtn` + `Drawer.Root` (mobile). Both code paths still call the same `renderExtras()` closure, but only one is mounted at a time — so `QrCode`'s async `toDataURL` runs once per card and the live-component `tag-appear` animation doesn't fire twice. Add a staggered slide-up + fade entrance on the inline children (`cubic-bezier(0.22, 1, 0.36, 1)`, 360ms, 70ms stagger), with a `prefers-reduced-motion` opt-out, so the extras section glides into the panel when the preview becomes ready instead of popping in.
|
📝 WalkthroughWalkthroughAdds a client-side ChangesCompact viewport extras and animations
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. Comment |
There was a problem hiding this comment.
Pull request overview
This PR fixes the preview “extras” (components chips + Web/Native QR cards) rendering logic in a2ui-playground so desktop shows the extras inline in the right preview panel, while mobile continues to use the Vaul drawer. It replaces a container-query-based split (which always matched due to the panel’s fixed width) with a viewport breakpoint via a new useMediaQuery hook, and adds a subtle entrance animation for inline extras.
Changes:
- Replace the (always-matching) preview panel container query with a viewport breakpoint (
(max-width: 720px)) via a newuseMediaQueryhook. - Conditionally mount either inline extras (desktop) or the drawer trigger + drawer content (mobile) to avoid duplicate work/animations.
- Add a slide-up + fade-in animation for inline extras with a
prefers-reduced-motionopt-out.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/genui/a2ui-playground/src/styles.css | Removes the preview-panel container-query setup and adds mount-time entrance animation for inline extras. |
| packages/genui/a2ui-playground/src/hooks/useMediaQuery.ts | Introduces a matchMedia-based hook for viewport breakpoint detection. |
| packages/genui/a2ui-playground/src/components/PreviewPanel.tsx | Switches extras rendering logic to viewport-based branching; mounts inline vs drawer content exclusively per breakpoint. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…pane Build on the inline-desktop fix by letting the user resize the split between the phone preview and the COMPONENTS + QR cards below it. The handle borrows the visual language of the existing chat/preview `.panelResizeHandle` (8px hit area, top+bottom 1px borders, centered 2px grip dot that widens and brightens on hover/active, `ns-resize` cursor, global `body[data-panel-resize="vertical"]` while dragging). State lives in `PreviewPanel` and is hydrated from / persisted to `localStorage` (`a2ui-playground:preview-extras-height`, default 280px). The pointerdown handler captures the panel's current rect and the extras pane's `scrollHeight`, then clamps drag output to `[80, min(panelHeight - 200, contentNatural + 12)]` — the content cap prevents the user from dragging into wasted empty space below the QR cards when content is short. `.previewPanelExtras` moves from `display: contents` to a real flex column with `overflow-y: auto` and an inline-styled `height`, and the legacy `border-top` on its first child is dropped so the resizer's own borders are the only divider line. The mobile drawer path stays unchanged — no resizer inside the bottom sheet.
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
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/components/PreviewPanel.tsx`:
- Around line 375-433: The handler handleExtrasResizeStart sets
document.body.dataset.panelResize = 'vertical' and registers window pointer
listeners but only clears them in handleEnd; add a cleanup effect (useEffect)
that on unmount clears document.body.dataset.panelResize and removes any
lingering window listeners (pointermove/pointerup/pointercancel) tied to the
same handlers so the global marker/cursors and iframe pointer blocking can't get
stuck if the component unmounts or the extras branch is removed; locate
handleExtrasResizeStart and the inner handleMove/handleEnd closures and ensure
the effect references stable handler refs or re-registers safe no-op removers to
detach the exact listeners and delete the dataset key on cleanup.
🪄 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: 523848db-90c1-4aee-99de-5b86f6f7077a
📒 Files selected for processing (2)
packages/genui/a2ui-playground/src/components/PreviewPanel.tsxpackages/genui/a2ui-playground/src/styles.css
Summary
The preview-panel container query
@container preview-panel (max-width: 660px)always matched — the panel is fixed atwidth: min(520px, 100%), so on desktop the inline COMPONENTS chips + Web/Native QR cards were hidden and the Vaul drawer behind the Smartphone icon was the only channel.useMediaQuery('(max-width: 720px)')hook (matches the app's existing mobile breakpoint — same threshold asMobileTabBar,.brandhide, etc.)..previewPanelExtras(desktop) or thepreviewInfoBtn+Drawer.Root(mobile). Both code paths still call the samerenderExtras()closure, but only one is mounted at a time — soQrCode's asynctoDataURLruns once per card and the live-componenttag-appearanimation doesn't double up.cubic-bezier(0.22, 1, 0.36, 1), 360ms, 70ms stagger,prefers-reduced-motion: reduceopt-out) so the extras glide into the panel when the preview becomes ready, instead of popping in.Test plan
pnpm run buildsucceeds.build:lynxtypia ESM resolution failure on this workspace prevented an end-to-end preview, but the React shell + UI changes are exercised byrsbuild dev.Summary by CodeRabbit
New Features
Refactor
Style