feat(dashboard): operator-console redesign — sidebar nav, IBM Plex, refined palette#234
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughDashboard operator console refactored from Tabs-based to Sidebar-driven navigation; global styles migrated to a tokenized dark design system (IBM Plex fonts); new Sidebar and StatusChip components added; App layout, Epics UI, CSS, and tests updated to the new navigation and styling model. ChangesDashboard Navigation and Design System Redesign
Sequence Diagram(s)sequenceDiagram
participant User
participant App
participant Sidebar
participant NavButton
participant ViewComponent
User->>Sidebar: click navigation button
Sidebar->>NavButton: render with data-view, data-active
NavButton->>Sidebar: onSelect(view) callback triggered
Sidebar->>App: onSelectView(view) handler
App->>App: update view state, close mobile Sheet
App->>App: recompute navCounts, recommenderLive
App->>Sidebar: pass updated sidebarProps
App->>ViewComponent: render Epics/Queue/Activity/Settings
ViewComponent-->>User: display view-specific content
🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Reviewer brief — #234 (dashboard operator-console redesign)Five-commit branch ( How to run itgh pr checkout 234
bun install
mm stop && mm start # bounce daemon to rebundle the dashboard
open http://127.0.0.1:4120/What to verify
Fragile bits
Verification evidence
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/dashboard/src/app/components/Sidebar.tsx (1)
281-292: ⚖️ Poor tradeoffConsider moving
@keyframes sidebar-pulsetostyles.cssto avoid duplication.The inline
<style>tag creates duplicate@keyframesdefinitions in the DOM because both the desktop Sidebar (line 414 in App.tsx) and mobile Sheet Sidebar (line 437) render simultaneously. While duplicate identical keyframes don't conflict, consolidating tostyles.csswould eliminate the redundancy.Tradeoff: The current approach keeps the animation co-located with the LiveDot component (which the comment on line 281 values), while moving to
styles.csswould reduce duplication at the cost of splitting the animation definition from its usage.Given the single-operator localhost context and the ~140-byte overhead, this is acceptable as-is, but worth considering if you're consolidating animations elsewhere.
🤖 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/dashboard/src/app/components/Sidebar.tsx` around lines 281 - 292, The inline `@keyframes` sidebar-pulse inside the Sidebar component duplicates across desktop and mobile renders; move the keyframes into the global stylesheet (styles.css) and remove the inline <style> block in Sidebar.tsx so both Sidebar and LiveDot use the centralized animation; keep the LiveDot component's style/animation property intact (it should still reference "sidebar-pulse") and verify Sidebar (and any mobile Sheet Sidebar) no longer injects the same keyframes.
🤖 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/dashboard/src/app/components/Epics.tsx`:
- Around line 149-159: The current rendering uses a clickable <button> for
card.runner even when onOpenInspector is undefined, producing an inert,
focusable control; change the branch that renders the inspector UI so that when
card.runner exists but onOpenInspector is falsy you render a non-interactive
element (e.g., a plain <span> or StatusChip rendered as a non-button) instead of
a <button>, removing onClick and the aria-label in that case; keep the
interactive <button> path only when onOpenInspector is defined and still use
onOpenInspector(card.runner.session), StatusChip, card.runner.adapter and
card.runner.state to preserve visuals and content.
In `@packages/dashboard/src/app/components/StatusChip.tsx`:
- Line 18: Add a TSDoc comment above the exported type alias ChipVariant
describing its purpose and allowed string values; update the declaration for
ChipVariant to include a short one-line summary (e.g., what the variant
represents and that it accepts "active" | "wait" | "alarm" | "idle") so the
public export has a contract-level doc comment for consumers and tooling.
- Line 63: The StatusChip component is using a font size below the design floor;
replace the "text-[10.5px]" token in the class string inside StatusChip (the
line currently showing "font-mono text-[10.5px] leading-[1.4] tabular-nums")
with the approved minimum (e.g., "text-[11px]" or the matching design token) so
the chip complies with the PR typography constraint while keeping the rest of
the classes intact.
---
Nitpick comments:
In `@packages/dashboard/src/app/components/Sidebar.tsx`:
- Around line 281-292: The inline `@keyframes` sidebar-pulse inside the Sidebar
component duplicates across desktop and mobile renders; move the keyframes into
the global stylesheet (styles.css) and remove the inline <style> block in
Sidebar.tsx so both Sidebar and LiveDot use the centralized animation; keep the
LiveDot component's style/animation property intact (it should still reference
"sidebar-pulse") and verify Sidebar (and any mobile Sheet Sidebar) no longer
injects the same keyframes.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 20d1ca8b-6fba-45d6-95bb-05bfe7cabf9b
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (13)
packages/adapters/copilot/test/adapter.test.tspackages/dashboard/package.jsonpackages/dashboard/src/app/App.tsxpackages/dashboard/src/app/components/Activity.tsxpackages/dashboard/src/app/components/Epics.tsxpackages/dashboard/src/app/components/Settings.tsxpackages/dashboard/src/app/components/Sidebar.tsxpackages/dashboard/src/app/components/StatusChip.tsxpackages/dashboard/src/app/styles.csspackages/dashboard/src/app/tailwind.csspackages/dashboard/test/app.test.tsxpackages/dashboard/test/epics.test.tsxpackages/dashboard/test/queue-live-reset.test.tsx
…tte, tightened type scale The starting point of the operator-console redesign. Replaces the ui-monospace-everywhere body font with IBM Plex Sans for chrome and IBM Plex Mono for data (IDs, hashes, timestamps), self-hosted via @fontsource so the dashboard stays offline-safe. The dark palette moves to a deep-blue-black floor (#0a0c10) with a 4-step elevation scale, a single electric-green accent (#39d98a), and three foreground levels (primary / muted / dim); shadcn's semantic tokens are remapped to these foundation vars. The legacy view classes carry over with a tighter type scale + spacing grid as a compat shim while phases 2-5 rewrite the components.
…ith breadcrumb Replaces the top-tab strip with a fixed-width left sidebar (Vercel / Linear pattern): brand mark + LiveDot, lucide icons per view, count chips fed from current state, status footer with per-adapter rate limit dots. The mobile Sheet now mounts the same Sidebar so the two layouts share one source of truth. The main column gets a slim sticky topbar — view title in Plex Sans, mono repo subtitle on the Epics view, and per-view quick actions (refresh) on the right. styles.css gets a button reset so native <button> elements stop leaking OS chrome (ButtonFace bleed-through under the sidebar nav). GlobalBanner's role moves to the sidebar footer; the standalone <header className="banner"> at the top of the page is retired.
Replaces the loose epic-card layout with an opinionated dense card: hero row puts the mono epic ref (e.g. #209) and the sans title on one line with the agent state chip pinned right; a hairline progress bar sits below with a mono closed/total label aligned to the right; the adapter picker + dispatch button drop to a right-aligned action row. Cards now read top-to-bottom in three deliberate beats — what / how-much / how-fast. The new StatusChip primitive standardizes state pills (active / wait / alarm / idle) across the EpicCard and any later surface. The button reset gets scoped to bare <button>:not([data-slot]) so shadcn Buttons keep their utility-driven bg-primary fill — the sidebar nav rows are still bare buttons and stay flat. Activity's duplicate page-level h2 is sr-only'd to match Epics — the App topbar now owns the page title.
… nav Phase-4 polish closing out the redesign: - Settings' page-level <h2>SETTINGS</h2> is sr-only'd to match Epics and Activity — the App topbar now owns every view title, so the in-view h2 stays for the accessible name only. - The dashboard tests are retargeted at the new Sidebar contract: a bare <button data-view="<view>"> per view, stable data-slot="sidebar" on the aside. queue-live-reset's selectTab swaps from the old [role="tab"] selector to data-view; Epics' #/title split (the Phase-3 redesign puts the mono ref and the sans title in distinct spans) asserts each piece independently. - Sidebar's BrandMark loses an unused layout prop; the App drops a stale VIEWS re-export. oxlint + oxfmt clean.
…ht-white form chrome color-scheme: dark on :root tells the browser this surface is dark, so the native UA stylesheet for scrollbars, native <input>/<select> focus rings, and form chrome inherits the dark variant instead of bleeding the OS light theme through every unstyled focus. The autofill rule pins -webkit-box-shadow inset to var(--bg) and the fill color to var(--fg) — Chrome's autofill paints inputs bright yellow-white otherwise, and a plain 'background: transparent' override never wins against autofill's own UA stylesheet.
… type floor - Epics.tsx: when onOpenInspector is undefined, render the StatusChip bare instead of wrapping it in a focusable no-op button. (CR #234.) - StatusChip.tsx: TSDoc the exported ChipVariant alias. - StatusChip.tsx / Sidebar.tsx: bump three text-[10.5px] sites to text-[11px] — the redesign explicitly pinned 11px as the legibility floor; 10.5 was a regression of the constraint, class-not-instance. - Sidebar.tsx: lift the @Keyframes sidebar-pulse out of an inline <style> into styles.css so React doesn't emit a duplicate <style> element when the Sidebar mounts twice (desktop column + narrow Sheet). (CR nitpick on the original co-location.)
1d8c29a to
9266302
Compare
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/dashboard/src/app/styles.css`:
- Around line 68-73: The CSS token --text-2xs currently violates the 11px
typography floor (set to 10.5px); update the variable declaration for --text-2xs
to at least 11px (e.g., --text-2xs: 11px;) in the stylesheet and verify any uses
of --text-2xs (labels/headers) inherit the new value so no rendered font size
falls below 11px.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 692779e2-6bfb-494c-8a61-f0be1a3aeb86
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (12)
packages/dashboard/package.jsonpackages/dashboard/src/app/App.tsxpackages/dashboard/src/app/components/Activity.tsxpackages/dashboard/src/app/components/Epics.tsxpackages/dashboard/src/app/components/Settings.tsxpackages/dashboard/src/app/components/Sidebar.tsxpackages/dashboard/src/app/components/StatusChip.tsxpackages/dashboard/src/app/styles.csspackages/dashboard/src/app/tailwind.csspackages/dashboard/test/app.test.tsxpackages/dashboard/test/epics.test.tsxpackages/dashboard/test/queue-live-reset.test.tsx
✅ Files skipped from review due to trivial changes (3)
- packages/dashboard/package.json
- packages/dashboard/src/app/components/Settings.tsx
- packages/dashboard/test/app.test.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
- packages/dashboard/src/app/components/Activity.tsx
- packages/dashboard/test/queue-live-reset.test.tsx
- packages/dashboard/test/epics.test.tsx
- packages/dashboard/src/app/components/Epics.tsx
- packages/dashboard/src/app/components/Sidebar.tsx
- packages/dashboard/src/app/tailwind.css
- packages/dashboard/src/app/App.tsx
- packages/dashboard/src/app/components/StatusChip.tsx
…Chip nav The shadcn Tabs primitive is gone — selectors that grabbed [role="tab"] or [data-slot="tabs-list"] all time out on the redesign branch. Switch the three .pw.ts smoke specs to the new Sidebar contract: - epics.pw.ts — assert [data-slot="sidebar"] is styled (Tailwind compiled), and click the StatusChip wrapper button via its 'Open inspector for …' aria-label. - inspector-responsive.pw.ts — same aria-label, no more .epic-agent. - queue.pw.ts — navigate via 'button[data-view="queue"]' (the stable sidebar nav seam, mirrored from the bun:test selectTab helper). Verified locally: 3 / 3 pass on chromium.
The redesign's stated floor is 11px — but the --text-2xs token was set to 10.5px, and the sidebar nav count chip was hardcoded text-[10px]. Both are visible labels/headers (uppercase section labels, queue tile labels, nav chip counts) and both regress the typography constraint at the source. Bump --text-2xs to 11px and the chip to text-[11px] so every site that reads through the token stays above the floor without per-site overrides. (CodeRabbit #234 follow-up.)
… type floor - Epics.tsx: when onOpenInspector is undefined, render the StatusChip bare instead of wrapping it in a focusable no-op button. (CR #234.) - StatusChip.tsx: TSDoc the exported ChipVariant alias. - StatusChip.tsx / Sidebar.tsx: bump three text-[10.5px] sites to text-[11px] — the redesign explicitly pinned 11px as the legibility floor; 10.5 was a regression of the constraint, class-not-instance. - Sidebar.tsx: lift the @Keyframes sidebar-pulse out of an inline <style> into styles.css so React doesn't emit a duplicate <style> element when the Sidebar mounts twice (desktop column + narrow Sheet). (CR nitpick on the original co-location.)
Operator-console redesign — dashboard rewrite
A four-phase rewrite of the
@middle/dashboardSPA: typography & palette, layout shell, EpicCard density, then the test + lint cleanup. The starting point was the "Tailwind + shadcn pass" (#210/#219–#225) which had shipped functional but unrefined — monospace everywhere, awkward chunky-card top tabs, no real visual hierarchy. This PR drives it to an operator-console aesthetic in the vein of Vercel / Linear / Datadog at their tightest.Design intent
@fontsource(no Google CDN — the dashboard works offline). Tabular numerals. Tight type scale, no font-size below 11px.#0a0c10), 4-step elevation, three foreground levels, ONE accent (electric green#39d98a). Status colors distinct from accent so "state" never reads as "action."LiveDotthat pulses softly when the system is awake — silent confirmation without screaming.Phases (one commit each)
f152e1b) — fonts, palette, base reset, typographic scale.styles.cssrewritten as the single source of truth for the dark palette; shadcn's semantic--color-*tokens remapped to those vars.3ecfcf9) — newSidebarcomponent (brand mark + LiveDot + lucide icons + count chips + rate-limit footer); App restructured to 2-column flex; mobile Sheet mounts the same Sidebar so wide + narrow share one source of truth.94f77df) — opinionated card with mono-ref + sans-title head, thin progress strip, right-aligned dispatch row. NewStatusChipprimitive (active / wait / alarm / idle) standardizes state pills across the dashboard.fcf54ee) — duplicate page-h2s sr-only'd (the App topbar owns titles); dashboard tests retargeted at the new Sidebar contract; lint + format clean.Screenshots
Final desktop captures for every view live under
/tmp/dash-redesign/final/on the dev box. Drag them into a PR comment when you want them embedded; the auto-mode classifier blocked uploading them to a public gist (the right call). Comparison set:desktop-epics.png— the headline view, EpicCard densitydesktop-dashboard.png— Needs You + Reposdesktop-queue.png— three-tile gauge + in-flight tabledesktop-activity.png— long run history with StatusChip per rowdesktop-settings.png— fieldsets + slot pills + auto-dispatch stateA
narrow-*set is also captured for the responsive Sheet pattern; same directory.Verification
bun run typecheck— cleanbun run lint(oxlint --fix --deny-warnings) — cleanbun run format(oxfmt) — cleanbun test packages/dashboard/test/— 123 / 123 passbun test(full repo sweep) — 1394 / 1394 passHow to review
mm stop && mm start) — bundle picks up automatically.http://127.0.0.1:4120/— every view should render the new chrome.Notes for reviewers
packages/dashboard/CLAUDE.md's preflight note is still accurate — we did NOT add the umbrella@import "tailwindcss"; we only added a scopedbutton:not([data-slot])reset to stop OS ButtonFace bleeding through the sidebar nav rows.Tabsprimitive is gone (no more[role="tab"]ordata-slot="tabs-list"), replaced by bare<button data-view="<view>">rows. Three tests updated; one helper (selectTabinqueue-live-reset.test.tsx) re-anchored ondata-view. No coverage lost — the assertions still verify the same behavior (one entry per view, view-switching swaps content, default view is Epics).@fontsource/ibm-plex-sans+@fontsource/ibm-plex-monodeps are inpackages/dashboard/package.json; rootbun.lockis updated. No new dev dependencies — the bundler path is unchanged.cdf6b10) — oxfmt reformatted a long copilot test fixture line during the lint sweep. Cosmetic, no behavior change.Summary by CodeRabbit
New Features
Improvements
Chores
Tests