From 8dae50967234cd343f730ead9c6d9587babf26e5 Mon Sep 17 00:00:00 2001 From: Sergio Marcelino Date: Sat, 25 Jul 2026 07:10:06 -0300 Subject: [PATCH 1/4] feat(console): tabs and console redesign --- console/DESIGN.md | 1030 +++++++++++------ console/web/.storybook/manager.ts | 24 +- console/web/DESIGN.md | 1018 ++++++++++------ console/web/src/App.tsx | 422 +++---- .../src/components/chat/AttachmentChip.tsx | 2 +- console/web/src/components/chat/ChatDock.tsx | 148 --- console/web/src/components/chat/ChatView.tsx | 4 +- console/web/src/components/chat/Composer.tsx | 28 +- .../web/src/components/chat/ContextUsage.tsx | 2 +- .../src/components/chat/DirectoryPicker.tsx | 4 +- .../web/src/components/chat/EmptyState.tsx | 4 +- .../components/chat/FunctionTriggerGroup.tsx | 14 +- console/web/src/components/chat/Message.tsx | 2 +- .../web/src/components/chat/MessageList.tsx | 2 +- .../web/src/components/chat/ModePicker.tsx | 6 +- .../web/src/components/chat/ModelPicker.tsx | 2 +- .../src/components/chat/SessionTriggers.tsx | 2 +- .../chat/engine/RegisterTriggerView.tsx | 6 +- .../web/src/components/chat/fp/UtilView.tsx | 7 +- .../chat/fp/__tests__/parsers.test.ts | 2 +- .../chat/lexical/FileMentionNode.tsx | 6 +- .../src/components/chat/lexical/FlipMenu.tsx | 8 +- .../chat/lexical/FunctionMentionNode.tsx | 6 +- .../chat/worker/__tests__/parsers.test.ts | 26 +- .../src/components/chat/workflow/shared.tsx | 2 +- .../function-trigger/FunctionTriggerCard.tsx | 4 +- .../components/sidebar/ConversationRow.tsx | 10 +- .../sidebar/ConversationSidebar.tsx | 10 +- console/web/src/components/ui/Button.tsx | 16 +- console/web/src/components/ui/Cell.tsx | 2 +- console/web/src/components/ui/Dialog.tsx | 4 +- .../web/src/components/ui/DropdownMenu.tsx | 12 +- console/web/src/components/ui/Input.tsx | 6 +- console/web/src/components/ui/ModeToggle.tsx | 9 +- console/web/src/components/ui/Pagination.tsx | 2 +- console/web/src/components/ui/Select.tsx | 8 +- console/web/src/components/ui/Skeleton.tsx | 2 +- console/web/src/components/ui/StatusDot.tsx | 3 +- console/web/src/components/ui/StatusPanel.tsx | 18 +- console/web/src/components/ui/Tabs.tsx | 9 +- console/web/src/components/ui/Tooltip.tsx | 2 +- .../src/components/workspace/EmptyPane.tsx | 50 + .../web/src/components/workspace/TabStrip.tsx | 495 ++++++++ .../workspace/use-screen-options.ts | 48 + console/web/src/hooks/use-chat-dock.test.ts | 18 - console/web/src/hooks/use-chat-dock.ts | 181 --- console/web/src/hooks/use-workspace-tabs.ts | 252 ++++ console/web/src/index.css | 135 ++- console/web/src/lib/chat-activity.test.ts | 109 -- console/web/src/lib/chat-activity.ts | 43 - .../web/src/lib/function-trigger-copy.test.ts | 7 +- console/web/src/lib/iii-client.test.ts | 2 +- console/web/src/lib/markdown.tsx | 8 +- console/web/src/lib/monaco.ts | 39 +- .../schema-form => lib}/reorder.test.ts | 0 .../WorkersTab/schema-form => lib}/reorder.ts | 7 +- console/web/src/lib/workspace-tabs.test.ts | 125 ++ console/web/src/lib/workspace-tabs.ts | 202 ++++ .../tabs/WorkersTab/WorkersList.tsx | 6 +- .../WorkersTab/schema-form/ArrayField.tsx | 4 +- .../schema-form/DictionaryField.tsx | 4 +- .../WorkersTab/schema-form/NumberField.tsx | 2 +- console/web/src/pages/Ext/index.tsx | 11 +- .../src/pages/Memory/components/BankRail.tsx | 3 +- .../pages/Memory/components/MemoriesPanel.tsx | 15 +- .../pages/Memory/components/MemoryGraph.tsx | 5 +- .../pages/Memory/components/RecallPanel.tsx | 17 +- .../src/pages/Memory/hooks/useMemoryLive.ts | 14 +- console/web/src/pages/Memory/index.tsx | 14 +- .../TracesV2/components/AttributesFilter.tsx | 6 +- .../TracesV2/components/GroupByPicker.tsx | 14 +- .../TracesV2/components/GroupedTraceList.tsx | 8 +- .../TracesV2/components/IconToggleButton.tsx | 2 +- .../TracesV2/components/SpanBaggageTab.tsx | 4 +- .../TracesV2/components/SpanErrorsTab.tsx | 10 +- .../pages/TracesV2/components/SpanInfoTab.tsx | 12 +- .../TracesV2/components/SpanLinksTab.tsx | 4 +- .../pages/TracesV2/components/SpanLogsTab.tsx | 4 +- .../TracesV2/components/SpanOtelLogsTab.tsx | 6 +- .../pages/TracesV2/components/SpanPanel.tsx | 30 +- .../pages/TracesV2/components/SpanTagsTab.tsx | 12 +- .../components/TraceDetailSkeleton.tsx | 38 +- .../TracesV2/components/TraceFilters.tsx | 26 +- .../pages/TracesV2/components/TraceHeader.tsx | 24 +- .../TracesV2/components/TraceListRow.tsx | 8 +- .../TracesV2/components/ViewsDropdown.tsx | 8 +- .../TracesV2/components/WaterfallChart.tsx | 20 +- .../TracesV2/components/WorkerBreakdown.tsx | 2 +- .../components/timeline/SpanFilterMenu.tsx | 6 +- .../components/timeline/SpanHoverCard.tsx | 4 +- .../components/timeline/TimelineStrip.tsx | 10 +- .../components/timeline/TraceTimeline.tsx | 8 +- console/web/src/pages/TracesV2/index.tsx | 16 +- 93 files changed, 3161 insertions(+), 1831 deletions(-) delete mode 100644 console/web/src/components/chat/ChatDock.tsx create mode 100644 console/web/src/components/workspace/EmptyPane.tsx create mode 100644 console/web/src/components/workspace/TabStrip.tsx create mode 100644 console/web/src/components/workspace/use-screen-options.ts delete mode 100644 console/web/src/hooks/use-chat-dock.test.ts delete mode 100644 console/web/src/hooks/use-chat-dock.ts create mode 100644 console/web/src/hooks/use-workspace-tabs.ts delete mode 100644 console/web/src/lib/chat-activity.test.ts delete mode 100644 console/web/src/lib/chat-activity.ts rename console/web/src/{pages/Configuration/tabs/WorkersTab/schema-form => lib}/reorder.test.ts (100%) rename console/web/src/{pages/Configuration/tabs/WorkersTab/schema-form => lib}/reorder.ts (57%) create mode 100644 console/web/src/lib/workspace-tabs.test.ts create mode 100644 console/web/src/lib/workspace-tabs.ts diff --git a/console/DESIGN.md b/console/DESIGN.md index c82d0fd6e..c60b9b3de 100644 --- a/console/DESIGN.md +++ b/console/DESIGN.md @@ -1,118 +1,155 @@ --- -version: alpha +version: beta name: iii Schematic -description: A minimal, blueprint-style design system for the iii engine. Engineering-document aesthetic in warm cream and ink, with a single vivid orange accent and an all-monospace voice. +description: A minimal, engineering-document design system for the iii engine. The system draws no lines — hierarchy comes entirely from layered surfaces and alpha-gray fills; warm cream paper in light, neutral grays from black in dark, with a single rationed accent (burnt orange on cream, electric blue on dark), one 6px corner radius everywhere, and a mono voice for technical data. The only sanctioned strokes are the focus ring and the very subtle `edge` frame around the floating workspace panels. colors: bg: "#f2f0ed" - panel: "#e9e6e2" + sidebar: "#edeae5" + panel: "lab(98.26% 0 0)" + panel-raised: "#f7f5f2" paper: "#f2f0ed" paper-2: "#ebe8e3" + surface: "rgba(20, 16, 8, 0.055)" + surface-hover: "rgba(20, 16, 8, 0.085)" + surface-selected: "rgba(184, 66, 15, 0.12)" + surface-active: "rgba(20, 16, 8, 0.12)" ink: "#0a0a0a" ink-2: "#1a1a1a" ink-soft: "#1a1a1a" ink-faint: "#6b6865" ink-ghost: "#a3a09c" + ink-disabled: "#b8b4ae" mute: "#6b6865" mute-2: "#a3a09c" - rule: "#d8d5d0" - rule-2: "#e6e3df" - accent: "#ff5a1f" - accent-dark: "#3ea8ff" - bg-dark: "#111110" - panel-dark: "#1a1916" - ink-dark: "#f2f0ed" - ink-faint-dark: "#9c9893" - ink-ghost-dark: "#5d5a55" - rule-dark: "#2a2926" - rule-2-dark: "#1f1e1c" - alert: "#c43e1c" + rule: "transparent" + rule-2: "transparent" + rule-strong: "transparent" + rule-focus: "rgba(184, 66, 15, 0.6)" + edge: "rgba(20, 16, 8, 0.08)" + accent: "#b8420f" + accent-fg: "#f2f0ed" + accent-hover: "#a53a0c" + accent-muted: "rgba(184, 66, 15, 0.1)" + accent-border: "rgba(184, 66, 15, 0.35)" + alert: "#ff0026" + alert-muted: "rgba(255, 0, 38, 0.08)" warn: "#a87a00" + warn-muted: "rgba(168, 122, 0, 0.12)" + ok: "#356f3d" + ok-muted: "rgba(53, 111, 61, 0.12)" + bg-dark: "#0a0a0a" + sidebar-dark: "#0e0e0e" + panel-dark: "#111111" + panel-raised-dark: "#171717" + paper-2-dark: "#171717" + surface-dark: "rgba(255, 255, 255, 0.055)" + surface-hover-dark: "rgba(255, 255, 255, 0.085)" + surface-selected-dark: "rgba(40, 168, 247, 0.14)" + surface-active-dark: "rgba(255, 255, 255, 0.12)" + ink-dark: "#ededed" + ink-faint-dark: "#a6a6a6" + ink-ghost-dark: "#6f6f6f" + ink-disabled-dark: "#4d4d4d" + rule-dark: "transparent" + rule-2-dark: "transparent" + rule-strong-dark: "transparent" + rule-focus-dark: "rgba(40, 168, 247, 0.7)" + edge-dark: "rgba(255, 255, 255, 0.07)" + accent-dark: "#28a8f7" + accent-hover-dark: "#46b6fa" + accent-muted-dark: "rgba(40, 168, 247, 0.12)" + accent-border-dark: "rgba(40, 168, 247, 0.35)" + alert-dark: "#f05d68" + warn-dark: "#f5a524" + ok-dark: "#36c98f" typography: logo: - fontFamily: Chivo Mono + fontFamily: Geist Mono fontSize: 18px fontWeight: 600 lineHeight: 1 letterSpacing: -0.02em display-hero: - fontFamily: Chivo Mono + fontFamily: Geist fontSize: 72px fontWeight: 600 lineHeight: 1.02 letterSpacing: -0.02em display-foot: - fontFamily: Chivo Mono + fontFamily: Geist fontSize: 48px fontWeight: 600 lineHeight: 1.05 letterSpacing: -0.03em headline-section: - fontFamily: Chivo Mono + fontFamily: Geist fontSize: 28px fontWeight: 500 lineHeight: 1.25 letterSpacing: -0.01em headline-card: - fontFamily: Chivo Mono + fontFamily: Geist fontSize: 20px fontWeight: 500 lineHeight: 1.1 letterSpacing: -0.02em title-cell: - fontFamily: Chivo Mono + fontFamily: Geist Mono fontSize: 16px fontWeight: 600 lineHeight: 1.3 letterSpacing: -0.01em body-md: - fontFamily: Chivo Mono + fontFamily: Geist fontSize: 14px fontWeight: 400 lineHeight: 1.7 body-sm: - fontFamily: Chivo Mono + fontFamily: Geist fontSize: 13px fontWeight: 400 lineHeight: 1.7 code-md: - fontFamily: Chivo Mono + fontFamily: Geist Mono fontSize: 13px fontWeight: 400 lineHeight: 1.65 code-sm: - fontFamily: Chivo Mono + fontFamily: Geist Mono fontSize: 12.5px fontWeight: 400 lineHeight: 1.55 label-caps-lg: - fontFamily: Chivo Mono + fontFamily: Geist Mono fontSize: 12px fontWeight: 500 lineHeight: 1 letterSpacing: 0.18em label-caps-md: - fontFamily: Chivo Mono + fontFamily: Geist Mono fontSize: 12px fontWeight: 500 lineHeight: 1 letterSpacing: 0.14em label-caps-sm: - fontFamily: Chivo Mono + fontFamily: Geist Mono fontSize: 11px fontWeight: 500 lineHeight: 1 letterSpacing: 0.06em micro: - fontFamily: Chivo Mono + fontFamily: Geist Mono fontSize: 9px fontWeight: 400 lineHeight: 1 letterSpacing: 0.04em rounded: none: 0px - sm: 0px - md: 0px - lg: 0px + xs: 6px + sm: 6px + md: 6px + lg: 6px + xl: 6px full: 9999px spacing: base: 16px @@ -147,35 +184,35 @@ components: backgroundColor: "{colors.ink}" textColor: "{colors.bg}" typography: "{typography.body-sm}" - rounded: "{rounded.none}" + rounded: "{rounded.md}" padding: 12px 20px button-primary-hover: - backgroundColor: "{colors.bg}" - textColor: "{colors.ink}" + backgroundColor: "{colors.ink}" + opacity: 0.9 button-ghost: - backgroundColor: "{colors.bg}" - textColor: "{colors.ink}" + backgroundColor: transparent + textColor: "{colors.ink-faint}" typography: "{typography.body-sm}" - rounded: "{rounded.none}" + rounded: "{rounded.sm}" padding: 12px 20px button-ghost-hover: - backgroundColor: "{colors.ink}" - textColor: "{colors.bg}" + backgroundColor: "{colors.surface-hover}" + textColor: "{colors.ink}" button-pill: - backgroundColor: "{colors.bg}" + backgroundColor: "{colors.surface}" textColor: "{colors.ink}" typography: "{typography.body-sm}" - rounded: "{rounded.none}" + rounded: "{rounded.sm}" padding: 6px 12px button-pill-hover: - backgroundColor: "{colors.ink}" - textColor: "{colors.bg}" + backgroundColor: "{colors.surface-hover}" button-icon: - backgroundColor: "{colors.bg}" + backgroundColor: transparent textColor: "{colors.ink-faint}" - rounded: "{rounded.none}" + rounded: "{rounded.sm}" size: 30px button-icon-hover: + backgroundColor: "{colors.surface-hover}" textColor: "{colors.ink}" nav-link: typography: "{typography.body-sm}" @@ -184,68 +221,78 @@ components: nav-link-hover: textColor: "{colors.ink}" card: - backgroundColor: "{colors.bg}" - rounded: "{rounded.none}" - padding: 28px + backgroundColor: "{colors.surface}" + rounded: "{rounded.md}" + padding: 20px card-focus: - backgroundColor: "{colors.panel}" + backgroundColor: "{colors.panel-raised}" card-head: - backgroundColor: "{colors.panel}" + backgroundColor: "{colors.panel-raised}" typography: "{typography.label-caps-lg}" textColor: "{colors.ink-faint}" padding: 10px 14px input: - backgroundColor: "{colors.bg}" + backgroundColor: "{colors.surface}" textColor: "{colors.ink}" typography: "{typography.body-sm}" - rounded: "{rounded.none}" - padding: 10px 2px + rounded: "{rounded.sm}" + padding: 10px 12px input-placeholder: textColor: "{colors.ink-ghost}" + input-hover: + backgroundColor: "{colors.surface-hover}" input-focus: - backgroundColor: "{colors.bg}" - textColor: "{colors.ink}" + borderColor: "{colors.rule-focus}" + ring: "3px {colors.accent-muted}" badge-numeric: backgroundColor: "{colors.accent}" - textColor: "{colors.bg}" + textColor: "{colors.accent-fg}" typography: "{typography.label-caps-sm}" - rounded: "{rounded.none}" + rounded: "{rounded.xs}" padding: 0 4px height: 16px status-dot: backgroundColor: "{colors.accent}" rounded: "{rounded.full}" size: 6px - rule-line: - backgroundColor: "{colors.rule}" - height: 1px code-block: backgroundColor: "{colors.bg}" typography: "{typography.code-sm}" textColor: "{colors.ink}" + rounded: "{rounded.sm}" padding: 18px 20px terminal-button: - backgroundColor: "{colors.bg}" + backgroundColor: "{colors.surface}" textColor: "{colors.ink}" typography: "{typography.body-sm}" - rounded: "{rounded.none}" + rounded: "{rounded.sm}" padding: 10px 14px terminal-prompt: textColor: "{colors.accent}" + toggle-track: + backgroundColor: "{colors.surface}" + rounded: "{rounded.sm}" + padding: 2px toggle-active: - backgroundColor: "{colors.ink}" - textColor: "{colors.bg}" + backgroundColor: "{colors.accent-muted}" + textColor: "{colors.ink}" + rounded: "{rounded.xs}" toggle-inactive: - backgroundColor: "{colors.bg}" + backgroundColor: transparent textColor: "{colors.mute}" --- # iii Schematic — design system -This document is a self-contained, portable spec for the iii Schematic UI: a -warm-cream, ink-on-paper, monospace web UI built like an engineering drafting -sheet. Everything needed to reproduce the system in another project lives -inside this file — there are no links to repository sources. +This document is a self-contained, portable spec for the iii Schematic UI: +an engineering-document web UI built from layered surfaces and alpha-gray +fills — warm cream paper in light, neutral grays from black in dark — with +Geist for UI text, Geist Mono for technical data, one 6px corner radius +everywhere, and a single rationed accent. The system draws no lines: the +only sanctioned strokes are the focus ring and the subtle `edge` frame +around the floating workspace panels. Everything needed to reproduce the +system in another project lives inside this file — there are no links to +repository sources. The YAML frontmatter above is the machine-readable token spec. The sections below translate it into implementation-ready CSS and React. They assume: @@ -263,18 +310,24 @@ canonical components in §10 as-is. ## 0. Setup -### Font +### Fonts -Load Chivo Mono in `index.html`. Weights 400/500/600 cover the entire scale -(body, label-caps, headlines, display). +Two families, loaded via `@fontsource` at the top of the global stylesheet: +**Geist** (the UI sans — navigation, buttons, body copy, headings) and +**Geist Mono** (the technical voice — trace names, IDs, timestamps, metrics, +code). Chivo Mono stays in the mono fallback stack for legacy glyph parity. +Weights 400/500/600 cover the entire scale. -```html - - - +```css +@import "@fontsource/geist/400.css"; +@import "@fontsource/geist/500.css"; +@import "@fontsource/geist/600.css"; +@import "@fontsource/geist-mono/400.css"; +@import "@fontsource/geist-mono/500.css"; +@import "@fontsource/geist-mono/600.css"; +@import "@fontsource/chivo-mono/400.css"; +@import "@fontsource/chivo-mono/500.css"; +@import "@fontsource/chivo-mono/600.css"; ``` ### HTML shell @@ -299,37 +352,90 @@ The full design-token stylesheet — drop this in as your global CSS entrypoint: @import "tailwindcss"; @theme { - --font-sans: "Chivo Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, - Consolas, "Liberation Mono", "Courier New", monospace; - --font-mono: "Chivo Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, + --font-sans: + "Geist", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-mono: + "Geist Mono", "Chivo Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* paper / surface ramp (3-step) */ + /* ── Surface ramp ───────────────────────────────────────────────────── + Layered surfaces are the ONLY structural tool — the system draws no + lines: bg (canvas) → sidebar → panel → panel-raised, then the + component fills surface → surface-hover → surface-selected → + surface-active. The base layers are solid; the component fills are + alpha grays so one step reads identically over any base layer. + `paper-2` is the legacy alias for panel-raised. */ --color-bg: #f2f0ed; - --color-panel: #e9e6e2; + --color-sidebar: #edeae5; + --color-panel: lab(98.26% 0 0); + --color-panel-raised: #f7f5f2; --color-paper-2: #ebe8e3; + --color-surface: rgba(20, 16, 8, 0.055); + --color-surface-hover: rgba(20, 16, 8, 0.085); + --color-surface-selected: rgba(184, 66, 15, 0.12); + --color-surface-active: rgba(20, 16, 8, 0.12); - /* ink ramp (3-step) */ + /* ── Ink ramp ──────────────────────────────────────────────────────── */ --color-ink: #0a0a0a; --color-ink-faint: #6b6865; --color-ink-ghost: #a3a09c; - - /* structural lines */ - --color-rule: #d8d5d0; - --color-rule-2: #e6e3df; - - /* accent (single hero — hot orange) */ - --color-accent: #ff5a1f; + --color-ink-disabled: #b8b4ae; + + /* ── Border ramp ────────────────────────────────────────────────────── + The system draws no lines: rule/rule-2/rule-strong resolve to + transparent in both themes (legacy `border-rule*` utilities become + inert 1px transparent borders, so layout never shifts). Hierarchy is + carried by the surface fills above. The single exception is + rule-focus — the focus indicator on inputs and controls, which must + stay visible for accessibility. */ + --color-rule: transparent; + --color-rule-2: transparent; + --color-rule-strong: transparent; + --color-rule-focus: rgba(184, 66, 15, 0.6); + + /* The one structural stroke the system keeps: a VERY subtle edge on the + main workspace panels (the tab columns), so the floating panels + read against the canvas. Never used inside a panel. */ + --color-edge: rgba(20, 16, 8, 0.08); + + /* accent (single hero — burnt orange on cream, blue on dark) */ + --color-accent: #b8420f; --color-accent-fg: #f2f0ed; + --color-accent-hover: #a53a0c; + --color-accent-muted: rgba(184, 66, 15, 0.1); + --color-accent-border: rgba(184, 66, 15, 0.35); - /* status */ - --color-alert: #c43e1c; + /* status (each with a muted fill for tinted backgrounds) */ + --color-alert: #ff0026; + --color-alert-muted: rgba(255, 0, 38, 0.08); --color-warn: #a87a00; - - /* radii — only two are allowed */ + --color-warn-muted: rgba(168, 122, 0, 0.12); + --color-ok: #356f3d; + --color-ok-muted: rgba(53, 111, 61, 0.12); + + /* ── Radii ──────────────────────────────────────────────────────────── + One radius everywhere: every step of the Tailwind scale resolves to + 6px, so badges, buttons, cards, panels, and modals share the same + corner. Only `none` (main columns' outer frame edge cases) and `full` + (dots, round action buttons) differ. */ --radius-none: 0px; + --radius-xs: 6px; + --radius-sm: 6px; + --radius-md: 6px; + --radius-lg: 6px; + --radius-xl: 6px; --radius-full: 9999px; + /* ── Elevation (the only two sanctioned shadows) ────────────────────── */ + --shadow-raised: + 0 1px 0 rgba(255, 255, 255, 0.025) inset, 0 8px 24px rgba(0, 0, 0, 0.18); + --shadow-floating: + 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 12px 32px rgba(0, 0, 0, 0.28); + + /* ── Motion ─────────────────────────────────────────────────────────── */ + --ease-glide: cubic-bezier(0.2, 0.8, 0.2, 1); + /* spacing scale (carried from the YAML) */ --spacing-gutter: 24px; --spacing-section-x: 36px; @@ -338,18 +444,41 @@ The full design-token stylesheet — drop this in as your global CSS entrypoint: --spacing-content-max: 1216px; } -/* dark theme: invert paper/ink and swap accent to electric blue */ [data-theme="dark"] { - --color-bg: #111110; - --color-panel: #1a1916; - --color-paper-2: #1f1e1c; - --color-ink: #f2f0ed; - --color-ink-faint: #9c9893; - --color-ink-ghost: #5d5a55; - --color-rule: #2a2926; - --color-rule-2: #1f1e1c; - --color-accent: #3ea8ff; - --color-accent-fg: #111110; + /* Neutral grays derived from black — no blue cast in the base ramp. The + component fills (surface*) are white-alpha so a step reads identically + over any base layer; the ONLY chromatic surface is surface-selected, + the blue selection tint. Borders are gone: rule/rule-2/rule-strong + resolve to transparent, and hierarchy is carried entirely by fills. */ + --color-bg: #0a0a0a; + --color-sidebar: #0e0e0e; + --color-panel: #111111; + --color-panel-raised: #171717; + --color-paper-2: #171717; + --color-surface: rgba(255, 255, 255, 0.055); + --color-surface-hover: rgba(255, 255, 255, 0.085); + --color-surface-selected: rgba(40, 168, 247, 0.14); + --color-surface-active: rgba(255, 255, 255, 0.12); + --color-ink: #ededed; + --color-ink-faint: #a6a6a6; + --color-ink-ghost: #6f6f6f; + --color-ink-disabled: #4d4d4d; + --color-rule: transparent; + --color-rule-2: transparent; + --color-rule-strong: transparent; + --color-rule-focus: rgba(40, 168, 247, 0.7); + --color-edge: rgba(255, 255, 255, 0.07); + --color-accent: #28a8f7; + --color-accent-fg: #070909; + --color-accent-hover: #46b6fa; + --color-accent-muted: rgba(40, 168, 247, 0.12); + --color-accent-border: rgba(40, 168, 247, 0.35); + --color-alert: #f05d68; + --color-alert-muted: rgba(240, 93, 104, 0.12); + --color-warn: #f5a524; + --color-warn-muted: rgba(245, 165, 36, 0.12); + --color-ok: #36c98f; + --color-ok-muted: rgba(54, 201, 143, 0.12); } @layer base { @@ -388,11 +517,13 @@ The full design-token stylesheet — drop this in as your global CSS entrypoint: ::-webkit-scrollbar-track { background: transparent; } + /* rule is transparent (no-lines system) — the thumb needs its own + visible alpha gray. */ ::-webkit-scrollbar-thumb { - background: var(--color-rule); + background: color-mix(in oklab, var(--color-ink) 22%, transparent); } ::-webkit-scrollbar-thumb:hover { - background: var(--color-ink-ghost); + background: color-mix(in oklab, var(--color-ink) 38%, transparent); } } @@ -428,12 +559,27 @@ The full design-token stylesheet — drop this in as your global CSS entrypoint: animation: wiggle 3s ease-in-out infinite; } -/* the one sanctioned shadow stack — used only for the "deal" stack - animation on language cards */ +/* transient shadow stack — used only for the "deal" stack animation on + language cards (default elevation goes through shadow-raised / + shadow-floating from the theme) */ @utility deal-shadow { box-shadow: -2px 0 0 var(--color-rule), -16px 4px 36px -10px rgba(0, 0, 0, 0.22); } + +/* Function/tool-call card chrome — an accent-tinted raised surface (a soft + gradient wash over panel-raised, no border) so calls read as special + without any outline. Derived from the accent token, so it tints blue in + dark mode and burnt orange in light mode. */ +@utility fcall-chrome { + background: + linear-gradient( + 180deg, + color-mix(in oklab, var(--color-accent) 7%, transparent), + color-mix(in oklab, var(--color-accent) 3%, transparent) + ), + var(--color-panel-raised); +} ``` ### `cn` helper @@ -454,19 +600,32 @@ export function cn(...inputs: ClassValue[]) { ## 1. Philosophy — "iii Schematic" -The app should feel like an **engineering document**, not a SaaS dashboard. - -- The page is a **drafting sheet**: cream paper, hairline ink rules, - monospace voice, an unflinching commitment to lowercase. Nothing is - rounded; everything sits on a 1px grid. -- Color is rationed: the palette is essentially **black-on-cream**, broken - only by a single hot orange used for state, focus, and emphasis. +The app should feel like an **engineering document**, not a SaaS dashboard — +technical, precise, dense without feeling cramped, structured without +looking boxed. + +- The page is built from **layered surfaces**: hierarchy comes from a + one-step background difference, full stop. The system draws **no lines** + — no outlines on controls, no dividers between rows or regions. Exactly + two strokes are sanctioned: the focus indicator (`rule-focus` + border/ring) on a focused control, and the very subtle `edge` frame + around the floating workspace panels. +- Dark mode is a **first-class layered system** — neutral grays derived + from black (`#0a0a0a → #171717`, no blue cast) with white-alpha component + fills, not an inverted paper ramp. +- One corner radius: **6px everywhere** (every Tailwind radius step + resolves to it); the shapes stay disciplined, not soft or + consumer-playful. Lowercase voice throughout. +- Color is rationed: the palette is essentially **ink-on-surface**, broken + by a single accent (burnt orange on cream, electric blue on dark) reserved + for selected, focused, active, and live states. - The personality is **technical but unintimidating** — the same energy as a well-kept lab notebook or a hand-drawn architecture diagram. It must feel built by engineers, for engineers, and for the agents working alongside them. - Density is deliberate: spec sheets, code, traces, and console panels - coexist on the same surface without a hierarchy contest. + coexist on the same surface without a hierarchy contest. Trace + visualizations stay the most colorful area of the UI. ### Voice @@ -478,21 +637,27 @@ The app should feel like an **engineering document**, not a SaaS dashboard. - The wordmark is pronounced *"three eye"* — every "i" stays lowercase. > If you removed all the type, the page should still read as a structured -> document. Lines establish hierarchy before color does. +> document. Surfaces establish hierarchy; color marks state. Lines never +> appear. --- -## 2. Typography — single typeface (Chivo Mono) +## 2. Typography — two families (Geist + Geist Mono) -Chivo Mono is wired to **both** `--font-sans` and `--font-mono` in the theme -(see §0), so every default text node is monospaced. There is no secondary -face in this design language. +**Geist** (`--font-sans`) carries the UI: navigation, conversation titles, +buttons, inputs, chat content, empty states, headings, labels. **Geist +Mono** (`--font-mono`) carries everything technical: trace names, worker +names, function names, IDs, timestamps, metrics, span labels, filter +expressions, code-like values. -**Rule:** every UI surface uses Chivo Mono (the default). Don't reach for a -sans-serif or a second monospace stack — variety comes from weight, scale, -case, and letter-spacing, not family. +**Rule:** if a human wrote it, it's sans; if the machine produced it (or a +machine will parse it), it's mono. Don't add a third family — variety comes +from weight, scale, case, and letter-spacing, not more fonts. Chat and +configuration surfaces remap incidental `.font-mono` chrome back to Geist +(see the `.chat-surface` / `.configuration-surface` rules in `index.css`); +function-trigger cards run Geist Mono throughout. -Decorative ligatures are explicitly disabled in `@layer base` +Decorative ligatures are explicitly disabled on mono surfaces (`liga 0, clig 0, calt 0, dlig 0`) to preserve the schematic feel. ### Size scale @@ -527,56 +692,85 @@ Decorative ligatures are explicitly disabled in `@layer base` ## 3. Color tokens All tokens live in the `@theme` block in §0. Use the Tailwind utility -(`bg-bg`, `text-ink-faint`, `text-accent`, `border-rule`, …) — **never** the +(`bg-bg`, `text-ink-faint`, `text-accent`, `bg-surface`, …) — **never** the raw CSS variable. -### Paper / surface (3-step neutral scale) - -| Token | Use | -| --------- | ---------------------------------------------------- | -| `bg` | Page (warm cream paper) — every default surface | -| `panel` | Header strips, focused cards, code-block chrome | -| `paper-2` | Nested separation when `panel` would be too heavy | - -### Ink (3-step contrast) - -| Token | Use | -| ----------- | -------------------------------------------------- | -| `ink` | Primary type, wordmark, primary buttons, hairlines | -| `ink-faint` | Body in muted contexts, captions, inactive nav | -| `ink-ghost` | Line numbers, placeholders, timestamps | - -### Rules (the structural lines) - -| Token | Use | -| -------- | ------------------------------------------------------------ | -| `rule` | Default 1px borders — defines every container | -| `rule-2` | Nested separators (e.g. card head → body) when `rule` is too loud | - -### Accent — hot orange (single hero) - -`accent`, `accent-fg`. Reserved for: the active state, the live pulse, the -keyword/return value in a code block, the `iii` highlight in a sentence, the -focus underline on the email row, the orange `$` prompt. **Never** for body -text or large fills. +### Surface ramp (the depth axis) + +The base layers (`bg` → `panel-raised`) are solid neutral tones; the +component fills (`surface*`) are **alpha grays**, so one step reads +identically over any base layer. `surface-selected` is the only chromatic +surface — the accent-tinted selection fill (blue in dark, burnt orange in +light). + +| Token | Use | +| ------------------ | ---------------------------------------------------------- | +| `bg` | Application canvas — the deepest layer | +| `sidebar` | Left navigation/sidebar (one step off the canvas) | +| `panel` | Main chat and traces columns | +| `panel-raised` | Composer, trace details, popovers, active tool cards | +| `paper-2` | Legacy alias for `panel-raised` (kept for existing code) | +| `surface` | Inputs, controls, pills, chips, secondary cards | +| `surface-hover` | Hover state on rows, items, and ghost controls | +| `surface-selected` | Selected conversation, trace row, or list item (accent tint) | +| `surface-active` | Strong active/pressed state | + +### Ink (4-step contrast) + +| Token | Use | +| -------------- | -------------------------------------------------- | +| `ink` | Primary type, wordmark, primary buttons | +| `ink-faint` | Body in muted contexts, captions, inactive nav | +| `ink-ghost` | Line numbers, placeholders, timestamps | +| `ink-disabled` | Disabled labels (paired with reduced opacity) | + +### Rules (there are none) + +`rule`, `rule-2`, and `rule-strong` resolve to `transparent` in both +themes. They exist only so legacy `border-rule*` / `divide-rule*` +utilities stay inert (1px transparent — layout never shifts) instead of +breaking. Never design with them. + +| Token | Value | Meaning | +| ------------- | ------------ | --------------------------------------------- | +| `rule` | transparent | Legacy default border — draws nothing | +| `rule-2` | transparent | Legacy subtle divider — draws nothing | +| `rule-strong` | transparent | Legacy emphasis border — draws nothing | +| `rule-focus` | accent ~60–70% alpha | The interactive stroke: the focus indicator on inputs and controls | +| `edge` | ink ~7–8% alpha | The structural stroke: the VERY subtle frame around the floating workspace panels (the tab columns) — never used inside a panel | + +### Accent (single hero — burnt orange on cream, electric blue on dark) + +`accent`, `accent-fg`, `accent-hover`, `accent-muted` (10–12%-alpha fill), +`accent-border` (35%-alpha, legacy — prefer `accent-muted` fills). Reserved +for: selected navigation and segmented controls (`accent-muted` fill), +focused inputs (`rule-focus`), active filters, live state, the selected +conversation/trace (`surface-selected` fill), the primary technical action, +the `$` prompt. **Never** for body text, large fills, or outlines. ### Status -`alert`, `warn` — desaturated on purpose. Status is expressed through **text -color + a small icon/stripe**, not full-color backgrounds. +`alert`, `warn`, `ok` — each with a `-muted` 8–12%-alpha fill for tinted +backgrounds. Status is expressed through **text color + a small icon/dot on +a muted tinted fill**, never a full-saturation background and never a +stripe or outline. | Token | Use | | -------- | ----------------------------------------- | -| `accent` | live / running / success | +| `accent` | live / running / focused / selected | +| `ok` | success, completed calls, diff additions | | `alert` | error states (traces, status panels) | -| `warn` | warning states | +| `warn` | warning states, pending approval | ### Dark theme -Override the same tokens inside a `[data-theme="dark"]` block (see §0). The -ramp inverts (`#111110 → #1a1916 → #1f1e1c` for paper; `#f2f0ed → #9c9893 → -#5d5a55` for ink) and the accent swaps to electric blue (`#3ea8ff`). Same -structural logic — never collapses into pure black. +Override the same tokens inside a `[data-theme="dark"]` block (see §0). Dark +is a neutral gray ramp derived from black (`#0a0a0a → #0e0e0e → #111111 → +#171717` — no blue cast), white-alpha component fills +(`rgba(255,255,255,0.055 → 0.12)`), neutral light ink (`#ededed → #a6a6a6 → +#6f6f6f`), and the accent swapped to electric blue (`#28a8f7`) — which also +tints `surface-selected`. Same structural logic; the blue lives only in +state, never in the grays. To follow the OS, set the attribute on load: @@ -587,102 +781,141 @@ document.documentElement.dataset.theme = isDark ? 'dark' : 'light' --- -## 4. The "lines define structure" rule (key composition pattern) +## 4. The "surfaces, not borders" rule (key composition pattern) + +> **A one-step surface difference defines a region. The system draws no +> lines — exactly two strokes are sanctioned: the focus ring, and the +> `edge` frame around the floating workspace panels.** + +Structure comes from the layered surface ramp (§3): a new region means a new +background step, never an outline and never a divider. Controls are +alpha-gray fills; rows separate by their hover/selected fills; regions +separate by base-layer steps; overlays separate by `panel-raised` + +`shadow-floating`. + +Two strokes are allowed, each with one job: -> **Borders define every container. Add a border before reaching for a -> background fill.** +1. **Focus** — a focused field swaps its (transparent) border to + `rule-focus` and gains a soft 3px accent ring; keyboard focus on + buttons uses the same `ring-rule-focus`. +2. **Panel edges** — the main workspace panels (each + workspace-tab column) float on the canvas as + `rounded-sm border border-edge bg-panel` with 6px gutters; the `edge` + stroke is a VERY subtle ink-alpha frame that keeps a panel readable + against the canvas. It is never used inside a panel — interior + hierarchy stays fill-only. -This is the inverse of a surface-stacking system: the schematic is held -together by 1px ink rules. A new line means a new container. Cards, cells, -panels, code blocks, and inputs are all defined by `border border-rule` — not -by background steps. +Nothing else in the chrome may draw a line. (Data visualizations are +exempt: charts may draw connector and grid lines with explicit alpha-ink +fills, e.g. `bg-ink/15` elbows and `bg-ink/8` time-grid guides in the +trace timeline.) ### Pattern ```tsx -
-
+
+
title
-
{/* body */}
+
{/* body */}
``` -The single tonal step (`bg → panel`) on the header strip is the only "lift" -allowed in default states (see §5). +The header strip is one surface step off the body — that difference IS the +separator. No divider, no outer outline. -### Allowed accents on top of borders +### Selection and severity -These are not extra borders, they are accents on the existing 1px grid: +- Selected row, card, conversation, or trace: `bg-surface-selected` — the + accent-tinted fill. **No left rail, no outline.** +- Active segment / tab / filter: `bg-accent-muted` fill. +- Row severity: the status's `-muted` tinted fill (e.g. `bg-alert-muted`) + plus the status text color and dot — no stripe. +- Legacy `divide-y divide-rule-2` / `border-b border-rule-2` classes are + inert (transparent) — don't add new ones. -- `border-l-2 border-accent` rail on a focused `WorkerCard` (see §10). -- `divide-x divide-rule` for inline grids (e.g. a metrics strip). -- The "feature grid" trick: draw 1px rules on the **top + left** of the grid - and the **right + bottom** of each cell. This produces a single, - perfectly-aligned grid of borders with no doubled lines. +### Shadows -### The single-shadow exception +Two sanctioned elevation shadows live in the theme: `shadow-raised` (raised +cards — composer, trace detail) and `shadow-floating` (popovers, dialogs, +dropdowns, tooltips). `.deal-shadow` remains a transient animation cue on +the language-card stack. No heavy glows — the only glow is the live +`pulse-dot`. -The system carries no default shadows. The **one** allowed transient shadow -is `.deal-shadow` (defined in §0), used only on the language-card stack -when a card slides on top of another — a deliberate tactile cue, not a -default elevation. - -**When in doubt:** add a 1px `border-rule`. Don't reach for a background fill -or a shadow. +**When in doubt:** step the background one level. Never reach for a border. --- ## 5. Surfaces & elevation -The 3-step ramp is the only tonal tool. Think of it as a depth axis with one -step of contrast — borders do the rest. +The surface ramp is the primary tonal tool. Think of it as a depth axis: +every step up reads as "closer" without a single stroke. ``` -bg page (cream paper) - └─ panel header strips, focused cards, code-block chrome - └─ paper-2 nested separation when needed +bg application canvas + └─ sidebar left navigation + └─ panel main chat / traces columns + └─ panel-raised composer, trace detail, popovers, tool cards + └─ surface inputs, controls, pills, secondary cards + └─ surface-hover hover + └─ surface-selected selected row / trace / conversation + └─ surface-active pressed / segmented-control selection ``` -Most cards/panels should accept a `focused` (or `variant`) prop that lifts -the body from `bg` to `panel`. That single tonal step is the only "lift" -allowed in default states. +Interactive containers walk the state sub-ramp (`surface → +surface-hover → surface-selected → surface-active`); nothing about their +edges ever changes except the focus ring. ### Where elevation actually shows up -1. **Tonal layering** — the 3-step ramp above. -2. **Hairline rules** — 1px lines in `rule` and `rule-2` do the work that - shadows would do in other systems. -3. **Reserved transient shadow** — the `.deal-shadow` utility, used only on - the language-card stack (`HelloCard` flow mode in §10). +1. **Tonal layering** — the ramp above; a one-step difference is the only + separator between regions. The alpha-gray component fills compose over + any base layer. +2. **Sanctioned shadows** — `shadow-raised` on raised in-flow cards + (composer, expanded trace detail) and `shadow-floating` on overlays + (dropdowns, popovers, dialogs, tooltips, hover cards). Both carry a 1px + white inset highlight so raised surfaces catch light in dark mode. +3. **Accent-tinted chrome** — the `.fcall-chrome` utility (§0): a soft + accent gradient wash over `panel-raised` (no border), reserved for + function/tool-call cards so agent actions read as special. 4. **Pulse + glow** — the live state uses `.pulse-dot` (1.6s expanding `box-shadow` ring) on a 6px accent dot. This is the only "glow" the system allows. -Dark mode keeps the same rule-driven hierarchy — the ramp flips but never -collapses into pure black. +Dark mode keeps the same surface-driven hierarchy — neutral grays stepping +up from black. The only visible edge anywhere is the workspace panels' +`edge` frame (§4). --- ## 6. Radii & shape -Rectilinear sharpness, period. The system is built from squares and 1px -strokes; the only curves are functional ones (status dots, glyph circles). +**One radius: 6px, globally.** Every step of the Tailwind scale +(`rounded-xs` through `rounded-xl`) resolves to 6px, so badges, chips, +buttons, inputs, sidebar rows, cards, tool/function-call cards, popovers, +dropdowns, dialogs, the composer, and the floating workspace panels all +share the same corner. There is no per-component scale to choose from — +write whichever step reads naturally (`rounded-sm` is the conventional +spelling) and it renders 6px. -| Token | Value | Used for | -| -------------- | ------- | ----------------------------------------------- | -| `rounded-none` | 0px | **Default for everything** (containers, buttons, cards, inputs, code blocks, badges, panels) | -| `rounded-full` | 9999px | Status dots and glyph circles **only** | +| Token | Value | Used for | +| --------------------- | ------ | ---------------------------------------- | +| `rounded-none` | 0px | Full-bleed edge cases only | +| `rounded-xs` … `-xl` | 6px | Everything — one corner everywhere | +| `rounded-full` | 9999px | Status dots, pills, round action buttons | -Never reach for `rounded-sm`, `rounded-md`, `rounded-lg`, or anything in -between. There is no "soft" variant — the YAML aliases `sm/md/lg` to `0px` -on purpose. +Don't invent in-between values, and don't reach for `rounded-full` on +anything that isn't genuinely circular. ### Stroke weight -- 1px is the default for all UI borders. -- SVG diagrams use 1–1.25px strokes; thicker strokes (1.25px `accent`) are - reserved for emphasized worker connections. +- UI chrome draws no strokes, with two exceptions: the focus indicator — a + 1px `rule-focus` border (inputs) or a 2px `ring-rule-focus` (buttons) — + and the 1px `edge` frame around the floating workspace panels (§4). +- SVG **data** diagrams use 1–1.25px strokes; thicker strokes (1.25px + `accent`) are reserved for emphasized worker connections. Charts draw + their connectors/grids with explicit alpha-ink fills (`bg-ink/15`, + `bg-ink/8`). - The wordmark is six rectangles (three "i"s, each a stem + a tittle), all sharing the same square unit. The mark is the design system in miniature: identical units, no curves, deliberate negative space. @@ -691,24 +924,37 @@ on purpose. ## 7. Spacing & layout -The page is a vertical sheet, max 1200px, sitting on cream paper with a 1px -ink-rule outer border. There is no hero card, no rounded container — the -entire site reads as one continuous spec sheet. +### Console workspace + +The console shell is a set of **floating panels over the canvas**: the +header holds server-persisted closable tabs (stored in the `console` +configuration entry under `workspace.tabs` — model in +`lib/workspace-tabs.ts`), and each tab shows one or two screens (any page +or the chat view) as equal columns; an unattached column renders an +attach affordance instead of a page. Each column renders as `rounded-sm border border-edge bg-panel` with 6px +gutters between panels and against the viewport, so the canvas shows +through as the seam. That gutter + edge frame is the entire column +chrome; panels draw no other lines. + +### Sheet pages + +The page is a vertical sheet, max 1200px, sitting on the canvas surface. +There is no hero card — the entire site reads as one continuous spec sheet. -- **Sheet:** `min(1200px, 100%)` centered, 1px outer rule (see `Sheet` in - §10). +- **Sheet:** `min(1200px, 100%)` centered (see `Sheet` in §10). The sheet + itself is unbordered; the surface step against the canvas defines it. - **Sticky nav:** `py-4.5` vertical padding, collapses to `py-2.5` on - scroll. Bordered bottom only. + scroll. No border — it reads as chrome by sitting on its own surface. - **Section padding:** `px-9` on desktop (36px), `px-4.5` on tablet (18px), `px-3.5` on small mobile (14px). Vertical: `py-20` to `py-24` (80–96px) between major sections. - **Hero grid:** Two equal columns (`1fr 1fr`) with a 64px gap on desktop; collapses to a single column under 880px. -- **Feature grids:** Always **3-up** on desktop (`grid-cols-3`), divided by - 1px rules drawn on the **top + left** of the grid and the **right + - bottom** of each cell. -- **Card padding:** 18–28px internal. Cards never carry shadows in their - default state. +- **Feature grids:** Always **3-up** on desktop (`grid-cols-3`), each cell + a `rounded-md bg-surface` card separated by grid `gap` — never by drawn + rules. +- **Card padding:** 12px compact, 20–24px default. In-flow cards carry no + shadow unless raised (`shadow-raised`); overlays use `shadow-floating`. - **Density rhythm:** 4/8 micro-scale for inline gaps (icon-to-text, dot-to-label), 12/14/16 for component padding, 24/28 for card padding, 36 for section gutters, 64–96 for section breathing room. @@ -739,23 +985,26 @@ Reach for these to keep the engineering-document feel consistent: ## 9. Status & semantics -`StatusPanel` (§10) is the canonical status display — bordered, monospaced, -icon + headline + detail. The body fill stays `bg` in every variant; only -the chrome (border + icon + headline) changes. +`StatusPanel` (§10) is the canonical status display — a rounded row on a +muted tinted fill, monospaced, icon + headline + detail. No stripe, no +outline in any variant; the tint + icon + headline color carry the +severity. -| Variant | Border + icon token | Body fill | -| ----------- | ------------------- | --------- | -| `v-info` | `rule` (ink icon) | `bg` | -| `v-success` | `accent` | `bg` | -| `v-warn` | `warn` | `bg` | -| `v-alert` | `alert` | `bg` | +| Variant | Icon + headline | Body fill | +| ----------- | --------------- | ------------- | +| `v-info` | `ink` | `surface` | +| `v-success` | `ok` | `ok-muted` | +| `v-warn` | `warn` | `warn-muted` | +| `v-alert` | `alert` | `alert-muted` | -**In tables and traces:** express row severity with a left-edge `border-l-2` -stripe plus a faint tinted background (e.g. `bg-alert/5`). Never with a -full ring or a solid status background. See `Trace` (§10). +**In tables and traces:** the same recipe — the status's `-muted` tinted +background plus its text color and dot. Never a stripe, ring, or solid +status background. See `Trace` (§10). -For inline "live" emphasis, pair a `StatusDot` with the `pulse-dot` -utility — the only sanctioned glow. +**Dot semantics:** green (`ok`) means completed; blue (`accent`) is reserved +for live/running (animated) and selected states; `warn` for pending; `alert` +for failed. For inline "live" emphasis, pair a `StatusDot` with the +`pulse-dot` utility — the only sanctioned glow. --- @@ -820,20 +1069,22 @@ export function Caret({ className }: CaretProps) { ### `StatusDot` -6px circle. The only place `rounded-full` is allowed besides glyphs. -Optional `.pulse-dot` glow for "live" emphasis. +6px circle. Optional `.pulse-dot` glow for "live" emphasis. Tone semantics +per §9: `ok` = completed, `accent` = live/selected, `warn` = pending, +`alert` = failed. ```tsx import * as React from 'react' import { cn } from '@/lib/utils' -type DotTone = 'accent' | 'alert' | 'warn' | 'ink' +type DotTone = 'accent' | 'alert' | 'warn' | 'ink' | 'ok' const dotTone: Record = { accent: 'bg-accent', alert: 'bg-alert', warn: 'bg-warn', ink: 'bg-ink', + ok: 'bg-ok', } interface StatusDotProps extends React.HTMLAttributes { @@ -864,12 +1115,14 @@ export function StatusDot({ ### `Button` -Variants: `primary` (ink fill, hover inverts to outlined `bg`), `ghost` -(transparent → solid ink on hover), `pill` (compact nav button), `icon` -(30×30 rule-bordered), `terminal` (full-width row with `$` prompt + command -+ optional copy), `wiggle` (primary + corner badge slot + `.wiggle`). Sizes: -`sm` `md` `lg` `icon`. Depends on `class-variance-authority` and -`@radix-ui/react-slot`. +Variants: `primary` (ink fill, 6px radius — reads as a light chip in dark +mode), `ghost` (borderless, faint text → `surface-hover` fill on hover), +`pill` (compact borderless `surface` chip), `icon` (30×30 borderless, +`surface-hover` on hover), `terminal` (full-width borderless `surface` row +with `$` prompt + command + optional copy), `wiggle` (primary + corner badge +slot + `.wiggle`). Sizes: `sm` `md` `lg` `icon`. Focus is a 2px +`rule-focus` ring — the only stroke a button ever shows. Depends on +`class-variance-authority` and `@radix-ui/react-slot`. ```tsx import * as React from 'react' @@ -878,22 +1131,22 @@ import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '@/lib/utils' const buttonVariants = cva( - 'inline-flex items-center justify-center gap-x-2 whitespace-nowrap font-mono lowercase rounded-none transition-[background-color,color,border-color] duration-150 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent disabled:pointer-events-none disabled:opacity-40 select-none', + 'inline-flex items-center justify-center gap-x-2 whitespace-nowrap font-mono lowercase rounded-sm transition-[background-color,color,border-color] duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rule-focus disabled:pointer-events-none disabled:opacity-40 select-none', { variants: { variant: { primary: - 'bg-ink text-bg border border-ink hover:bg-bg hover:text-ink', + 'bg-ink text-bg border border-transparent hover:bg-ink/90 rounded-md', ghost: - 'bg-transparent text-ink border border-transparent hover:bg-ink hover:text-bg', + 'bg-transparent text-ink-faint border border-transparent hover:bg-surface-hover hover:text-ink', pill: - 'bg-bg text-ink border border-ink hover:bg-ink hover:text-bg', + 'bg-surface text-ink border border-transparent hover:bg-surface-hover', icon: - 'bg-bg text-ink-faint border border-rule hover:text-ink', + 'bg-transparent text-ink-faint border border-transparent hover:bg-surface-hover hover:text-ink', terminal: - 'bg-bg text-ink border border-rule justify-start', + 'bg-surface text-ink border border-transparent justify-start hover:bg-surface-hover', wiggle: - 'wiggle bg-ink text-bg border border-ink hover:bg-bg hover:text-ink relative', + 'wiggle bg-ink text-bg border border-transparent hover:bg-ink/90 rounded-md relative', }, size: { sm: 'h-8 px-3 text-[13px]', @@ -937,8 +1190,8 @@ export { buttonVariants } ### `StatusPanel` A row component for system messages: 18px icon slot, 13px Semi-Bold -headline, 12px ink-faint detail. Variants tint the **border + icon + -headline only** — body fill stays `bg`. +headline, 12px ink-faint detail. Variants are a **muted tinted fill** — +no stripe, no outline (§9). ```tsx import * as React from 'react' @@ -948,25 +1201,25 @@ export type StatusVariant = 'info' | 'success' | 'warn' | 'alert' const variantTone: Record< StatusVariant, - { border: string; icon: string; headline: string } + { fill: string; icon: string; headline: string } > = { info: { - border: 'border-rule', + fill: 'bg-surface', icon: 'text-ink', headline: 'text-ink', }, success: { - border: 'border-accent', - icon: 'text-accent', - headline: 'text-accent', + fill: 'bg-ok-muted', + icon: 'text-ok', + headline: 'text-ok', }, warn: { - border: 'border-warn', + fill: 'bg-warn-muted', icon: 'text-warn', headline: 'text-warn', }, alert: { - border: 'border-alert', + fill: 'bg-alert-muted', icon: 'text-alert', headline: 'text-alert', }, @@ -991,8 +1244,8 @@ export function StatusPanel({ return (
@@ -1026,10 +1279,11 @@ export function StatusPanel({ ### `CodeBlock` -`bg` fill, 1px `rule` border, monospace at 12.5px / line-height 1.55. Light -syntax tinting: comments italic-ghost, strings in `accent`, keywords -bold-ink, numbers in `alert`. The accent orange is reserved for **string -literals and the active call return**, not arbitrary keywords. +A `bg` well — the canvas tone punching through the surface it sits on, no +border — monospace at 12.5px / line-height 1.55. Light syntax tinting: +comments italic-ghost, strings in `accent`, keywords bold-ink, numbers in +`alert`. The accent orange is reserved for **string literals and the active +call return**, not arbitrary keywords. ```tsx import * as React from 'react' @@ -1043,7 +1297,7 @@ export function CodeBlock({ className, children, ...props }: CodeBlockProps) { return (
+    
{title ? ( -
+
{title}
) : null} @@ -1108,8 +1363,9 @@ export function TerminalRow({ ### `Trace` Header strip + a list of trace rows with a `StatusDot`, op label, duration, -and a label-caps status. A waterfall of horizontal bars follows: `rule-2` -background, `ink` fill, `alert` fill for error spans. +and a label-caps status. A waterfall of horizontal bars follows: `surface` +track, `ink` fill, `alert` fill for error spans. Rows separate by spacing — +no dividers. > `TraceStatus` is the consumer's own domain type. The shape assumed here is > `{ id, op, durationMs, status, startMs?, spanMs? }`. Replace with whatever @@ -1140,9 +1396,9 @@ interface TraceProps { const statusTone: Record< TraceStatus, - { dot: 'accent' | 'warn' | 'alert'; label: string; bar: string } + { dot: 'ok' | 'warn' | 'alert'; label: string; bar: string } > = { - ok: { dot: 'accent', label: 'text-accent', bar: 'bg-ink' }, + ok: { dot: 'ok', label: 'text-ok', bar: 'bg-ink' }, warn: { dot: 'warn', label: 'text-warn', bar: 'bg-warn' }, err: { dot: 'alert', label: 'text-alert', bar: 'bg-alert' }, } @@ -1154,11 +1410,11 @@ export function Trace({ title, rows, totalMs, className }: TraceProps) { ...rows.map((r) => (r.startMs ?? 0) + (r.spanMs ?? r.durationMs)), ) return ( -
-
+
+
{title}
-
    +
      {rows.map((row) => { const tone = statusTone[row.status] const start = ((row.startMs ?? 0) / span) * 100 @@ -1181,7 +1437,7 @@ export function Trace({ title, rows, totalMs, className }: TraceProps) { > {row.status} -
      +
      +
      {title ? (
      {title} @@ -1232,8 +1489,8 @@ export function Cell({ title, children, className }: CellProps) { 400px-wide ticker card with a name + version row, description, a `panel`-tinted command block, and a footer with a kind tag and check icon. -Focused state switches body fill from `bg` to `panel` and grows a -`border-l-2 border-l-accent` rail on the left edge. +Focused state switches the body fill from `surface` to `surface-selected` +(the accent tint) — no rail, no outline. ```tsx import * as React from 'react' @@ -1261,12 +1518,12 @@ export function WorkerCard({ return (
      -
      +
      {name}
      @@ -1277,10 +1534,10 @@ export function WorkerCard({
      {description}
      -
      +
      {command}
      -