fix(web/playground): tighten grid, fix Workspace void, unify Create chat composer#986
Conversation
…hat composer - Components tab: responsive grid capped at minmax(260px, 1fr)/320px so wide viewports show 4-5 cards/row instead of 8. Pack sections where every component lacks a registered preview (azure, aks, github) render as compact chip-style cards with a single explanatory banner, replacing the wall of 'No preview available' placeholders. - Workspace tab: added a fillContainer prop to FileViewer so the Playground editor fills the full width of its pane instead of being clamped to the 45% chat-side-panel sizing. Removes the large black void. - Create tab: aligned the chat composer with the main chat input (borderRadiusLarge, matching padding, stroke-1 border, 300-size font, ArrowRight send icon) so the Create entry point feels like chat. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Docs & changeset gate
Changeset present. Good. Hard gate for user-facing package changes without docs or changeset. ✅ = done, |
👀 Squad review trailCurrent head:
|
There was a problem hiding this comment.
🔒 Zapp — Security Review
Verdict: ✅ Approve — no security impact.
Scope reviewed
Playground.tsx (grid styling + compact card mode + createInputRow/createInput style overhaul + send-button icon swap), FileViewer.tsx (fillContainer boolean prop), PlaygroundWorkspace.tsx (pass the new prop). Read end-to-end for any new user-input path, URL-param surface, or preview-rendering change.
Findings
- No new user-input path. The Create composer is the same
createPrompttextarea as before; only its styling and icon change. No newdangerouslySetInnerHTML, no new URL param parsing, no new query-string reads, no new clipboard handlers. - Preview rendering is unchanged.
ComponentCardstill renders throughA2UIEnvelopePreview/COMPONENT_PREVIEWS. The compact-mode branch only changes layout (classes.compCardCompact, an 88px min-height, a different grid template). The no-preview branch switches from an inline-styled div toclasses.emptyPreviewLabel— still a static literal, no interpolation of untrusted data. - Icon swap is inert. Replacing
<img src="assets/icons/commands/go.svg">with<ArrowRight24Regular>removes a static asset reference; no new script execution, no CSP implications,script-src 'self'stays clean. fillContainerprop. Pure boolean. Defaultfalse, opt-in, togglesstyles.rootFillviamergeClasses. Does not affect data flow or trust boundaries.- No new deps, no new API calls, no secret handling, no auth changes.
Notes
- The
compPackBannercopy (Previews unavailable for pack-only components — showing compact cards.) is a static literal — fine. Do not later substitute this with an LLM- or pack-contributor-supplied string without escaping.
Applying zapp:approved.
There was a problem hiding this comment.
✅ Zapp recorded a security approved via zapp:approved on head 284b018.
This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.
sabbour
left a comment
There was a problem hiding this comment.
🏗️ Leela — Architecture Review
Verdict: Approve.
Scope is right — pure packages/web presentation polish, zero API or pack-contract surface. No architecture risk.
What's good:
FileViewer.fillContaineris opt-in withfillContainer = falsedefault. Existing chat-side-panel call sites stay on the 45%/55% clamp; onlyPlaygroundWorkspacepassesfillContainer. Backward-compatible prop addition done correctly — no consumer migration needed.- Grid sizing lives in
makeStylesclasses, not inline style props — keeps the responsive tuning centralised. ThecomponentGrid/componentCompactGridsplit is a reasonable way to express "this pack contributes no previews" as a data-driven layout variant rather than per-card conditional chrome. - Compact-grid transition is derived from data (
allEmpty = comps.every(c => !COMPONENT_PREVIEWS[c.name])), not hardcoded to pack names. When pack-azure/aks/github ship their own previews in the future, they'll automatically get the full grid — no follow-up refactor needed. Good future-proofing. - Composer parity with main chat: borrowing
borderRadiusLarge,stroke-1,fontSizeBase300,ArrowRight24Regular, and the spacing tokens keeps the Create surface visually consistent with the chat entry. Swapping the PNGcommands/go.svgfor the Fluent icon also drops a raster asset dependency — small net positive.
Non-blocking notes:
componentGridcaps each card atmaxWidth: 320pxvia descendant selector& > *, whilecomponentCompactGridcaps at 280px. If a pack ships a mix of preview and no-preview components in the future, the "all empty" gate toggles the whole section to compact — which is still the right default, but worth a comment onComponentCard.compactdescribing that the prop is pack-wide, not per-card.
Architecture 🟢 approved.
There was a problem hiding this comment.
✅ Leela recorded a architecture approved via leela:approved on head 284b018.
This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.
sabbour
left a comment
There was a problem hiding this comment.
🧪 Nibbler — Code Quality Review
Verdict: ✅ Approve — targeted CSS/UX polish, no regressions, behaviour-preserving.
Correctness
FileViewer's newfillContainerprop is opt-in (= falsedefault), so the existing chat-side-panel consumers keep theirwidth: 45%/maxWidth: 55%layout. OnlyPlaygroundWorkspacepassesfillContainer, which is the single site that needed the fix. No broadcast changes.mergeClasses(styles.root, fillContainer && styles.rootFill)—mergeClassestolerates falsy values, and the override rules (width: 100%; minWidth: 0; maxWidth: none; flex: 1) are applied late enough in the cascade to win againststyles.root. The "Select a file" empty-state path also gets the same merge, so the void is killed in both branches, not just the populated one.- Grid:
gridTemplateColumns: repeat(auto-fill, minmax(260px, 1fr))+'& > *': { maxWidth: '320px' }yields 4–5 cards per row at common viewports without breaking narrow widths (theminmax(260px, 1fr)floor is respected at ≥260px). The 320px cap produces wasted gutter space at very wide viewports, which is a deliberate UX choice documented in the PR and doesn't cause layout bugs. allEmpty = comps.every(c => !COMPONENT_PREVIEWS[c.name])— correct predicate. The compact grid is only applied when every component in a pack section lacks a preview, and the per-cardcompactprop is OR-ed with!hasPreviewso mixed sections still compact the individual empty cards. Good.
Create composer unification
ArrowRight24Regularreplaces thego.svg<img>in both render sites (lines 1463 and 1678); old asset is no longer referenced. The inlinestyle={{ width: '16px', height: '16px' }}scales the 24px icon viewBox correctly.- Padding/border/radius/font-size/line-height now match the main chat composer tokens (
borderRadiusLarge,colorNeutralStroke1,fontSizeBase300/lineHeightBase300,spacingVerticalS+spacingHorizontalM).alignItems: 'flex-end'is the right alignment for a growing textarea.
Loading / error states
- Pre-existing
registryError/registryLoadingbranches are untouched. The newcompPackBanneronly renders whenallEmpty— it does not suppress error states, and the error MessageBar still fires first. No path changes; no new error surfaces to cover.
Test coverage
- 🟡 Concern (non-blocking): zero new tests for the grid compacting logic (
allEmpty,compactprop propagation) or for thefillContainerbranch. Given this is a visual polish PR and both CI suites (unit + Playwright E2E) passed green, regression risk is low — but a small snapshot/class assertion for thecompactpath would lock in the "all-empty pack → compact grid + banner" contract so a future refactor ofCOMPONENT_PREVIEWSdoesn't silently flip it. Not blocking, but worth a follow-up.
🟢 Nit
-
className={${classes.compCardClickable}${isCompact ?${classes.compCardCompact}: ''}}— the rest of the file (and this PR) already usesmergeClasses(it's imported inFileViewer). UsingmergeClasses(classes.compCardClickable, isCompact && classes.compCardCompact)would be more idiomatic. Trivial.
CI green across lint/build/unit/check and Playwright E2E. Behaviour-preserving for every non-Playground consumer.
Approving.
|
Working as Scribe · see Retro entryQueued via retro-log PR #994: #994 |
Working as Scribe — see .squad/agents/scribe/charter.md
* fix(web): core components tab density + preview rendering (#995) #986 tightened the component grid but left two regressions on the Core tab: - Grid produced 6+ cards/row at 1920px viewports (target was 4-5). - Video, AudioPlayer, Tabs, Modal, Accordion had no COMPONENT_PREVIEWS entries, so the Core tab rendered 'No preview' placeholders next to real preview cards. - Add previews for all shipped core basic components. - Bump grid minmax (260->300) and card cap (320->380); gap -> spacingVerticalL. - Add preview-card min-height so live previews are legible. - Extract geometry to playground-layout-constants.ts so CSS and tests consume the same named constants (Nibbler DP ask). - Unit-test guard for Core preview coverage + density math. - Playwright assertions on card dimensions and preview visibility. Closes #995. References #986. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(squad/fry): log #995 outcome Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: sabbour-squad-frontend[bot] <275832692+sabbour-squad-frontend[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Updated GIT IDENTITY block to clarify that session-level GH_TOKEN export (done once at session start) covers all gh write commands: - gh pr review - gh pr comment - gh pr merge - gh pr edit - gh issue comment - etc. Previously, the template only mentioned 'push' and 'pr create', causing agents to omit GH_TOKEN for review/comment/merge commands, which fell through to authenticated user identity instead of bot identity. Ported from sabbour/squad PR #27. Fixes: #986 #989 #990
#1007) * squad: merge decisions inbox (7 files) + update agent histories (leela bug intake) - Merged .squad/decisions/inbox/ → decisions.md (7 inbox files, chronological order, deduplicated) - Deleted all inbox files after merge - Updated .squad/agents/fry/history.md: added note on bug #995, #997 assignments - Updated .squad/agents/bender/history.md: added note on bug #998 (priority:high, regression from #989), #996 assignments - Updated .squad/agents/zapp/history.md: consolidated history, archived entries > 15KB to history-archive.md, kept 2026-04-21 work only - Created .squad/orchestration-log/2026-04-21T10-50-06Z-leela.md: logged leela spawn outcome - Created .squad/log/2026-04-21T10-50-06Z-bug-intake.md: session log Leela spawn (2026-04-21T03:46:48-07:00 → 2026-04-21T03:50:06-07:00): - Filed 4 new issues (#995, #996, #997, #998) with squad triage labels - Confirmed 1 duplicate (#991) - Identified 1 high-priority regression (#998 from #989) - Surfaced 1 test-coverage gap (schema conformance audit) Decisions archive gate: No entries > 7 days old; no archival needed. Inbox files: 7 processed and deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(identity): session-export pattern covers all gh write commands Updated GIT IDENTITY block to clarify that session-level GH_TOKEN export (done once at session start) covers all gh write commands: - gh pr review - gh pr comment - gh pr merge - gh pr edit - gh issue comment - etc. Previously, the template only mentioned 'push' and 'pr create', causing agents to omit GH_TOKEN for review/comment/merge commands, which fell through to authenticated user identity instead of bot identity. Ported from sabbour/squad PR #27. Fixes: #986 #989 #990 * chore: add changeset for identity consistency fix Changeset for template and documentation updates that fix the identity consistency issue where reviewer agents posted as users instead of their bot identities on PRs #986/#989/#990. Ported from sabbour/squad PR #27. * Scribe: Merge Round 3 decisions + ceremony logs - Merged 5 inbox files (leela-round3, zapp-round3, nibbler-round3, zapp-993, nibbler-993) into decisions.md (149KB → 172KB) - Deleted inbox files after merge - Created orchestration logs for ralph, leela, zapp, nibbler, docs-gate, + session log - Updated agent histories: fry (lockout from PR #1000), bender (dual assignment #998 + #1000-revise), leela (DP closure + gate mechanics) - Summarized 4 agent histories (archive old logs; keep recent work + patterns) Round 3 Summary: - 5 DPs approved (DP #998 chat HIGH, #995–#997 FE, #987 Ideas tab blocked on #991) - PR #1001 merged (emit_ui fixture, all gates green) - PR #1000 rejected (red CI + missing CI grep rule; Fry locked out; bender-1000-revise assigned) - Mechanical gate (#993) active: 4-way + docs gate enforced on all future PRs - 5 security decisions documented (schema invariant, Ideas curated-only, composition harness, DP-time conditions) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(squad): bender — #998 decision + history Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(nibbler): round-4 review history + decisions (PRs #1005/#1000/#1003/#1004) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: scribe round-4 decisions merge + identity update + retrospective DECISIONS MERGED (7 inbox files → decisions.md): - bender-1000-revise-2026-04-21.md: Pack ./client subpath resolution, zod version skew cast pattern, pack-client guardrails via vitest - bender-998-fix-2026-04-21.md: Strict-required conformance for pack-core tool schemas (.nullable() in discriminatedUnion, parametrised test) - fry-995-fix-2026-04-21.md: Playground layout constants are single source of truth (CSS, unit test, Playwright) - fry-997-fix-2026-04-21.md: Workspace flex min-height:0 discipline, explicit geometry assertions - leela-round4-2026-04-21.md: Round-4 PR review (PRs #1005/#1000/#1003/#1004, 4-way gate summary) - nibbler-round4-2026-04-21.md: Code review verdicts, approved all 4 PRs, bundle-budget gate + geometry SSoT patterns locked in - zapp-round4-2026-04-21.md: Security verdicts, approved all 4 PRs, .nullable() discipline + vitest guardrail acceptance + bundle-budget protection IDENTITY STATUS UPDATED (identity/now.md): - Mode: bug-shipping-then-feature-unblock - Shipped: 5 UI bugs (#991, #980, #995, #997, #998) → PRs #1000–#1005 - In flight: #996 (allow-list drift), #987 (Playground E2E regression) - Round-4 learnings embedded (7 key lessons + implications) RETROSPECTIVE APPENDED (retro-log.md): - Round-4 summary: 5 bugs shipped, 5 PRs merged, 4-way gate cycle - 7 key learnings: 1. Stale agent verdicts must verify live CI state 2. Edit-but-not-commit causes silent test passes 3. Approval labels strip on PR synchronize (relabel pass needed) 4. Worktree hygiene overdue (stale fry-987, bender-996, etc.) 5. Bundle-budget ceiling gate proved ✅ 6. Named-constant geometry SSoT proved ✅ 7. Parametrised tool conformance test is durable ✅ - 5 implications for future rounds (agent validation, rebase discipline, label persistence, cleanup automation) Decisions inbox cleaned (all 7 files merged + deleted). decisions.md size: 204,010 bytes (+32 KB from round 3). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: sabbour-squad-frontend[bot] <275832692+sabbour-squad-frontend[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: sabbour-squad-backend[bot] <sabbour-squad-backend[bot]@users.noreply.github.com> Co-authored-by: sabbour-squad-lead[bot] <nibbler@squad.local> Co-authored-by: Bender (Backend Dev) <bender@squad.local>
Playground visual/UX polish (4 issues)
A. Components tab grid tightness + empty-card sprawl
Before: 8 cards per row on wide viewports; empty "No preview available" cards took full ~180px body like real preview cards, making core/azure/aks/github sections feel sparse and noisy.
After: Responsive grid capped at
repeat(auto-fill, minmax(260px, 1fr))with a 320px card max — 4–5 cards/row at standard viewports. Pack sections where every component lacks a registered preview (azure, aks, github) now render as compact chip-style cards (~88px min-height) with a single explanatory banner (Previews unavailable for pack-only components — showing compact cards.) replacing the wall of identical empty states.B. Workspace tab black void below the editor
Before: Workspace opened
server.tsin a viewer clamped to ~45% width/height, leaving a large black rectangle to the right of the editor.After: Added a
fillContainerprop toFileViewer(non-default; opt-in) that overrides the chat-side-panelwidth: 45%/maxWidth: 55%sizing withwidth: 100%+flex: 1. Playground Workspace now renders the viewer edge-to-edge in the right pane. Main App chat layout is unchanged becausefillContainerdefaults tofalse.C. Create tab chat input styled inconsistently with main chat
Before: Create tab used a bespoke hero-style input — different border radius, different padding, larger font, custom send icon (
commands/go.svg).After: Create composer styles mirror the main chat composer —
borderRadiusLarge,stroke-1border,spacing-s/spacing-mpadding,fontSizeBase300+lineHeightBase300, and the sameArrowRight24Regularicon on the send button. The Sparkle "Inspire me" affordance is preserved. Create now reads as a sibling chat entry point.D. Non-core packs sparse (azure/aks/github all "No preview available")
Covered by the Issue A compact empty-state + banner.
Validation
npm run lint— 0 errors, 59 pre-existing warnings (unchanged)CI=1 npm test -- --reporter=dot— 900 passed, 3 skipped, 159 todo (no regressions)Files changed
packages/web/src/pages/Playground.tsx— grid, compact card mode, composer style,ArrowRightsend iconpackages/web/src/components/FileManager/FileViewer.tsx—fillContainerproppackages/web/src/pages/PlaygroundWorkspace.tsx— passfillContainer.changeset/polish-playground-grid-workspace-create.md🤖 Created by sabbour-squad-frontend