From df38ec003cbe64e00d16cafb13e676dafe1c2cd7 Mon Sep 17 00:00:00 2001 From: Aaron Trowbridge Date: Sun, 25 Oct 2026 10:28:48 -0400 Subject: [PATCH] =?UTF-8?q?feat(app):=20unify=20activity=20status=20colors?= =?UTF-8?q?=20=E2=80=94=20blue=20running,=20amber=20attention?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One activity vocabulary (--activity-{idle,running,attention,success,error} plus -soft fills) aliasing the v2 state tokens replaces the raw-hex --status-* tokens. Session tab status gains an attention state (error > attention > running > unread > idle) shared by the tab strip, sessions dropdown, home and sidebar. Run, connection, MCP, LSP, server, vault and devtools dots map onto the same roles, carry accessible labels, and share one reduced-motion-aware pulse. Yellow no longer encodes any state. Refs #378 --- packages/app/CONTEXT.md | 28 +++++ .../app/src/components/connection-banner.tsx | 15 ++- .../app/src/components/dialog-new-project.tsx | 2 +- .../app/src/components/server/server-row.tsx | 28 +++-- .../session/context-warning-banner.tsx | 4 +- .../settings-v2/developer-tools.tsx | 22 ++-- .../src/components/status-popover-body.tsx | 22 +--- .../status-popover-indicator.test.ts | 45 ++++--- .../components/status-popover-indicator.ts | 33 +++-- .../app/src/components/status-popover.tsx | 53 +++++--- packages/app/src/design-polish.css | 91 ++++++++++---- packages/app/src/pages/home/legacy-home.tsx | 16 +-- .../pages/layout/session-tab-status.test.ts | 33 ++++- .../src/pages/layout/session-tab-status.tsx | 48 +++++--- .../app/src/pages/layout/sidebar-items.tsx | 83 ++++++++----- .../src/pages/layout/sidebar-workspace.tsx | 4 +- .../pages/session/v2/lineage-ledger-panel.css | 4 +- .../src/server/amicode/widget-frame-html.ts | 4 +- .../test/server/amicode-widget-frame.test.ts | 6 + .../session-review-file-preview-v2.tsx | 16 ++- packages/ui/src/amicode/activity-dot.tsx | 22 ++++ packages/ui/src/amicode/activity.test.ts | 57 +++++++++ packages/ui/src/amicode/activity.ts | 76 ++++++++++++ packages/ui/src/amicode/amico-presence.css | 28 +---- .../ui/src/amicode/amico-presence.stories.tsx | 2 - packages/ui/src/amicode/amicode.css | 113 +++++++++++------- packages/ui/src/amicode/calibration-view.tsx | 2 +- packages/ui/src/amicode/connection-icon.tsx | 28 ++--- packages/ui/src/amicode/connections-tab.tsx | 22 ++-- packages/ui/src/amicode/device-view.tsx | 4 +- packages/ui/src/amicode/run-view.tsx | 14 +-- packages/ui/src/amicode/run-window.tsx | 25 ++-- packages/ui/src/amicode/vaults-tab.tsx | 17 +-- .../ui/src/components/amicode-activity.tsx | 12 ++ packages/ui/src/components/card.css | 2 +- packages/ui/src/components/card.tsx | 11 +- 36 files changed, 664 insertions(+), 328 deletions(-) create mode 100644 packages/ui/src/amicode/activity-dot.tsx create mode 100644 packages/ui/src/amicode/activity.test.ts create mode 100644 packages/ui/src/amicode/activity.ts create mode 100644 packages/ui/src/components/amicode-activity.tsx diff --git a/packages/app/CONTEXT.md b/packages/app/CONTEXT.md index 53a5f5e8f0..7a8bf1bde4 100644 --- a/packages/app/CONTEXT.md +++ b/packages/app/CONTEXT.md @@ -92,6 +92,34 @@ It persists until onboarding is complete and then disappears; it never gates the _Avoid_: Onboarding modal, wizard (the wizard is the conversational interview; the nudge only surfaces its state), setup wall +### Activity vocabulary + +**Activity**: +The one semantic state set every status surface speaks — session dots (tab strip, +sessions dropdown, sidebar, home), run status (run window, run view), health dots +(connection, MCP, LSP, server, vault), build status, and status banners. Five roles, +each a `--activity-*` token in `src/design-polish.css` (solid dot colour plus a `-soft` +fill) aliasing the theme's v2 state tokens so light and dark move together: + +| Role | Colour | Means | +| --- | --- | --- | +| `running` | blue | in progress — working, solving, pending, validating | +| `attention` | amber | waiting on you — permission, question, needs auth, expired, drift, stalled | +| `success` | green | done-unread, healthy, finished | +| `error` | red | failed, unreachable | +| `idle` | grey | done-seen, aborted, stopped, disabled | + +A session's tab status ranks **error > attention > running > done-unread > idle**. Running +dots share one reduced-motion-aware pulse (`.amc-activity-pulse`). Colour is never the +only signal: every status dot carries an accessible label or sits beside text that +names the state. + +**Yellow never encodes a state.** Yellow is brand and selection only (the `--accent` +family). Amico's presence — the thought-rail harmonic dot, the working lane, the +thinking wave, the mark — is brand, not status, and keeps the accent. +_Avoid_: yellow "running", gold/amber warning edges from the theme as an attention +colour, the agent identity tint on a status spinner, raw hex status colours + ## Relationships - The **Landing** is a **Chat**; the window never opens on anything else. diff --git a/packages/app/src/components/connection-banner.tsx b/packages/app/src/components/connection-banner.tsx index be54a988d9..8be6b59fe1 100644 --- a/packages/app/src/components/connection-banner.tsx +++ b/packages/app/src/components/connection-banner.tsx @@ -1,6 +1,7 @@ import { Show, createEffect, createSignal, onCleanup } from "solid-js" import { useLanguage } from "@/context/language" import { useServerSDK } from "@/context/server-sdk" +import { ActivityDot } from "@opencode-ai/ui/amicode-activity" // Amicode webview: a dead server (e.g. an opencode:build restart) used to read // as an endless "thinking" wave — the event loop retries silently every 250ms. @@ -64,12 +65,18 @@ export function ConnectionBanner() { "border-radius": "var(--radius-full)", "font-size": "12px", "font-weight": 600, - border: "1px solid var(--v2-border-border-base, #3c3c3c)", - background: "var(--v2-background-bg-layer-01, #1e1e1e)", - "box-shadow": "0 8px 24px rgba(0, 0, 0, 0.35)", - color: down() ? "var(--v2-state-fg-warning, #d29922)" : "var(--v2-state-fg-success, #3fb950)", + display: "flex", + "align-items": "center", + gap: "var(--space-2)", + border: "1px solid var(--v2-border-border-base)", + background: "var(--v2-background-bg-layer-01)", + "box-shadow": "var(--elev-float)", + color: "var(--v2-text-text-base)", }} > + {/* activity dot: attention while the link is down, success once back. + The words carry the state; the dot is the at-a-glance colour. */} + {down() ? language.t("app.server.connectionLost") : language.t("app.server.reconnected")} diff --git a/packages/app/src/components/dialog-new-project.tsx b/packages/app/src/components/dialog-new-project.tsx index e75364a8a0..a7d7615443 100644 --- a/packages/app/src/components/dialog-new-project.tsx +++ b/packages/app/src/components/dialog-new-project.tsx @@ -76,7 +76,7 @@ export function DialogNewProject(props: {