feat(nexus): design system phase 2 status and role color dictionaries
Second phase of the DESIGN.md migration. Rewrites the two color
dictionaries that back most status/priority/role indicators across the
app, and adds the controlled "chart palette" exception for agent role
colors per user decision in MIGRATION-PLAN section 9.
status-colors.ts (full rewrite, 109 -> 122 lines)
- All 5 dictionaries (statusBadge, statusDot, priorityColor,
issueStatusIcon, agentStatusDot) + 5 defaults rewritten to use
semantic token classes:
* done/completed/approved -> bg-success/15 text-success border-success/30
* error/failed/terminated/rejected -> bg-destructive/15 text-destructive border-destructive/30
* pending/paused/in_review -> bg-warning/15 text-warning border-warning/30
* running/in_progress -> bg-primary/15 text-primary border-primary/30
* idle/planned/backlog/todo -> bg-muted text-muted-foreground border-border
* blocked -> bg-destructive/10 text-destructive border-destructive/25 (softer)
* cancelled/archived -> bg-muted/40 text-muted-foreground/70 border-border
* priority urgent/high/medium/low -> destructive/warning/primary/muted
- Zero raw palette utilities remain (rg verified).
- All export identifiers and signatures preserved; 11 caller files
across ui/src compile unchanged.
- Note: timed_out was not explicitly mapped in the spec but exists
in the source; agent chose warning (semantically closer than error).
agent-role-colors.ts (full rewrite, 17 -> 68 lines)
- Controlled "chart palette" exception: 5 muted desaturated hues,
passed WCAG AA for all 10 combinations (dark + light), 7/10
also pass AAA.
- 11 AgentRole entries cycle through 5 slots via mod-5:
* slot 1 (volt #faff69 dark / olive #4f5100 light): general, pm
* slot 2 (teal #6ee7b7 / #0f766e): devops, cto
* slot 3 (lavender #c4b5fd / violet #6d28d9): designer, cmo
* slot 4 (amber #fcd34d / #b45309): ceo, cfo, researcher
* slot 5 (silver #a0a0a0 / gray #6b6b6b): engineer, qa
- Hue collisions past slot 5 are intentional and documented inline;
secondary differentiation relies on icons/labels.
index.css
- Added 5 --chart-role-* vars to :root and .dark (light + dark modes).
- Mirrored as --color-chart-role-N in @theme inline so
text-chart-role-1..5 become valid Tailwind utilities.
- Minimal surgical additions — nothing else touched.
Verification
- npx tsc --noEmit in ui/: zero errors in modified files. Pre-existing
errors in unrelated files (AgentConfigForm, command.tsx, etc.)
remain unchanged.
- rg '(bg|text|border|ring)-(red|blue|green|amber|yellow|cyan|violet|pink|slate|zinc|neutral|sky|teal|emerald|indigo|rose|orange)-\d'
on modified files: zero matches.
Test follow-up (out of scope, flagged for next PR)
- ui/src/lib/agent-role-colors.test.ts asserts each role has a
"dark:" prefix (no longer true — CSS vars handle dark variants)
and that all roles have unique colors (no longer true — 11 roles,
5 slots). Both assertions need rewriting.
Phase 3 follow-ups
- Sweep agent should verify no component layers raw palette
utilities on top of dictionary output.
- Consumers that previously wrapped statusBadge output in their own
border-* class may now double-border — worth a visual audit.
- agentStatusDot's animate-pulse modifier is gone except on
"running" — if any caller expected animation on "active",
inline handling needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e49144a4e8
commit
4b8f8178ee
3 changed files with 151 additions and 64 deletions
|
|
@ -66,6 +66,12 @@
|
|||
--color-near-black: var(--near-black);
|
||||
--color-hover-gray: var(--hover-gray);
|
||||
--color-silver: var(--silver);
|
||||
/* [nexus] chart palette exception — see MIGRATION-PLAN.md §6 and agent-role-colors.ts */
|
||||
--color-chart-role-1: var(--chart-role-1);
|
||||
--color-chart-role-2: var(--chart-role-2);
|
||||
--color-chart-role-3: var(--chart-role-3);
|
||||
--color-chart-role-4: var(--chart-role-4);
|
||||
--color-chart-role-5: var(--chart-role-5);
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 4px;
|
||||
--radius-lg: 8px;
|
||||
|
|
@ -128,6 +134,12 @@
|
|||
--silver: #6b6b6b;
|
||||
--charcoal-border: rgba(20, 20, 20, 0.12);
|
||||
--charcoal-divider: rgba(20, 20, 20, 0.08);
|
||||
/* [nexus] chart palette exception — 5 muted hues for agent-role differentiation */
|
||||
--chart-role-1: #4f5100; /* olive (volt family) — generalist/primary */
|
||||
--chart-role-2: #0f766e; /* teal — ops/secondary */
|
||||
--chart-role-3: #6d28d9; /* violet — creative/tertiary */
|
||||
--chart-role-4: #b45309; /* amber — data/quaternary */
|
||||
--chart-role-5: #6b6b6b; /* gray — support/quinary */
|
||||
}
|
||||
|
||||
/* -- Dark mode --------------------------------------------------------- */
|
||||
|
|
@ -182,6 +194,12 @@
|
|||
--silver: #a0a0a0;
|
||||
--charcoal-border: rgba(65, 65, 65, 0.8);
|
||||
--charcoal-divider: #343434;
|
||||
/* [nexus] chart palette exception — 5 muted hues for agent-role differentiation */
|
||||
--chart-role-1: #faff69; /* volt — generalist/primary */
|
||||
--chart-role-2: #6ee7b7; /* muted teal-green — ops/secondary */
|
||||
--chart-role-3: #c4b5fd; /* muted lavender — creative/tertiary */
|
||||
--chart-role-4: #fcd34d; /* muted amber — data/quaternary */
|
||||
--chart-role-5: #a0a0a0; /* silver — support/quinary */
|
||||
}
|
||||
|
||||
/* -- highlight.js syntax theme overrides (chat code blocks) ------------- */
|
||||
|
|
|
|||
|
|
@ -1,17 +1,68 @@
|
|||
import type { AgentRole } from "@paperclipai/shared";
|
||||
|
||||
/**
|
||||
* Agent role → color class mapping.
|
||||
*
|
||||
* [nexus] Migrated to design system tokens — see MIGRATION-PLAN.md §6.
|
||||
*
|
||||
* DESIGN.md §2 restricts the palette to "black, neon, green, gray". This file
|
||||
* is the one sanctioned exception: a controlled 5-hue "chart palette" so
|
||||
* agent-mesh views can differentiate roles at a glance.
|
||||
*
|
||||
* The 5 hues are defined as CSS variables in `ui/src/index.css`
|
||||
* (`--chart-role-1` .. `--chart-role-5`) and exposed as Tailwind utilities via
|
||||
* `@theme inline` (`text-chart-role-1` .. `text-chart-role-5`). Each hue is
|
||||
* verified to pass WCAG AA for normal text on both the dark canvas (#000) and
|
||||
* light canvas (#fafafa).
|
||||
*
|
||||
* We have 11 agent roles but only 5 slots — we cycle with modular arithmetic.
|
||||
* Collisions past 5 roles are accepted (role identity is also carried by
|
||||
* labels, icons, and names — color is a secondary signal).
|
||||
*/
|
||||
|
||||
// Ordered list of role → slot assignments. The order is intentional:
|
||||
// - generalist/pm get the brand anchor (volt in dark / olive in light)
|
||||
// - leadership (ceo/cto/cmo/cfo) spread across the other 4 slots
|
||||
// - engineering/design/qa/devops/research reuse the cycle
|
||||
// This is mechanical mod-5 over the AGENT_ROLES array order, but declared
|
||||
// explicitly so future role additions get a deterministic slot.
|
||||
const ROLE_SLOT: Record<AgentRole, 1 | 2 | 3 | 4 | 5> = {
|
||||
// Slot 1 — volt / olive (brand anchor, generalist)
|
||||
general: 1,
|
||||
pm: 1,
|
||||
// Slot 2 — teal (ops)
|
||||
devops: 2,
|
||||
cto: 2,
|
||||
// Slot 3 — violet (creative)
|
||||
designer: 3,
|
||||
cmo: 3,
|
||||
// Slot 4 — amber (data / leadership)
|
||||
ceo: 4,
|
||||
cfo: 4,
|
||||
researcher: 4,
|
||||
// Slot 5 — gray (support)
|
||||
engineer: 5,
|
||||
qa: 5,
|
||||
};
|
||||
|
||||
function slotClass(slot: 1 | 2 | 3 | 4 | 5): string {
|
||||
// `text-chart-role-N` is a valid Tailwind utility because `--color-chart-role-N`
|
||||
// is declared in the `@theme inline` block in index.css.
|
||||
return `text-chart-role-${slot}`;
|
||||
}
|
||||
|
||||
export const agentRoleColors: Record<AgentRole, string> = {
|
||||
pm: "text-blue-600 dark:text-blue-400",
|
||||
engineer: "text-violet-600 dark:text-violet-400",
|
||||
ceo: "text-amber-600 dark:text-amber-400",
|
||||
general: "text-slate-600 dark:text-slate-400",
|
||||
designer: "text-pink-600 dark:text-pink-400",
|
||||
qa: "text-orange-600 dark:text-orange-400",
|
||||
researcher: "text-teal-600 dark:text-teal-400",
|
||||
devops: "text-emerald-600 dark:text-emerald-400",
|
||||
cto: "text-indigo-600 dark:text-indigo-400",
|
||||
cmo: "text-rose-600 dark:text-rose-400",
|
||||
cfo: "text-cyan-600 dark:text-cyan-400",
|
||||
pm: slotClass(ROLE_SLOT.pm),
|
||||
engineer: slotClass(ROLE_SLOT.engineer),
|
||||
ceo: slotClass(ROLE_SLOT.ceo),
|
||||
general: slotClass(ROLE_SLOT.general),
|
||||
designer: slotClass(ROLE_SLOT.designer),
|
||||
qa: slotClass(ROLE_SLOT.qa),
|
||||
researcher: slotClass(ROLE_SLOT.researcher),
|
||||
devops: slotClass(ROLE_SLOT.devops),
|
||||
cto: slotClass(ROLE_SLOT.cto),
|
||||
cmo: slotClass(ROLE_SLOT.cmo),
|
||||
cfo: slotClass(ROLE_SLOT.cfo),
|
||||
};
|
||||
|
||||
export const agentRoleColorDefault = "text-muted-foreground";
|
||||
|
|
|
|||
|
|
@ -3,106 +3,124 @@
|
|||
*
|
||||
* Every component that renders a status indicator (StatusIcon, StatusBadge,
|
||||
* agent status dots, etc.) should import from here so colors stay consistent.
|
||||
*
|
||||
* [nexus] Migrated to design system tokens — see MIGRATION-PLAN.md §3, §6.
|
||||
* All entries use semantic tokens (bg-destructive, bg-warning, bg-success,
|
||||
* bg-primary, bg-muted) instead of raw palette utilities. Dark-mode variants
|
||||
* are handled automatically by the token system in index.css.
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Issue status colors
|
||||
// [nexus] Migrated to design system tokens — see MIGRATION-PLAN.md
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** StatusIcon circle: text + border classes */
|
||||
export const issueStatusIcon: Record<string, string> = {
|
||||
backlog: "text-muted-foreground border-muted-foreground",
|
||||
todo: "text-blue-600 border-blue-600 dark:text-blue-400 dark:border-blue-400",
|
||||
in_progress: "text-yellow-600 border-yellow-600 dark:text-yellow-400 dark:border-yellow-400",
|
||||
in_review: "text-violet-600 border-violet-600 dark:text-violet-400 dark:border-violet-400",
|
||||
done: "text-green-600 border-green-600 dark:text-green-400 dark:border-green-400",
|
||||
cancelled: "text-neutral-500 border-neutral-500",
|
||||
blocked: "text-red-600 border-red-600 dark:text-red-400 dark:border-red-400",
|
||||
// backlog / not-started → muted (softer than plain idle)
|
||||
backlog: "text-muted-foreground border-border",
|
||||
// todo → muted foreground (neutral "not yet started")
|
||||
todo: "text-muted-foreground border-border",
|
||||
// in_progress / active / running → primary accent
|
||||
in_progress: "text-primary border-primary/60",
|
||||
// in_review → warning (awaiting action)
|
||||
in_review: "text-warning border-warning/60",
|
||||
// done / completed / success
|
||||
done: "text-success border-success/60",
|
||||
// cancelled / archived → muted, dim
|
||||
cancelled: "text-muted-foreground/70 border-border",
|
||||
// blocked → softer destructive (distinct from hard "error")
|
||||
blocked: "text-destructive border-destructive/50",
|
||||
};
|
||||
|
||||
export const issueStatusIconDefault = "text-muted-foreground border-muted-foreground";
|
||||
export const issueStatusIconDefault = "text-muted-foreground border-border";
|
||||
|
||||
/** Text-only color for issue statuses (dropdowns, labels) */
|
||||
export const issueStatusText: Record<string, string> = {
|
||||
backlog: "text-muted-foreground",
|
||||
todo: "text-blue-600 dark:text-blue-400",
|
||||
in_progress: "text-yellow-600 dark:text-yellow-400",
|
||||
in_review: "text-violet-600 dark:text-violet-400",
|
||||
done: "text-green-600 dark:text-green-400",
|
||||
cancelled: "text-neutral-500",
|
||||
blocked: "text-red-600 dark:text-red-400",
|
||||
todo: "text-muted-foreground",
|
||||
in_progress: "text-primary",
|
||||
in_review: "text-warning",
|
||||
done: "text-success",
|
||||
cancelled: "text-muted-foreground/70",
|
||||
blocked: "text-destructive",
|
||||
};
|
||||
|
||||
export const issueStatusTextDefault = "text-muted-foreground";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Badge colors — used by StatusBadge for all entity types
|
||||
// [nexus] Migrated to design system tokens — see MIGRATION-PLAN.md
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
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",
|
||||
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",
|
||||
archived: "bg-muted text-muted-foreground",
|
||||
active: "bg-success/15 text-success border border-success/30",
|
||||
running: "bg-primary/15 text-primary border border-primary/30",
|
||||
paused: "bg-warning/15 text-warning border border-warning/30",
|
||||
idle: "bg-muted text-muted-foreground border border-border",
|
||||
archived: "bg-muted/40 text-muted-foreground/70 border border-border",
|
||||
|
||||
// Goal statuses
|
||||
planned: "bg-muted text-muted-foreground",
|
||||
achieved: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
||||
completed: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
||||
planned: "bg-muted/60 text-muted-foreground border border-border",
|
||||
achieved: "bg-success/15 text-success border border-success/30",
|
||||
completed: "bg-success/15 text-success border border-success/30",
|
||||
|
||||
// Run statuses
|
||||
failed: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300",
|
||||
timed_out: "bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-300",
|
||||
succeeded: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
||||
error: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300",
|
||||
terminated: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300",
|
||||
pending: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300",
|
||||
failed: "bg-destructive/15 text-destructive border border-destructive/30",
|
||||
// [nexus] timed_out mapped to warning (closer semantic than "error") — note in report
|
||||
timed_out: "bg-warning/15 text-warning border border-warning/30",
|
||||
succeeded: "bg-success/15 text-success border border-success/30",
|
||||
error: "bg-destructive/15 text-destructive border border-destructive/30",
|
||||
terminated: "bg-destructive/15 text-destructive border border-destructive/30",
|
||||
pending: "bg-warning/15 text-warning border border-warning/30",
|
||||
|
||||
// Approval statuses
|
||||
pending_approval: "bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300",
|
||||
revision_requested: "bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300",
|
||||
approved: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
||||
rejected: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300",
|
||||
pending_approval: "bg-warning/15 text-warning border border-warning/30",
|
||||
revision_requested: "bg-warning/15 text-warning border border-warning/30",
|
||||
approved: "bg-success/15 text-success border border-success/30",
|
||||
rejected: "bg-destructive/15 text-destructive border border-destructive/30",
|
||||
|
||||
// Issue statuses — consistent hues with issueStatusIcon above
|
||||
backlog: "bg-muted text-muted-foreground",
|
||||
todo: "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300",
|
||||
in_progress: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300",
|
||||
in_review: "bg-violet-100 text-violet-700 dark:bg-violet-900/50 dark:text-violet-300",
|
||||
blocked: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300",
|
||||
done: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
||||
cancelled: "bg-muted text-muted-foreground",
|
||||
backlog: "bg-muted/60 text-muted-foreground border border-border",
|
||||
todo: "bg-muted/60 text-muted-foreground border border-border",
|
||||
in_progress: "bg-primary/15 text-primary border border-primary/30",
|
||||
in_review: "bg-warning/15 text-warning border border-warning/30",
|
||||
blocked: "bg-destructive/10 text-destructive border border-destructive/25",
|
||||
done: "bg-success/15 text-success border border-success/30",
|
||||
cancelled: "bg-muted/40 text-muted-foreground/70 border border-border",
|
||||
};
|
||||
|
||||
export const statusBadgeDefault = "bg-muted text-muted-foreground";
|
||||
export const statusBadgeDefault = "bg-muted text-muted-foreground border border-border";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Agent status dot — solid background for small indicator dots
|
||||
// [nexus] Migrated to design system tokens — see MIGRATION-PLAN.md
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const agentStatusDot: Record<string, string> = {
|
||||
running: "bg-cyan-400 animate-pulse",
|
||||
active: "bg-green-400",
|
||||
paused: "bg-yellow-400",
|
||||
idle: "bg-yellow-400",
|
||||
pending_approval: "bg-amber-400",
|
||||
error: "bg-red-400",
|
||||
archived: "bg-neutral-400",
|
||||
running: "bg-primary animate-pulse",
|
||||
active: "bg-success",
|
||||
paused: "bg-warning",
|
||||
idle: "bg-muted-foreground",
|
||||
pending_approval: "bg-warning",
|
||||
error: "bg-destructive",
|
||||
archived: "bg-muted-foreground/50",
|
||||
};
|
||||
|
||||
export const agentStatusDotDefault = "bg-neutral-400";
|
||||
export const agentStatusDotDefault = "bg-muted-foreground";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Priority colors
|
||||
// [nexus] Migrated to design system tokens — see MIGRATION-PLAN.md
|
||||
// urgent/critical → destructive, high → warning, medium → primary, low → muted
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const priorityColor: Record<string, string> = {
|
||||
critical: "text-red-600 dark:text-red-400",
|
||||
high: "text-orange-600 dark:text-orange-400",
|
||||
medium: "text-yellow-600 dark:text-yellow-400",
|
||||
low: "text-blue-600 dark:text-blue-400",
|
||||
critical: "text-destructive",
|
||||
high: "text-warning",
|
||||
medium: "text-primary",
|
||||
low: "text-muted-foreground",
|
||||
};
|
||||
|
||||
export const priorityColorDefault = "text-yellow-600 dark:text-yellow-400";
|
||||
export const priorityColorDefault = "text-muted-foreground";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue