Conversation
Two cohesive cleanups in one pass; both are pure surface-level (no behavior or layout changes), and both unblock the larger pane-level refactors that come next. ## New primitive: StatusDot `desktop/src/components/ui/status-dot.tsx` — small colored dot indicating a state (running, error, working, etc.). Replaces ~14 hand-rolled `<span className="size-X rounded-full bg-X" />` instances across the shell so a single change here propagates everywhere. cva-based variants matching real usage: - `variant`: success | destructive | warning | info | primary | muted | neutral - `size`: sm (6px, default) | md (8px) | lg (10px) - `withRing`: card-colored ring for badge dots that sit on top of icons - `pulse`: enables animate-pulse for "in progress" / "starting" states Sites swapped: AppSurfacePane, IntegrationsList, ConnectIntegrationsStep, PublishScreen, SpaceBrowserDisplayPane, SpaceApplicationsExplorerPane, SpaceBrowserExplorerPane (5 sites), OperationsDrawer (badge dot, withRing + lg), ChatPane (inbox-unread badge), RuntimeStatusIndicator (refactored visual spec to carry `dotVariant` + `dotPulse` instead of a Tailwind class string), AuthPanel, CreatingView. ## Token cleanup Eliminate hand-rolled opacity hacks on canonical tokens — these were masking earlier insufficient-contrast palettes that the current design system has since resolved at the token level. - `border-border/35|40|55|60|70` → `border-border` (28 occurrences across 13 files) - `bg-foreground/6` → `bg-fg-6`; `bg-foreground/5` → `bg-fg-5` - The dropdown-menu / select shadcn templates keep their `bg-foreground/10` because those live inside `**:data-[state=...]` variant selectors that need a literal token. Magic-number radii mapped to the standard scale: | Hand-rolled | Mapped to | |---|---| | `rounded-[7px]` | `rounded-md` | | `rounded-[10px]` | `rounded-lg` | | `rounded-[12px]` | `rounded-xl` | | `rounded-[14px]` | `rounded-2xl` | | `rounded-[16px]` | `rounded-2xl` | | `rounded-[18px]` | `rounded-2xl` | | `rounded-[20px]` | `rounded-2xl` | | `rounded-[22px]` | `rounded-2xl` | | `rounded-[24px]` | `rounded-3xl` | | `rounded-[28px]` | `rounded-3xl` | | `rounded-[4px]` | `rounded-sm` | | `rounded-[9999px]` | `rounded-full` | The 18/20/28 → 2xl/3xl mappings shrink the affected dialog/sheet shells by 2-4px on a single visual element. Small enough to be imperceptible side-by-side but consolidates the radius scale to shadcn standards. Two intentional pixel-level decorations stay hand-tuned: the tooltip arrow corner (rounded-[2px]) and the chat streaming-cursor block (rounded-[1px]). ## Verification - `npm run desktop:typecheck` — clean - `npm --prefix desktop run build:renderer` — clean - `node --test` on AppShell + SpaceApplicationsExplorerPane — same pass count as upstream/main baseline (49 pass / 1 pre-existing fail on an unrelated `updateNotification` brittle assertion). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dashboard's `EmptyState` was a great primitive locked in the wrong
folder. Five other surfaces hand-rolled the same pattern (sidebar
empties, automations placeholders, publish-wizard docs empties,
marketplace gallery empties), each with subtly different copy
hierarchy and chip treatments.
This PR moves `EmptyState` to `desktop/src/components/ui/`, extends
its API, and migrates the hand-rolled call sites.
## API
```tsx
<EmptyState
icon={Icon} // optional LucideIcon
title="No items" // primary line (was `message`)
description="Sub line" // optional secondary (was `hint`)
action={<Button…/>} // optional CTA below
size="sm" | "md" // sm = compact dashboard look (default),
// md = chip-icon + text-sm + roomier padding
minHeight={number} // forces height (chart panels)
className="" // outer wrapper extras
/>
```
`sm` keeps the original dashboard visual (small unframed icon at
opacity-45, text-xs message). `md` wraps the icon in a muted chip and
uses text-sm/text-xs for title/description — the look that
SpaceApplicationsExplorerPane, SpaceBrowserExplorerPane, and
AutomationsPane were rolling by hand.
## Migrations
- **dashboard/{Board,Table,List,Chart}** (4 sites): import path moved,
`message` → `title`, `hint` → `description`. Visual unchanged.
- **AutomationsPane**: deleted local 21-line `EmptyState` + 30-line
`EmptyScheduled` re-implementations. The CTA "Ask the agent" now
uses the new `action` slot.
- **SpaceApplicationsExplorerPane** + **SpaceBrowserExplorerPane**:
inline chip-icon empty states swapped for `<EmptyState size="md" />`.
- **publish/LivePreviewPanel**: deleted local 8-line `DocsEmptyState`,
swapped its 2 call sites.
- **marketplace/AppsGallery**: the "No apps available." and "No apps
match the current filter." inline divs become real EmptyStates.
Filter empty surfaces a Search icon + the existing Clear-filter CTA
via the new `action` slot.
- **marketplace/MarketplaceGallery**: same treatment — Search icon for
filter empty, LayoutGrid for the "no templates yet" case.
## Deliberately not migrated
- **OperationsDrawer.EmptyNotice**: 5 call sites use it for spinner +
error states, not just empty. Different semantic; primitive doesn't
cover it cleanly.
- **InternalSurfacePane.EmptyState**: framed bordered card with
tone="error" variant for file-preview failures. Also different —
it's a "preview frame", not a placeholder.
- **BackgroundTasksPane** + **SubagentSessionsPane**: dashed-border
containers with single-line text. Intentionally different visual
(placeholder zone, not centered empty state).
## Verification
- `npm run desktop:typecheck` — clean
- `npm --prefix desktop run build:renderer` — clean
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jotyy
added a commit
that referenced
this pull request
May 7, 2026
Sweep through settings-scope panels (Account, Billing, Submissions)
and replace card-shaped `shadow-md` with `border border-border` so the
visual language matches `SettingsCard` and the full-screen settings
reference. Net effect: every settings card now reads as a quiet
hairline-bordered group, with the page itself doing the heavy visual
framing.
Sites updated:
- `auth/AuthPanel.tsx` — connected-providers list (3567), empty-state
CTA card (3579), add-provider clickable row (3600), and the two
`theme-shell` outer sections (4308, 4315) drop `shadow-card`.
- `billing/BillingSummaryCard.tsx` — section card (133).
- `settings/SubmissionsPanel.tsx` — destructive error card (399);
swaps to `border border-destructive/24` to keep the warning tone.
Skipped (intentional):
- `auth/AuthPanel.tsx:3763, 4104` — Dialog popups with `shadow-xl`.
Floating modals; lift is correct for them.
- Workspace surfaces outside settings (PaneCard, AppShell sections,
dashboard panels, ChatPane, AppSurfacePane). Out of scope; their
lift is part of the workspace-pane visual language.
- `IntegrationsPane.tsx` standalone path — when mounted in settings
via `<IntegrationsPane embedded />` the embedded branch already
bypasses the card-shell wrapper, so settings already render
shadow-free for integrations. Standalone path stays as-is.
Tests:
- `auth/AuthPanel.test.mjs` brittle assertions updated:
- `assert.match(source, /shadow-md/)` → `assert.doesNotMatch(...)`
- empty-state CTA card pattern: `bg-card shadow-md` → `border
border-border bg-card`
- setup-loading section pattern: `rounded-[24px] ... shadow-card`
→ `rounded-3xl ...` (also picks up the rounded-3xl scale move
from #285).
- `npm run desktop:typecheck` clean
- `node --test` 57 pass / 1 pre-existing baseline fail
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two cohesive rounds of design-system work — both pure surface-level (no behavior or layout changes), both unblock the larger pane-level refactors that come next. Now spans 2 commits (#285's original + a follow-up), reviewable independently.
Round 1 — StatusDot primitive + design-token cleanup (commit 1)
StatusDotprimitive`desktop/src/components/ui/status-dot.tsx` — small colored dot indicating a state. Replaces ~14 hand-rolled `<span className="size-X rounded-full bg-X" />` instances.
cva-based variants matching real usage:
Sites swapped: AppSurfacePane, IntegrationsList, ConnectIntegrationsStep, PublishScreen, SpaceBrowserDisplayPane, SpaceApplicationsExplorerPane, SpaceBrowserExplorerPane (5 sites), OperationsDrawer, ChatPane, RuntimeStatusIndicator, AuthPanel, CreatingView. RuntimeStatusIndicator's visual spec refactored from a Tailwind class string (`dotClass: "animate-pulse bg-warning"`) to typed enum fields (`dotVariant` + `dotPulse`).
Token cleanup
Magic radii mapped to the standard scale: `[7px]`→md, `[10px]`→lg, `[12px]`→xl, `[14-22px]`→2xl, `[24-28px]`→3xl, `[4px]`→sm, `[9999px]`→full. The 18/20/28 → 2xl/3xl mappings shrink the affected dialog/sheet shells by 2-4px on a single visual element. Two intentional pixel-level decorations stay hand-tuned: tooltip arrow (`[2px]`) and the chat streaming-cursor block (`[1px]`).
Round 2 — EmptyState promoted to `ui/` + migrated (commit 2)
The dashboard's `EmptyState` was a great primitive locked in the wrong folder. Five other surfaces hand-rolled the same pattern (sidebar empties, automations placeholders, publish-wizard docs empties, marketplace gallery empties).
New API
```tsx
<EmptyState
icon={Icon} // optional LucideIcon
title="No items" // primary line (was `message`)
description="Sub line" // optional secondary (was `hint`)
action={<Button…/>} // optional CTA below
size="sm" | "md" // sm = compact dashboard look (default),
// md = chip-icon + text-sm + roomier padding
minHeight={number} // forces height (chart panels)
className="" // outer wrapper extras
/>
```
`sm` keeps the original dashboard visual. `md` wraps the icon in a muted chip and uses text-sm/text-xs for title/description — the look the sidebar empty states were rolling by hand.
Migrations
Deliberately not migrated
Verification
🤖 Generated with Claude Code