feat(ui): gallery feedback round 2 — incident-card legibility, bar-fill stragglers, cyan liveness sweep
Three user rulings from the tune-session gallery (intentional visible changes; snapshots NOT re-baselined): A. BudgetIncidentCard hard-stop card was unreadable in LIGHT mode (pre-existing: dark-tuned red-50/100/200 text over the light pink gradient, no light variants). Eyebrow/title/description/banner/icon/ validation now use red-600..950 light text with the original dark classes behind dark:; same treatment for sibling BudgetPolicyCard statusTone chips + red banner. B1 gradient tokens untouched. B. Remaining bar-fill stragglers -> canonical status hues: ClaudeSubscriptionPanel fillClass and ProviderQuotaCard quota-window fills (red-400 -> bg-(--status-task-blocked), amber/yellow-400 -> bg-(--status-task-todo), green-400 -> bg-(--status-task-done)). BudgetSidebarMarker is an icon badge, not a bar — left. C. Systematic cyan->status-blue liveness sweep (~50 sites / 20 files): running tones, live dots/pings, Live surfaces (LiveRunWidget, ActiveAgentsPanel Live-now box, AgentDetail live card, IssueDetail Live pill), scheduled-retry family, externalObjectStatus running maps, timeline now-marker #2dd4bf -> #2563eb, and liveness glow shadow token VALUES --shadow-extract-1/11/14 rgba(6,182,212,0.08) -> rgba(37,99,235,0.08) (sites unchanged). Non-liveness cyan left and documented (xterm cursor, on_demand chips, assets chip, import rename text, recovery_required, ledger Advanced/snoozed, UxLab). DECISION-SHEET.md: 'Gallery feedback round 2' section. Gates 3/3 CLEAN, typecheck green, ui build green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
59e536fc08
commit
e2c6f4b4b7
|
|
@ -46,6 +46,13 @@ User rulings from the tune-session gallery review; all intentionally visible, sn
|
|||
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.
|
||||
|
||||
## Gallery feedback round 2 (preset-tune session, Jul 6) — executed
|
||||
|
||||
1. **BudgetIncidentCard light-mode legibility (pre-existing bug)** — the hard-stop card's eyebrow/title/description/banner used dark-tuned red-50/100/200 text with no light variants over the light pink gradient. Light mode now uses red-600..950-tier text (matching the app's existing `text-red-700 dark:text-red-300` light-red-surface pattern); dark classes preserved verbatim behind `dark:`. Sibling fix: BudgetPolicyCard statusTone chips (hard_stop/warning/ok) + its red banner had the same dark-only text — same treatment. Gradient backgrounds (kept B1 tokens) untouched.
|
||||
2. **Bar fills, remaining stragglers → status hues** — ClaudeSubscriptionPanel fillClass ("Current week Opus only" salmon red) and ProviderQuotaCard quota-window fills: red-400→`bg-(--status-task-blocked)`, amber/yellow-400→`bg-(--status-task-todo)`, green-400→`bg-(--status-task-done)`. Healthy `bg-primary/70` + null `bg-zinc-700` tiers unchanged (r1 ruling). Inspected, NOT a bar: BudgetSidebarMarker circular icon badge (left).
|
||||
3. **Systematic cyan→status-blue liveness sweep** (~50 sites / 20 files; supersedes r1's "Live family left" note per user ruling): running-status tones (CommentThread, interrupt-handoff, runRetryState, AgentDetail run chip + status maps), live dots/pings (AgentDetail, OnboardingChat, ArtifactsPanel generating, ActiveAgentsPanel, IssueDetail Live pill, RunTranscriptView, IssueRunLedger live chip, DesignGuide sample), Live surfaces (LiveRunWidget theme, ActiveAgentsPanel Live-now box, AgentDetail live-card border), scheduled-retry family (IssueScheduledRetryCard, runRetryState, IssueRunLedger retry-pending), externalObjectStatus icon/badge `running` (now shares blue with `open`; liveness pulse differentiates — flagged), timeline "now" marker `#2dd4bf`→`#2563eb` (1.5px line vs `#5b9bf6` delegated bars, shape differentiates — flagged), and the liveness glow shadow tokens `--shadow-extract-1/11/14` value-edited `rgba(6,182,212,0.08)`→`rgba(37,99,235,0.08)` (kept-token VALUES changed, call sites unchanged).
|
||||
Deliberately LEFT (non-liveness cyan, one-line reasons): xterm terminal cursor (CompanyEnvironments — terminal chrome); `on_demand` invocation-source chips (AgentDetail x2 + DesignGuide sample — source tag, not liveness); CompanySkills "Includes assets" chip (content-type tag); CompanyImport renamed-file mono text x2 (rename annotation); BlockedReasonChip `recovery_required` (blocked-reason category); IssueRunLedger "Advanced" outcome + "Silence snoozed" tones (outcome/pause semantics, not live); UxLab decorative gradients (B1 allowlisted decoration).
|
||||
|
||||
## Verification status (this review)
|
||||
|
||||
- `pnpm check:token-gates` — re-run independently: 3/3 CLEAN (468 files, 31 allowlist entries).
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ const AgentRunCard = memo(function AgentRunCard({
|
|||
<div className={cn(
|
||||
"flex h-(--sz-320px) flex-col overflow-hidden rounded-xl border shadow-sm",
|
||||
isActive
|
||||
? "border-cyan-500/25 bg-cyan-500/[0.04] shadow-(--shadow-extract-1)"
|
||||
? "border-blue-500/25 bg-blue-500/[0.04] shadow-(--shadow-extract-1)"
|
||||
: "border-border bg-background/70",
|
||||
className,
|
||||
)}>
|
||||
|
|
@ -182,8 +182,8 @@ const AgentRunCard = memo(function AgentRunCard({
|
|||
<div className="flex items-center gap-2">
|
||||
{isActive ? (
|
||||
<span className="relative flex h-2.5 w-2.5 shrink-0">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-cyan-400 opacity-70" />
|
||||
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-cyan-500" />
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-blue-400 opacity-70" />
|
||||
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-blue-500" />
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex h-2.5 w-2.5 rounded-full bg-muted-foreground/35" />
|
||||
|
|
@ -209,7 +209,7 @@ const AgentRunCard = memo(function AgentRunCard({
|
|||
to={`/issues/${issue?.identifier ?? run.issueId}`}
|
||||
className={cn(
|
||||
"line-clamp-2 hover:underline",
|
||||
isActive ? "text-cyan-700 dark:text-cyan-300" : "text-muted-foreground hover:text-foreground",
|
||||
isActive ? "text-blue-700 dark:text-blue-300" : "text-muted-foreground hover:text-foreground",
|
||||
)}
|
||||
title={issue?.title ? `${issue?.identifier ?? run.issueId.slice(0, 8)} - ${issue.title}` : issue?.identifier ?? run.issueId.slice(0, 8)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -193,8 +193,8 @@ export function ArtifactsPanel({ taskId, isAgentWorking, openDocKey, openDocTitl
|
|||
{showGenerating ? (
|
||||
<div className="mt-0.5 shrink-0">
|
||||
<span className="relative flex h-4 w-4">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-4 w-4 bg-cyan-500" />
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-4 w-4 bg-blue-500" />
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -212,7 +212,7 @@ export function ArtifactsPanel({ taskId, isAgentWorking, openDocKey, openDocTitl
|
|||
{wp.type.replace(/_/g, " ")}
|
||||
</span>
|
||||
{showGenerating ? (
|
||||
<span className="inline-flex items-center gap-1 text-(length:--text-nano) font-medium px-1.5 py-0.5 rounded-full bg-cyan-500/10 text-cyan-600 dark:text-cyan-400">
|
||||
<span className="inline-flex items-center gap-1 text-(length:--text-nano) font-medium px-1.5 py-0.5 rounded-full bg-blue-500/10 text-blue-600 dark:text-blue-400">
|
||||
<Loader2 className="h-2.5 w-2.5 animate-spin" />
|
||||
Generating...
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -48,25 +48,25 @@ export function BudgetIncidentCard({
|
|||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="text-(length:--text-micro) uppercase tracking-(--tracking-caps) text-red-200/80">
|
||||
<div className="text-(length:--text-micro) uppercase tracking-(--tracking-caps) text-red-700/90 dark:text-red-200/80">
|
||||
{incident.scopeType} hard stop
|
||||
</div>
|
||||
<Badge variant={incident.status === "resolved" ? "outline" : "secondary"}>
|
||||
{stateLabel}
|
||||
</Badge>
|
||||
</div>
|
||||
<CardTitle className="mt-1 text-base text-red-50">{incident.scopeName}</CardTitle>
|
||||
<CardDescription className="mt-1 text-red-100/70">
|
||||
<CardTitle className="mt-1 text-base text-red-950 dark:text-red-50">{incident.scopeName}</CardTitle>
|
||||
<CardDescription className="mt-1 text-red-900/75 dark:text-red-100/70">
|
||||
Spending reached {formatCents(incident.amountObserved)} against a limit of {formatCents(incident.amountLimit)}.
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="rounded-full border border-red-400/30 bg-red-500/10 p-2 text-red-200">
|
||||
<div className="rounded-full border border-red-400/30 bg-red-500/10 p-2 text-red-600 dark:text-red-200">
|
||||
<AlertOctagon className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4 px-5 pb-5 pt-0">
|
||||
<div className="flex items-start gap-2 rounded-xl border border-red-400/20 bg-red-500/10 px-3 py-2 text-sm text-red-50/90">
|
||||
<div className="flex items-start gap-2 rounded-xl border border-red-400/20 bg-red-500/10 px-3 py-2 text-sm text-red-950/90 dark:text-red-50/90">
|
||||
<PauseCircle className="mt-0.5 h-4 w-4 shrink-0" />
|
||||
<div>
|
||||
{incident.scopeType === "project"
|
||||
|
|
@ -98,7 +98,7 @@ export function BudgetIncidentCard({
|
|||
</Button>
|
||||
</div>
|
||||
{parsed !== null && parsed <= incident.amountObserved ? (
|
||||
<p className="mt-2 text-xs text-red-200/80">
|
||||
<p className="mt-2 text-xs text-red-700 dark:text-red-200/80">
|
||||
The new budget must exceed current observed spend.
|
||||
</p>
|
||||
) : null}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ function windowLabel(windowKind: BudgetPolicySummary["windowKind"]) {
|
|||
}
|
||||
|
||||
function statusTone(status: BudgetPolicySummary["status"]) {
|
||||
if (status === "hard_stop") return "text-red-300 border-red-500/30 bg-red-500/10";
|
||||
if (status === "warning") return "text-amber-200 border-amber-500/30 bg-amber-500/10";
|
||||
return "text-emerald-200 border-emerald-500/30 bg-emerald-500/10";
|
||||
if (status === "hard_stop") return "text-red-700 dark:text-red-300 border-red-500/30 bg-red-500/10";
|
||||
if (status === "warning") return "text-amber-700 dark:text-amber-200 border-amber-500/30 bg-amber-500/10";
|
||||
return "text-emerald-700 dark:text-emerald-200 border-emerald-500/30 bg-emerald-500/10";
|
||||
}
|
||||
|
||||
export function BudgetPolicyCard({
|
||||
|
|
@ -116,7 +116,7 @@ export function BudgetPolicyCard({
|
|||
);
|
||||
|
||||
const pausedPane = summary.paused ? (
|
||||
<div className="flex items-start gap-2 rounded-xl border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-100">
|
||||
<div className="flex items-start gap-2 rounded-xl border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-900 dark:text-red-100">
|
||||
<PauseCircle className="mt-0.5 h-4 w-4 shrink-0" />
|
||||
<div>
|
||||
{summary.scopeType === "project"
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ function orderedWindows(windows: QuotaWindow[]): QuotaWindow[] {
|
|||
|
||||
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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ function runStatusClass(status: string) {
|
|||
case "timed_out":
|
||||
return "text-orange-700 dark:text-orange-300";
|
||||
case "running":
|
||||
return "text-cyan-700 dark:text-cyan-300";
|
||||
return "text-blue-700 dark:text-blue-300";
|
||||
case "queued":
|
||||
case "pending":
|
||||
return "text-amber-700 dark:text-amber-300";
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ function getIconSpec(
|
|||
// Heartbeat — animated when active, static otherwise
|
||||
if (action.startsWith("heartbeat.")) {
|
||||
if (isActive && action === "heartbeat.invoked") {
|
||||
return { kind: "lucide", Icon: Loader2, color: "text-cyan-600 dark:text-cyan-400", spin: true };
|
||||
return { kind: "lucide", Icon: Loader2, color: "text-blue-600 dark:text-blue-400", spin: true };
|
||||
}
|
||||
return { kind: "lucide", Icon: Loader2, color: "text-muted-foreground" };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ const PENDING_LIVENESS_COPY: LivenessCopy = {
|
|||
|
||||
const RETRY_PENDING_LIVENESS_COPY: LivenessCopy = {
|
||||
label: "Retry pending",
|
||||
tone: "border-cyan-500/30 bg-cyan-500/10 text-cyan-700 dark:text-cyan-300",
|
||||
tone: "border-blue-500/30 bg-blue-500/10 text-blue-700 dark:text-blue-300",
|
||||
description: "Paperclip queued an automatic retry that has not started yet.",
|
||||
};
|
||||
|
||||
|
|
@ -734,8 +734,8 @@ export function IssueRunLedgerContent({
|
|||
{statusLabel(run.status)}
|
||||
</span>
|
||||
{run.isLive ? (
|
||||
<span className="inline-flex items-center gap-1 rounded-md border border-cyan-500/30 bg-cyan-500/10 px-1.5 py-0.5 text-(length:--text-micro) text-cyan-700 dark:text-cyan-300">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-cyan-400" />
|
||||
<span className="inline-flex items-center gap-1 rounded-md border border-blue-500/30 bg-blue-500/10 px-1.5 py-0.5 text-(length:--text-micro) text-blue-700 dark:text-blue-300">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-blue-500" />
|
||||
live
|
||||
</span>
|
||||
) : null}
|
||||
|
|
|
|||
|
|
@ -69,12 +69,12 @@ export function IssueScheduledRetryCard({
|
|||
return (
|
||||
<div
|
||||
data-testid="issue-scheduled-retry-card"
|
||||
className="mb-3 rounded-lg border border-cyan-500/30 bg-cyan-500/5 px-3 py-3"
|
||||
className="mb-3 rounded-lg border border-blue-500/30 bg-blue-500/5 px-3 py-3"
|
||||
>
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex flex-wrap items-center gap-2 text-xs">
|
||||
<span className="inline-flex items-center gap-1 rounded-full border border-cyan-500/30 bg-cyan-500/10 px-2 py-0.5 font-medium text-cyan-700 dark:text-cyan-300">
|
||||
<span className="inline-flex items-center gap-1 rounded-full border border-blue-500/30 bg-blue-500/10 px-2 py-0.5 font-medium text-blue-700 dark:text-blue-300">
|
||||
<Clock className="h-3 w-3" aria-hidden="true" />
|
||||
{badgeLabel}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ export function LiveRunWidget({ issueId, companyId }: LiveRunWidgetProps) {
|
|||
if (runs.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-xl border border-cyan-500/25 bg-background/80 shadow-(--shadow-extract-11)">
|
||||
<div className="border-b border-border/60 bg-cyan-500/[0.04] px-4 py-3">
|
||||
<div className="text-xs font-semibold uppercase tracking-(--tracking-caps) text-cyan-700 dark:text-cyan-300">
|
||||
<div className="overflow-hidden rounded-xl border border-blue-500/25 bg-background/80 shadow-(--shadow-extract-11)">
|
||||
<div className="border-b border-border/60 bg-blue-500/[0.04] px-4 py-3">
|
||||
<div className="text-xs font-semibold uppercase tracking-(--tracking-caps) text-blue-700 dark:text-blue-300">
|
||||
Live Runs
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-muted-foreground">
|
||||
|
|
@ -113,7 +113,7 @@ export function LiveRunWidget({ issueId, companyId }: LiveRunWidgetProps) {
|
|||
<div className="mt-2 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
||||
<Link
|
||||
to={`/agents/${run.agentId}/runs/${run.id}`}
|
||||
className="inline-flex items-center rounded-full border border-border/70 bg-background/70 px-2 py-1 font-mono hover:border-cyan-500/30 hover:text-foreground"
|
||||
className="inline-flex items-center rounded-full border border-border/70 bg-background/70 px-2 py-1 font-mono hover:border-blue-500/30 hover:text-foreground"
|
||||
>
|
||||
{run.id.slice(0, 8)}
|
||||
</Link>
|
||||
|
|
@ -135,7 +135,7 @@ export function LiveRunWidget({ issueId, companyId }: LiveRunWidgetProps) {
|
|||
)}
|
||||
<Link
|
||||
to={`/agents/${run.agentId}/runs/${run.id}`}
|
||||
className="inline-flex items-center gap-1 rounded-full border border-border/70 bg-background/70 px-2.5 py-1 text-(length:--text-micro) font-medium text-cyan-700 transition-colors hover:border-cyan-500/30 hover:text-cyan-600 dark:text-cyan-300"
|
||||
className="inline-flex items-center gap-1 rounded-full border border-border/70 bg-background/70 px-2.5 py-1 text-(length:--text-micro) font-medium text-blue-700 transition-colors hover:border-blue-500/30 hover:text-blue-600 dark:text-blue-300"
|
||||
>
|
||||
Open run
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
|
|
|
|||
|
|
@ -323,8 +323,8 @@ export function OnboardingChat({
|
|||
{hasActiveRun ? (
|
||||
<>
|
||||
<span className="relative flex h-2.5 w-2.5 shrink-0">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-cyan-500" />
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-blue-500" />
|
||||
</span>
|
||||
{getRunStatusMessage(activeRun.status, agentName, elapsed)}
|
||||
</>
|
||||
|
|
@ -468,8 +468,8 @@ function WelcomeMessage({
|
|||
{!showMessage && (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground px-3 py-2">
|
||||
<span className="relative flex h-2.5 w-2.5 shrink-0">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-cyan-500" />
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-blue-500" />
|
||||
</span>
|
||||
{phase === "waking"
|
||||
? `${agentName} is waking up...`
|
||||
|
|
|
|||
|
|
@ -338,10 +338,10 @@ export function ProviderQuotaCard({
|
|||
qw.usedPercent == null
|
||||
? null
|
||||
: qw.usedPercent >= 90
|
||||
? "bg-red-400"
|
||||
? "bg-(--status-task-blocked)"
|
||||
: qw.usedPercent >= 70
|
||||
? "bg-yellow-400"
|
||||
: "bg-green-400";
|
||||
? "bg-(--status-task-todo)"
|
||||
: "bg-(--status-task-done)";
|
||||
return (
|
||||
<div key={qw.label} className="space-y-1">
|
||||
<div className="flex items-center justify-between gap-2 text-xs">
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ export function WorkTimelineChart({
|
|||
);
|
||||
})}
|
||||
|
||||
{/* now line — teal "Signal" present marker */}
|
||||
{/* now line — status-blue "Signal" present marker (gallery r2; was teal) */}
|
||||
{now >= layout.fromMs && now <= layout.toMs && (
|
||||
<line
|
||||
x1={layout.gutter + ((now - layout.fromMs) / 60000) * layout.pxPerMinute}
|
||||
|
|
|
|||
|
|
@ -744,7 +744,7 @@ function TranscriptToolCard({
|
|||
: "Completed";
|
||||
const statusTone =
|
||||
block.status === "running"
|
||||
? "text-cyan-700 dark:text-cyan-300"
|
||||
? "text-blue-700 dark:text-blue-300"
|
||||
: block.status === "error"
|
||||
? "text-red-700 dark:text-red-300"
|
||||
: "text-emerald-700 dark:text-emerald-300";
|
||||
|
|
@ -758,7 +758,7 @@ function TranscriptToolCard({
|
|||
? "text-red-600 dark:text-red-300"
|
||||
: block.status === "completed"
|
||||
? "text-emerald-600 dark:text-emerald-300"
|
||||
: "text-cyan-600 dark:text-cyan-300",
|
||||
: "text-blue-600 dark:text-blue-300",
|
||||
);
|
||||
const summary = block.status === "running"
|
||||
? summarizeToolInput(block.name, block.input, density)
|
||||
|
|
@ -857,7 +857,7 @@ function TranscriptCommandGroup({
|
|||
? summarizeToolInput("command_execution", runningItem.input, density)
|
||||
: null;
|
||||
const statusTone = isRunning
|
||||
? "text-cyan-700 dark:text-cyan-300"
|
||||
? "text-blue-700 dark:text-blue-300"
|
||||
: "text-foreground/70";
|
||||
|
||||
return (
|
||||
|
|
@ -885,7 +885,7 @@ function TranscriptCommandGroup({
|
|||
"inline-flex h-6 w-6 items-center justify-center rounded-full border shadow-sm",
|
||||
index > 0 && "-ml-1.5",
|
||||
isRunning
|
||||
? "border-cyan-500/25 bg-cyan-500/[0.08] text-cyan-600 dark:text-cyan-300"
|
||||
? "border-blue-500/25 bg-blue-500/[0.08] text-blue-600 dark:text-blue-300"
|
||||
: "border-border/70 bg-background text-foreground/55",
|
||||
isRunning && "animate-pulse",
|
||||
)}
|
||||
|
|
@ -934,7 +934,7 @@ function TranscriptCommandGroup({
|
|||
item.status === "error"
|
||||
? "border-red-500/25 bg-red-500/[0.08] text-red-600 dark:text-red-300"
|
||||
: item.status === "running"
|
||||
? "border-cyan-500/25 bg-cyan-500/[0.08] text-cyan-600 dark:text-cyan-300"
|
||||
? "border-blue-500/25 bg-blue-500/[0.08] text-blue-600 dark:text-blue-300"
|
||||
: "border-border/70 bg-background text-foreground/55",
|
||||
)}>
|
||||
<TerminalSquare className="h-3 w-3" />
|
||||
|
|
@ -985,7 +985,7 @@ function TranscriptToolGroup({
|
|||
? summarizeToolInput(runningItem.name, runningItem.input, density)
|
||||
: null;
|
||||
const statusTone = isRunning
|
||||
? "text-cyan-700 dark:text-cyan-300"
|
||||
? "text-blue-700 dark:text-blue-300"
|
||||
: "text-foreground/70";
|
||||
|
||||
return (
|
||||
|
|
@ -1008,7 +1008,7 @@ function TranscriptToolGroup({
|
|||
"inline-flex h-6 w-6 items-center justify-center rounded-full border shadow-sm",
|
||||
index > 0 && "-ml-1.5",
|
||||
isItemRunning
|
||||
? "border-cyan-500/25 bg-cyan-500/[0.08] text-cyan-600 dark:text-cyan-300"
|
||||
? "border-blue-500/25 bg-blue-500/[0.08] text-blue-600 dark:text-blue-300"
|
||||
: isItemError
|
||||
? "border-red-500/25 bg-red-500/[0.08] text-red-600 dark:text-red-300"
|
||||
: "border-border/70 bg-background text-foreground/55",
|
||||
|
|
@ -1049,7 +1049,7 @@ function TranscriptToolGroup({
|
|||
item.status === "error"
|
||||
? "border-red-500/25 bg-red-500/[0.08] text-red-600 dark:text-red-300"
|
||||
: item.status === "running"
|
||||
? "border-cyan-500/25 bg-cyan-500/[0.08] text-cyan-600 dark:text-cyan-300"
|
||||
? "border-blue-500/25 bg-blue-500/[0.08] text-blue-600 dark:text-blue-300"
|
||||
: "border-border/70 bg-background text-foreground/55",
|
||||
)}>
|
||||
<Wrench className="h-3 w-3" />
|
||||
|
|
@ -1106,8 +1106,8 @@ function TranscriptActivityRow({
|
|||
<Check className="mt-0.5 h-3.5 w-3.5 shrink-0 text-emerald-600 dark:text-emerald-300" />
|
||||
) : (
|
||||
<span className="relative mt-1 flex h-2.5 w-2.5 shrink-0">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-cyan-400 opacity-70" />
|
||||
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-cyan-500" />
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-blue-400 opacity-70" />
|
||||
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-blue-500" />
|
||||
</span>
|
||||
)}
|
||||
<div className={cn(
|
||||
|
|
|
|||
|
|
@ -1681,7 +1681,7 @@ span.paperclip-mention-chip[data-mention-kind="external-object"] {
|
|||
--pct-85: 85%; /* Extracted from ui/src/components/IssueChatThread.test.tsx (max-w-[85%]). */
|
||||
--pct-neg-50: -50%; /* Extracted from ui/src/components/ui/alert-dialog.tsx (translate-x-[-50%]). */
|
||||
--pct-72: 72%; /* Extracted from ui/src/pages/IssueDetail.tsx (w-[72%]). */
|
||||
--shadow-extract-1: 0 16px 40px rgba(6,182,212,0.08); /* Extracted from ui/src/components/ActiveAgentsPanel.tsx (shadow-[0_16px_40px_rgba(6,182,212,0.08)]). */
|
||||
--shadow-extract-1: 0 16px 40px rgba(37,99,235,0.08); /* ActiveAgentsPanel.tsx live-box glow. Gallery feedback r2: liveness glow recolored cyan->status blue (value edit, site unchanged; originally extracted as rgba(6,182,212,0.08)). */
|
||||
--shadow-extract-2: 0 20px 80px -40px rgba(0,0,0,0.55); /* Extracted from ui/src/components/BudgetPolicyCard.tsx (shadow-[0_20px_80px_-40px_rgba(0,0,0,0.55)]). */
|
||||
--shadow-extract-3: 0 0 0 1px rgba(255,255,255,0.08); /* Extracted from ui/src/components/BudgetSidebarMarker.tsx (shadow-[0_0_0_1px_rgba(255,255,255,0.08)]). */
|
||||
--shadow-extract-4: 0 -12px 28px rgba(15,23,42,0.08); /* Extracted from ui/src/components/ChatComposer.test.tsx (shadow-[0_-12px_28px_rgba(15,23,42,0.08)]). */
|
||||
|
|
@ -1691,10 +1691,10 @@ span.paperclip-mention-chip[data-mention-kind="external-object"] {
|
|||
--shadow-extract-8: 0 1px 0 rgba(15,23,42,0.02); /* Extracted from ui/src/components/IssueRecoveryActionCard.tsx (shadow-[0_1px_0_rgba(15,23,42,0.02)]). */
|
||||
--shadow-extract-9: 0 18px 42px rgba(15,23,42,0.06); /* Extracted from ui/src/components/IssueThreadInteractionCard.tsx (shadow-[0_18px_42px_rgba(15,23,42,0.06)]). */
|
||||
--shadow-extract-10: 0 1px 0 1px hsl(var(--border)); /* Extracted from ui/src/components/KeyboardShortcutsCheatsheet.tsx (shadow-[0_1px_0_1px_hsl(var(--border))]). */
|
||||
--shadow-extract-11: 0 18px 50px rgba(6,182,212,0.08); /* Extracted from ui/src/components/LiveRunWidget.tsx (shadow-[0_18px_50px_rgba(6,182,212,0.08)]). */
|
||||
--shadow-extract-11: 0 18px 50px rgba(37,99,235,0.08); /* LiveRunWidget.tsx glow. Gallery feedback r2: liveness glow recolored cyan->status blue (value edit, site unchanged; originally extracted as rgba(6,182,212,0.08)). */
|
||||
--shadow-extract-12: 0 0 0 2px hsl(var(--background)); /* Extracted from ui/src/components/SidebarNavItem.tsx (shadow-[0_0_0_2px_hsl(var(--background))]). */
|
||||
--shadow-extract-13: 0 0 0 3px rgba(245,158,11,0.18); /* Extracted from ui/src/components/environment-variables-editor/index.tsx (shadow-[0_0_0_3px_rgba(245,158,11,0.18)]). */
|
||||
--shadow-extract-14: 0 0 12px rgba(6,182,212,0.08); /* Extracted from ui/src/pages/AgentDetail.tsx (shadow-[0_0_12px_rgba(6,182,212,0.08)]). */
|
||||
--shadow-extract-14: 0 0 12px rgba(37,99,235,0.08); /* AgentDetail.tsx live-card glow. Gallery feedback r2: liveness glow recolored cyan->status blue (value edit, site unchanged; originally extracted as rgba(6,182,212,0.08)). */
|
||||
--shadow-extract-18: 0 18px 44px -28px rgba(0,0,0,0.45); /* Extracted from ui/src/pages/ProfileSettings.tsx (shadow-[0_18px_44px_-28px_rgba(0,0,0,0.45)]). */
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export function runStatusClassName(status: string): string {
|
|||
case "timed_out":
|
||||
return "text-orange-700 dark:text-orange-300";
|
||||
case "running":
|
||||
return "text-cyan-700 dark:text-cyan-300";
|
||||
return "text-blue-700 dark:text-blue-300";
|
||||
case "queued":
|
||||
case "pending":
|
||||
return "text-amber-700 dark:text-amber-300";
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export function describeRunRetryState(run: RetryAwareRun): RunRetryStateSummary
|
|||
return {
|
||||
kind: "scheduled",
|
||||
badgeLabel: isMaxTurnContinuation ? "Continuation scheduled" : "Retry scheduled",
|
||||
tone: "border-cyan-500/30 bg-cyan-500/10 text-cyan-700 dark:text-cyan-300",
|
||||
tone: "border-blue-500/30 bg-blue-500/10 text-blue-700 dark:text-blue-300",
|
||||
detail: joinFragments([attemptLabel, reasonLabel]),
|
||||
secondary: dueAt
|
||||
? `${isMaxTurnContinuation ? "Next continuation" : "Next retry"} ${dueAt}`
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ export const priorityColorDefault = "text-yellow-600 dark:text-yellow-400";
|
|||
// unknown → backlog hue (muted, dashed circle)
|
||||
// open → todo / blue
|
||||
// waiting → amber (distinct from internal in_progress yellow)
|
||||
// running → cyan, animated when motion is allowed
|
||||
// running → status blue (gallery r2; was cyan), animated when motion is allowed
|
||||
// succeeded → done / green
|
||||
// failed → red
|
||||
// blocked → red
|
||||
|
|
@ -275,7 +275,7 @@ export const externalObjectStatusIcon: Record<string, string> = {
|
|||
unknown: "text-muted-foreground border-muted-foreground",
|
||||
open: "text-blue-600 border-blue-600 dark:text-blue-400 dark:border-blue-400",
|
||||
waiting: "text-amber-600 border-amber-600 dark:text-amber-400 dark:border-amber-400",
|
||||
running: "text-cyan-600 border-cyan-600 dark:text-cyan-400 dark:border-cyan-400",
|
||||
running: "text-blue-600 border-blue-600 dark:text-blue-400 dark:border-blue-400", // Gallery feedback r2: running = status blue (pulse animation still distinguishes it from static blue `open`).
|
||||
succeeded: "text-green-600 border-green-600 dark:text-green-400 dark:border-green-400",
|
||||
merged: "text-violet-600 border-violet-600 dark:text-violet-400 dark:border-violet-400",
|
||||
failed: "text-red-600 border-red-600 dark:text-red-400 dark:border-red-400",
|
||||
|
|
@ -292,7 +292,7 @@ export const externalObjectStatusBadge: Record<string, string> = {
|
|||
unknown: "bg-muted text-muted-foreground",
|
||||
open: "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300",
|
||||
waiting: "bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-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 r2: running = status blue (now shares tint with `open`; liveness animation differentiates).
|
||||
succeeded: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
||||
failed: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300",
|
||||
blocked: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300",
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ export function isCancelledStatus(status: string): boolean {
|
|||
* (kicked off by another actor) read blue; automation/self-started runs read
|
||||
* amber. The blue/amber pair is colour-blind-safe and holds contrast on both
|
||||
* light and dark backgrounds so the chart screenshots cleanly. Cancelled runs
|
||||
* drop their fill entirely (rendered as a hollow dashed bar) and a teal "now"
|
||||
* drop their fill entirely (rendered as a hollow dashed bar) and a status-blue "now"
|
||||
* line marks the present.
|
||||
*/
|
||||
export const TIMELINE_COLORS = {
|
||||
|
|
@ -133,7 +133,7 @@ export const TIMELINE_COLORS = {
|
|||
automation: "#f4b740",
|
||||
/** stroke/ink for a hollow, cancelled bar. */
|
||||
cancelled: "#9aa3ad",
|
||||
now: "#2dd4bf",
|
||||
now: "#2563eb", // Gallery feedback r2: "now" liveness marker = status blue (was teal #2dd4bf); shape (1.5px vertical line) still distinguishes it from #5b9bf6 delegated bars.
|
||||
} as const;
|
||||
|
||||
export type RunSourceKind = "delegated" | "automation";
|
||||
|
|
|
|||
|
|
@ -477,7 +477,7 @@ function workspaceOperationStatusTone(status: WorkspaceOperation["status"]) {
|
|||
case "failed":
|
||||
return "border-red-500/20 bg-red-500/10 text-red-700 dark:text-red-300";
|
||||
case "running":
|
||||
return "border-cyan-500/20 bg-cyan-500/10 text-cyan-700 dark:text-cyan-300";
|
||||
return "border-blue-500/20 bg-blue-500/10 text-blue-700 dark:text-blue-300";
|
||||
case "skipped":
|
||||
return "border-yellow-500/20 bg-yellow-500/10 text-yellow-700 dark:text-yellow-300";
|
||||
default:
|
||||
|
|
@ -1280,8 +1280,8 @@ function LatestRunCard({ runs, agentId }: { runs: HeartbeatRun[]; agentId: strin
|
|||
<h3 className="flex items-center gap-2 text-sm font-medium">
|
||||
{isLive && (
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-pulse absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-cyan-400" />
|
||||
<span className="animate-pulse absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-blue-500" />
|
||||
</span>
|
||||
)}
|
||||
{isLive ? "Live Run" : "Latest Run"}
|
||||
|
|
@ -1298,7 +1298,7 @@ function LatestRunCard({ runs, agentId }: { runs: HeartbeatRun[]; agentId: strin
|
|||
to={`/agents/${agentId}/runs/${run.id}`}
|
||||
className={cn(
|
||||
"block border rounded-lg p-4 space-y-2 w-full no-underline transition-colors hover:bg-muted/50 cursor-pointer",
|
||||
isLive ? "border-cyan-500/30 shadow-(--shadow-extract-14)" : "border-border"
|
||||
isLive ? "border-blue-500/30 shadow-(--shadow-extract-14)" : "border-border"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -4132,10 +4132,10 @@ function LogViewer({ run, adapterType }: { run: HeartbeatRun; adapterType: strin
|
|||
</Button>
|
||||
)}
|
||||
{isLive && (
|
||||
<span className="flex items-center gap-1 text-xs text-cyan-400">
|
||||
<span className="flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-pulse absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-cyan-400" />
|
||||
<span className="animate-pulse absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-blue-500" />
|
||||
</span>
|
||||
Live
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1402,10 +1402,10 @@ export function DesignGuide() {
|
|||
<div className="text-foreground">[12:00:17] INFO Reconnected successfully</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="relative flex h-1.5 w-1.5">
|
||||
<span className="absolute inline-flex h-full w-full rounded-full bg-cyan-400 animate-pulse" />
|
||||
<span className="inline-flex h-full w-full rounded-full bg-cyan-400" />
|
||||
<span className="absolute inline-flex h-full w-full rounded-full bg-blue-400 animate-pulse" />
|
||||
<span className="inline-flex h-full w-full rounded-full bg-blue-500" />
|
||||
</span>
|
||||
<span className="text-cyan-400">Live</span>
|
||||
<span className="text-blue-600 dark:text-blue-400">Live</span>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
|
|
|||
|
|
@ -3860,10 +3860,10 @@ export function IssueDetail() {
|
|||
<span className="text-sm font-mono text-muted-foreground shrink-0">{issue.identifier ?? issue.id.slice(0, 8)}</span>
|
||||
|
||||
{hasLiveRuns && (
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full bg-cyan-500/10 border border-cyan-500/30 px-2 py-0.5 text-(length:--text-nano) font-medium text-cyan-600 dark:text-cyan-400 shrink-0">
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full bg-blue-500/10 border border-blue-500/30 px-2 py-0.5 text-(length:--text-nano) font-medium text-blue-600 dark:text-blue-400 shrink-0">
|
||||
<span className="relative flex h-1.5 w-1.5">
|
||||
<span className="animate-pulse absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-cyan-400" />
|
||||
<span className="animate-pulse absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-blue-500" />
|
||||
</span>
|
||||
Live
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Reference in New Issue