From 59e536fc08887d8a921cf296f3389dcb44ae3acc Mon Sep 17 00:00:00 2001 From: scotttong Date: Mon, 6 Jul 2026 18:05:35 -0700 Subject: [PATCH] =?UTF-8?q?feat(ui):=20gallery=20feedback=20round=201=20?= =?UTF-8?q?=E2=80=94=20destructive=20revert,=20status=20bar=20fills,=20run?= =?UTF-8?q?ning=3Dblue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three user rulings from the tune-session gallery review (all intentional visible changes; snapshots NOT re-baselined): 1. Dark-mode --destructive reverted to master's original oklch(0.637 0.237 25.331) (preset's softer red rejected); light untouched. 2. Budget/quota progress-bar FILLS re-pointed at canonical status hues (bar-fill elements only): healthy -> bg-(--status-task-done), warning -> bg-(--status-task-todo), exceeded/hard-stop -> bg-(--status-task-blocked) in BudgetPolicyCard, QuotaBar (covers ProviderQuotaCard/BillerSpendCard surfaces), Costs, and CodexSubscriptionPanel (escalation tiers; healthy stays bg-primary/70 by design — flagged). Left: Org.tsx status dot, chip washes/borders. 3. RUNNING chips/labels cyan -> canonical blue: IssueChatThread running chip now composes brandChipBadge.blue (layout unchanged); RunTranscriptView running label via new runningLabelText export (hexes stay in lib/status-colors.ts, gate-clean); statusBadge.running, agentStatusDot.running, AgentDetail runStatusIcons.running -> blue. Left + flagged: externalObjectStatus* running (open already blue in those maps) and the cyan 'Live' branding family (dedicated decision recommended). DECISION-SHEET.md: 'Gallery feedback round 1' section added. Gates 3/3 CLEAN, typecheck green, ui build green (token bar-fill classes verified in built CSS), vitest 103/103 on touched components. Co-Authored-By: Claude Fable 5 --- DECISION-SHEET.md | 8 ++++++++ ui/src/components/BudgetPolicyCard.tsx | 6 +++--- ui/src/components/CodexSubscriptionPanel.tsx | 4 ++-- ui/src/components/IssueChatThread.tsx | 10 +++++++++- ui/src/components/QuotaBar.tsx | 6 +++--- ui/src/components/transcript/RunTranscriptView.tsx | 4 +++- ui/src/index.css | 2 +- ui/src/lib/status-colors.ts | 12 ++++++++++-- ui/src/pages/AgentDetail.tsx | 2 +- ui/src/pages/Costs.tsx | 6 +++--- 10 files changed, 43 insertions(+), 17 deletions(-) diff --git a/DECISION-SHEET.md b/DECISION-SHEET.md index cf94363351..94c5629fe2 100644 --- a/DECISION-SHEET.md +++ b/DECISION-SHEET.md @@ -38,6 +38,14 @@ Every open question from TOKEN-AUDIT.md §8 + batch logs and COMPONENT-INVENTORY | C9 | Toast system (no shadcn primitive installed) | Keep custom toast; document as permanent choice (working tone/variant system; sonner migration = churn without user-visible gain) | DEFERRED to Run 4 — decide when toast palette colors get retokenized; sonner-behind-a-pushToast-facade is the alternative to evaluate | | C10 | FeatureGate wrapper pattern (3 near-identical gates) | Nice-to-have shared primitive; backlog, not a run | APPROVED — backlog nice-to-have, not a run | +## Gallery feedback round 1 (preset-tune session, Jul 6) — executed + +User rulings from the tune-session gallery review; all intentionally visible, snapshots NOT re-baselined (fresh before/after triplets regenerated in tests/storybook-visual/test-results/ against the old baseline): + +1. **Dark destructive red reverted** — `.dark --destructive` back to master's original `oklch(0.637 0.237 25.331)` (preset's softer `oklch(0.704 0.191 22.216)` rejected); light mode untouched. +2. **Budget/quota BAR FILLS reuse status colors** — moving-fill elements only: healthy → `bg-(--status-task-done)`, warning → `bg-(--status-task-todo)`, exceeded/hard-stop → `bg-(--status-task-blocked)` in BudgetPolicyCard.tsx, QuotaBar.tsx (feeds ProviderQuotaCard/BillerSpendCard pages), Costs.tsx, CodexSubscriptionPanel.tsx (escalation tiers only). Inspected and deliberately LEFT: Org.tsx status dot (not a bar), BudgetPolicyCard chip washes/notice borders (not fills), CodexSubscriptionPanel healthy `bg-primary/70` + null `bg-zinc-700` (healthy tier uses brand primary by design — flagged as ambiguous, not emerald). +3. **RUNNING = status blue, not cyan/teal** — IssueChatThread running chip now composes `brandChipBadge.blue` (layout classes unchanged); RunTranscriptView running label uses new `runningLabelText` export (`text-[#1D4ED8] dark:text-[#2563EB]`, hexes kept in lib/status-colors.ts for gate cleanliness); `statusBadge.running` + `agentStatusDot.running` maps and AgentDetail `runStatusIcons.running` re-pointed cyan→blue. Deliberately LEFT + flagged: `externalObjectStatusIcon/Badge.running` (same-map collision — `open` is already blue there; documented UX-spec tone system), and the cyan "Live" branding family (LiveRunWidget theme, AgentDetail live-card border + Live pulse dots, DesignGuide Live sample) — "Live" is a distinct motif from RUNNING chips; note AgentDetail's mobile Live pill is already blue, so a dedicated Live-color decision is recommended. + ## Verification status (this review) - `pnpm check:token-gates` — re-run independently: 3/3 CLEAN (468 files, 31 allowlist entries). diff --git a/ui/src/components/BudgetPolicyCard.tsx b/ui/src/components/BudgetPolicyCard.tsx index 3b894cac69..b24692c7f9 100644 --- a/ui/src/components/BudgetPolicyCard.tsx +++ b/ui/src/components/BudgetPolicyCard.tsx @@ -104,10 +104,10 @@ export function BudgetPolicyCard({ className={cn( "h-full rounded-full transition-(--tp-width-background-color) duration-200", summary.status === "hard_stop" - ? "bg-red-400" + ? "bg-(--status-task-blocked)" : summary.status === "warning" - ? "bg-amber-300" - : "bg-emerald-300", + ? "bg-(--status-task-todo)" + : "bg-(--status-task-done)", )} style={{ width: `${progress}%` }} /> diff --git a/ui/src/components/CodexSubscriptionPanel.tsx b/ui/src/components/CodexSubscriptionPanel.tsx index 9751455e06..5f26314024 100644 --- a/ui/src/components/CodexSubscriptionPanel.tsx +++ b/ui/src/components/CodexSubscriptionPanel.tsx @@ -42,8 +42,8 @@ function detailText(window: QuotaWindow): string | null { function fillClass(usedPercent: number | null): string { if (usedPercent == null) return "bg-zinc-700"; - if (usedPercent >= 90) return "bg-red-400"; - if (usedPercent >= 70) return "bg-amber-400"; + if (usedPercent >= 90) return "bg-(--status-task-blocked)"; + if (usedPercent >= 70) return "bg-(--status-task-todo)"; return "bg-primary/70"; } diff --git a/ui/src/components/IssueChatThread.tsx b/ui/src/components/IssueChatThread.tsx index d4ac88df9e..4cc180f563 100644 --- a/ui/src/components/IssueChatThread.tsx +++ b/ui/src/components/IssueChatThread.tsx @@ -162,6 +162,7 @@ import { } from "../lib/transcriptPresentation"; import { buildAgentMentionHref } from "@paperclipai/shared"; import { cn, formatDateTime, formatShortDate } from "../lib/utils"; +import { brandChipBadge } from "../lib/status-colors"; import { nextWorkMode, titleForPendingWorkMode, workModeMetaFor, workModeMetaList } from "../lib/work-mode-meta"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; @@ -1896,7 +1897,14 @@ function IssueChatAssistantMessage({ ) : null} {isRunning ? ( - + // Gallery feedback r1: running chip uses the canonical brand blue + // (brandChipBadge.blue), not cyan; layout/size classes unchanged. + Running diff --git a/ui/src/components/QuotaBar.tsx b/ui/src/components/QuotaBar.tsx index 62c16e9523..0fcefde135 100644 --- a/ui/src/components/QuotaBar.tsx +++ b/ui/src/components/QuotaBar.tsx @@ -12,9 +12,9 @@ interface QuotaBarProps { } function fillColor(pct: number): string { - if (pct > 90) return "bg-red-400"; - if (pct > 70) return "bg-yellow-400"; - return "bg-green-400"; + if (pct > 90) return "bg-(--status-task-blocked)"; + if (pct > 70) return "bg-(--status-task-todo)"; + return "bg-(--status-task-done)"; } export function QuotaBar({ diff --git a/ui/src/components/transcript/RunTranscriptView.tsx b/ui/src/components/transcript/RunTranscriptView.tsx index a8b3453aaa..102fd23e72 100644 --- a/ui/src/components/transcript/RunTranscriptView.tsx +++ b/ui/src/components/transcript/RunTranscriptView.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import type { TranscriptEntry } from "../../adapters"; import { MarkdownBody, type MarkdownExternalReferenceMap } from "../MarkdownBody"; import { cn, formatTokens } from "../../lib/utils"; +import { runningLabelText } from "../../lib/status-colors"; import { Check, ChevronDown, @@ -1057,7 +1058,8 @@ function TranscriptToolGroup({ {humanizeLabel(item.name)} diff --git a/ui/src/index.css b/ui/src/index.css index d573f4cb1e..ffc36fe529 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -197,7 +197,7 @@ --muted-foreground: oklch(0.708 0 0); --accent: oklch(0.269 0 0); --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); + --destructive: oklch(0.637 0.237 25.331); /* Gallery feedback round 1: user preferred master's original dark destructive red over the preset's softer 0.704 0.191 22.216. */ --destructive-foreground: oklch(0.985 0 0); --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); diff --git a/ui/src/lib/status-colors.ts b/ui/src/lib/status-colors.ts index 5192c8fe54..c7133dfdaf 100644 --- a/ui/src/lib/status-colors.ts +++ b/ui/src/lib/status-colors.ts @@ -49,7 +49,7 @@ export const issueStatusTextDefault = "text-muted-foreground"; export const statusBadge: Record = { // Agent statuses active: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300", - running: "bg-cyan-100 text-cyan-700 dark:bg-cyan-900/50 dark:text-cyan-300", + running: "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300", // Gallery feedback r1: running = status blue (matches in_progress liveness), not cyan. scheduled_retry: "bg-sky-100 text-sky-700 dark:bg-sky-900/50 dark:text-sky-300", paused: "bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-300", idle: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300", @@ -146,6 +146,14 @@ export const brandChipBadge: Record = { red: "bg-[#FEE2E2] text-[#991B1B] border-[#DC2626] dark:bg-[#dc26262e] dark:text-[#DC2626] dark:border-[#dc262673]", }; +/** + * Brand blue TEXT pair (the text hues of `brandChipBadge.blue`) for non-chip + * "Running" labels — Gallery feedback round 1: running-state copy uses the + * canonical status blue, not cyan/teal. Kept here so components stay free of + * hex literals (token-gate scope). + */ +export const runningLabelText = "text-[#1D4ED8] dark:text-[#2563EB]"; + /** * Issue/task status → brand colour name (PAP-75). `in_progress` is blue * (liveness), `todo` amber (queued), `in_review` violet (awaiting review), @@ -219,7 +227,7 @@ export const taskStatusIconVarDefault = "--status-task-icon-backlog"; // --------------------------------------------------------------------------- export const agentStatusDot: Record = { - running: "bg-cyan-400 animate-pulse", + running: "bg-blue-400 animate-pulse", // Gallery feedback r1: running dot = blue, not cyan. active: "bg-green-400", paused: "bg-yellow-400", idle: "bg-yellow-400", diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx index e62684b707..c196c99b3e 100644 --- a/ui/src/pages/AgentDetail.tsx +++ b/ui/src/pages/AgentDetail.tsx @@ -116,7 +116,7 @@ import { const runStatusIcons: Record = { succeeded: { icon: CheckCircle2, color: "text-green-600 dark:text-green-400" }, failed: { icon: XCircle, color: "text-red-600 dark:text-red-400" }, - running: { icon: Loader2, color: "text-cyan-600 dark:text-cyan-400" }, + running: { icon: Loader2, color: "text-blue-600 dark:text-blue-400" }, // Gallery feedback r1: running = status blue, not cyan. queued: { icon: Clock, color: "text-yellow-600 dark:text-yellow-400" }, scheduled_retry: { icon: Clock, color: "text-sky-600 dark:text-sky-400" }, timed_out: { icon: Timer, color: "text-orange-600 dark:text-orange-400" }, diff --git a/ui/src/pages/Costs.tsx b/ui/src/pages/Costs.tsx index 90881ba3c6..b51a3f59ee 100644 --- a/ui/src/pages/Costs.tsx +++ b/ui/src/pages/Costs.tsx @@ -689,10 +689,10 @@ export function Costs() { className={cn( "h-full transition-(--tp-width-background-color) duration-150", spendData.summary.utilizationPercent > 90 - ? "bg-red-400" + ? "bg-(--status-task-blocked)" : spendData.summary.utilizationPercent > 70 - ? "bg-yellow-400" - : "bg-emerald-400", + ? "bg-(--status-task-todo)" + : "bg-(--status-task-done)", )} style={{ width: `${Math.min(100, spendData.summary.utilizationPercent)}%` }} />