feat(ui): gallery feedback round 1 — destructive revert, status bar fills, running=blue
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 <noreply@anthropic.com>
This commit is contained in:
parent
46d9a3c0c9
commit
59e536fc08
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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}%` }}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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({
|
|||
</Badge>
|
||||
) : null}
|
||||
{isRunning ? (
|
||||
<span className="inline-flex items-center gap-1 rounded-full border border-cyan-400/40 bg-cyan-500/10 px-2 py-0.5 text-(length:--text-nano) font-medium uppercase tracking-(--tracking-eyebrow) text-cyan-700 dark:text-cyan-200">
|
||||
// Gallery feedback r1: running chip uses the canonical brand blue
|
||||
// (brandChipBadge.blue), not cyan; layout/size classes unchanged.
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-(length:--text-nano) font-medium uppercase tracking-(--tracking-eyebrow)",
|
||||
brandChipBadge.blue,
|
||||
)}
|
||||
>
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
Running
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
</span>
|
||||
<span className={cn("text-(length:--text-nano) font-semibold uppercase tracking-(--tracking-eyebrow)",
|
||||
item.status === "running" ? "text-cyan-700 dark:text-cyan-300"
|
||||
// Gallery feedback r1: running label uses brand blue, not cyan.
|
||||
item.status === "running" ? runningLabelText
|
||||
: item.status === "error" ? "text-red-700 dark:text-red-300"
|
||||
: "text-emerald-700 dark:text-emerald-300"
|
||||
)}>
|
||||
|
|
|
|||
|
|
@ -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%);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export const issueStatusTextDefault = "text-muted-foreground";
|
|||
export const statusBadge: Record<string, string> = {
|
||||
// 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<BrandChipColor, string> = {
|
|||
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<string, string> = {
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ import {
|
|||
const runStatusIcons: Record<string, { icon: typeof CheckCircle2; color: string }> = {
|
||||
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" },
|
||||
|
|
|
|||
|
|
@ -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)}%` }}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue