feat(ui): gallery feedback round 3 — one toggle, canonical agent chips, light-wash text sweep

Four user rulings (intentional visible changes; snapshots NOT
re-baselined):

1+2. Toggle unification: the app's only second switch implementation —
   ToggleField's hand-rolled h-5 w-9 pill with a bg-green-600 track in
   agent-config-primitives.tsx — now renders the canonical ToggleSwitch
   capsule (on = var(--status-task-done)); props/testid preserved. All
   other suspects already used ToggleSwitch; every switch now shares
   one form and one green.
3. Agent-status chips ('Org snippets and quick scan identity' section,
   StatusBadge): statusBadge agent keys route through brandChipBadge —
   running=blue, idle=gray (was yellow tint), paused=amber, and
   active=green (no canonical agent status; user-ruled mapping to the
   brand green family). brandChipBadge moved above statusBadge for
   declaration order. Org.tsx status dots route through agentStatusDot.
4. Dark-tuned-text-on-light-wash class of bug: flagged BudgetPolicyCard
   banner was already fixed in r2; systematic sweep fixed 58 more sites
   in 14 files (50->950, 100->900, 200->800, 300->700 light tiers with
   originals behind dark:, opacity suffixes preserved). Safe leftovers
   documented (InviteLanding dark page, solid-bg badges).

DECISION-SHEET.md: 'Gallery feedback round 3' section. Gates 3/3 CLEAN,
typecheck green, ui build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
scotttongandClaude Fable 5 committed 2026-07-06 19:10:00 -07:00
1 parent 3da1bbcc5a
commit d8ace306d3
18 files changed
+104 -106

No files matched your search

+6
View File
@@ -53,6 +53,12 @@ User rulings from the tune-session gallery review; all intentionally visible, sn
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).
## Gallery feedback round 3 (preset-tune session, Jul 6) — executed
1+2. **Toggle unification** — hunt found exactly ONE second switch implementation: `ToggleField` in agent-config-primitives.tsx (hand-rolled h-5 w-9 pill, `bg-green-600` track — the "other green"). It now renders the canonical `ToggleSwitch` (3da1bbcc5 capsule, on = `var(--status-task-done)`), same props/behavior/testid. All other named suspects (AgentConfigForm, Instance*Settings, RoutineDetail, PipelineSettings, story fixtures) already used ToggleSwitch; remaining `bg-green-600` hits are buttons, not tracks. Every switch now renders the one capsule + one green.
3. **Agent-status chips → canonical colors** ("Org snippets and quick scan identity" = StatusBadge in control-plane-surfaces story): `statusBadge` agent keys now route through `brandChipBadge` families (bordered brand chips): running → blue, idle → GRAY (was yellow tint), paused → amber, **active → green (no canonical agent status exists — user-ruled mapping to the brand green/done family)**; error already rides the shared run-status red. brandChipBadge block moved above statusBadge in status-colors.ts (declaration order). Org.tsx's hand-rolled status-dot ternary now routes through `agentStatusDot` (same hues + gains the blue running dot). Left: Companies.tsx company-status chip (company entity, not agent), AgentConfigForm "current" model tag (not a status).
4. **Dark-text-on-light-wash sweep** — the flagged BudgetPolicyCard banner was already fixed in round 2 (screenshot predated it). Systematic sweep (bare `text-{red,amber,emerald,sky,green,yellow,cyan}-{50..300}` without dark: protection): **58 sites fixed across 14 files** (Dashboard budget alert, BudgetPolicyCard remaining-amount, AgentConfigForm banners x3, AgentDetail banners/chips x9, ProjectDetail x3, CompanyAccess, IssueDocumentsSection x6, RoutineHistoryTab x11, DocumentDiffModal x2, PipelineItemBodyDocument x2, RoutineSaveBar, DocumentFrameHeader, OutputFileTile x3, CompanySkills x10) — pattern: 50→950, 100→900, 200→800, 300→700 in light + original behind `dark:`, opacity suffixes preserved. Verified-safe leftovers: InviteLanding x4 (dark-styled standalone page), SidebarNavItem badge + DocumentAnnotationLayer tail + DevRestartBanner (solid dark/colored bg or dark:-protected).
## Verification status (this review)
- `pnpm check:token-gates` — re-run independently: 3/3 CLEAN (468 files, 31 allowlist entries).
+3 -3
View File
@@ -1012,7 +1012,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
}}
/>
</Field>
<div className="rounded-md border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-xs text-amber-100">
<div className="rounded-md border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-xs text-amber-900 dark:text-amber-100">
Prompt template is replayed on every heartbeat. Keep it compact and dynamic to avoid recurring token cost and cache churn.
</div>
</>
@@ -1041,7 +1041,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
{kubernetesEnvironment.name} · Kubernetes sandbox
</div>
) : (
<div className="rounded-md border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-xs text-amber-200">
<div className="rounded-md border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-200">
This instance requires the Kubernetes sandbox, but no managed Kubernetes
environment is available for this company yet. Configure one before creating
agents; execution will not fall back to local.
@@ -1353,7 +1353,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
}}
/>
</Field>
<div className="rounded-md border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-xs text-amber-200">
<div className="rounded-md border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-200">
Bootstrap prompt is legacy and will be removed in a future release. Consider moving this content into the agent&apos;s prompt template or instructions file instead.
</div>
</>
+2 -2
View File
@@ -166,9 +166,9 @@ export function BudgetPolicyCard({
className={cn(
"inline-flex items-center gap-2 text-(length:--text-micro) uppercase tracking-(--tracking-caps)",
summary.status === "hard_stop"
? "text-red-300"
? "text-red-700 dark:text-red-300"
: summary.status === "warning"
? "text-amber-200"
? "text-amber-800 dark:text-amber-200"
: "text-muted-foreground",
)}
>
+2 -2
View File
@@ -73,8 +73,8 @@ export function DocumentDiffModal({
const lineClassesByKind: Record<DiffRow["kind"], string> = {
context: "bg-transparent",
removed: "bg-red-500/10 text-red-100",
added: "bg-green-500/10 text-green-100",
removed: "bg-red-500/10 text-red-900 dark:text-red-100",
added: "bg-green-500/10 text-green-900 dark:text-green-100",
};
const markerByKind: Record<DiffRow["kind"], string> = {
+1 -1
View File
@@ -92,7 +92,7 @@ export function DocumentFrameHeader({
size="sm"
className={cn(
"h-auto px-1.5 py-0 text-(length:--text-micro) font-normal text-muted-foreground hover:text-foreground",
revisionMenu.historicalPreview && "text-amber-300 hover:text-amber-200",
revisionMenu.historicalPreview && "text-amber-700 hover:text-amber-800 dark:text-amber-300 dark:hover:text-amber-200",
)}
>
rev {revisionMenu.displayedRevisionNumber}
+6 -6
View File
@@ -964,7 +964,7 @@ export function IssueDocumentsSection({
size="icon-xs"
className={cn(
"text-muted-foreground transition-colors",
isLocked && "text-amber-300 hover:text-amber-200",
isLocked && "text-amber-700 hover:text-amber-800 dark:text-amber-300 dark:hover:text-amber-200",
)}
title={isLocked ? "Unlock document" : "Lock document"}
aria-label={isLocked ? `Unlock ${doc.key} document` : `Lock ${doc.key} document`}
@@ -974,7 +974,7 @@ export function IssueDocumentsSection({
{isLocked ? <Lock className="h-3.5 w-3.5" /> : <Unlock className="h-3.5 w-3.5" />}
</Button>
) : isLocked ? (
<span title="Locked document" aria-label="Locked document" className="inline-flex h-6 w-6 items-center justify-center text-amber-300">
<span title="Locked document" aria-label="Locked document" className="inline-flex h-6 w-6 items-center justify-center text-amber-700 dark:text-amber-300">
<Lock className="h-3.5 w-3.5" />
</span>
) : null}
@@ -1063,7 +1063,7 @@ export function IssueDocumentsSection({
<div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-3">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1">
<p className="text-sm font-medium text-amber-200">
<p className="text-sm font-medium text-amber-800 dark:text-amber-200">
Viewing revision {selectedHistoricalRevision.revisionNumber}
</p>
<p className="text-xs text-muted-foreground">
@@ -1100,7 +1100,7 @@ export function IssueDocumentsSection({
<div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-3">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1">
<p className="text-sm font-medium text-amber-200">Out of date</p>
<p className="text-sm font-medium text-amber-800 dark:text-amber-200">Out of date</p>
<p className="text-xs text-muted-foreground">
This document changed while you were editing. Your local draft is preserved and autosave is paused.
</p>
@@ -1220,9 +1220,9 @@ export function IssueDocumentsSection({
<span
className={`text-(length:--text-micro) transition-opacity duration-150 ${
isHistoricalPreview
? "text-amber-300"
? "text-amber-700 dark:text-amber-300"
: activeConflict
? "text-amber-300"
? "text-amber-700 dark:text-amber-300"
: autosaveState === "error"
? "text-destructive"
: "text-muted-foreground"
@@ -297,7 +297,7 @@ export function PipelineItemBodyDocument({
<div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-3">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1">
<p className="text-sm font-medium text-amber-200">
<p className="text-sm font-medium text-amber-800 dark:text-amber-200">
Viewing revision {selectedHistoricalRevision.revisionNumber}
</p>
<p className="text-xs text-muted-foreground">
@@ -415,7 +415,7 @@ export function PipelineItemBodyDocument({
/>
) : null}
actionsSlot={editing ? (
<span className="text-(length:--text-micro) font-medium text-amber-300">● Editing · unsaved</span>
<span className="text-(length:--text-micro) font-medium text-amber-700 dark:text-amber-300">● Editing · unsaved</span>
) : (
<Button variant="ghost" size="sm" className="h-auto gap-1.5 px-2 py-1 text-xs" onClick={beginEdit}>
<FilePenLine className="h-3.5 w-3.5" />
+11 -11
View File
@@ -335,7 +335,7 @@ function HistoricalPreviewBanner({
<div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1">
<p className="text-sm font-medium text-amber-200">
<p className="text-sm font-medium text-amber-800 dark:text-amber-200">
Viewing revision {revisionNumber} (read-only)
</p>
<p className="text-xs text-muted-foreground">
@@ -374,7 +374,7 @@ function ConflictBanner({
<div className="rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1">
<p className="text-sm font-medium text-amber-200">Unsaved routine edits</p>
<p className="text-sm font-medium text-amber-800 dark:text-amber-200">Unsaved routine edits</p>
<p className="text-xs text-muted-foreground">
You changed {fieldsText} but haven&apos;t saved yet. Save or discard before previewing or
restoring an older revision.
@@ -465,7 +465,7 @@ function RevisionList({
</span>
)}
{revision.restoredFromRevisionId && (
<span className="rounded-full border border-amber-500/40 bg-amber-500/10 px-1.5 text-(length:--text-nano) uppercase tracking-(--tracking-eyebrow) text-amber-200">
<span className="rounded-full border border-amber-500/40 bg-amber-500/10 px-1.5 text-(length:--text-nano) uppercase tracking-(--tracking-eyebrow) text-amber-800 dark:text-amber-200">
Restored
</span>
)}
@@ -611,7 +611,7 @@ function RevisionPreview({
<p className="text-sm">
{row.value || <span className="text-muted-foreground">—</span>}
{row.differs && (
<span className="ml-2 rounded-full border border-amber-500/40 bg-amber-500/10 px-1.5 text-(length:--text-nano) uppercase tracking-(--tracking-eyebrow) text-amber-200">
<span className="ml-2 rounded-full border border-amber-500/40 bg-amber-500/10 px-1.5 text-(length:--text-nano) uppercase tracking-(--tracking-eyebrow) text-amber-800 dark:text-amber-200">
differs from current
</span>
)}
@@ -738,7 +738,7 @@ function RestoreConfirmDialog({
Previous run history is preserved.
</li>
{recreatedWebhookLabels.map((label) => (
<li key={label} className="flex items-start gap-2 text-amber-200">
<li key={label} className="flex items-start gap-2 text-amber-800 dark:text-amber-200">
<span className="mt-1 inline-block h-1.5 w-1.5 rounded-full bg-amber-400" />
The webhook trigger {label} will be recreated with a new URL and secret. Paperclip will
show the secret once after restore — copy it before closing.
@@ -858,10 +858,10 @@ function RoutineRevisionDiffModal({
{fieldChanges.map((change) => (
<tr key={change.field} className="border-t border-border/60">
<td className="px-3 py-2 align-top text-xs font-medium">{change.field}</td>
<td className="px-3 py-2 align-top text-xs text-red-300">
<td className="px-3 py-2 align-top text-xs text-red-700 dark:text-red-300">
{change.oldValue ?? "—"}
</td>
<td className="px-3 py-2 align-top text-xs text-emerald-300">
<td className="px-3 py-2 align-top text-xs text-emerald-700 dark:text-emerald-300">
{change.newValue ?? "—"}
</td>
</tr>
@@ -907,8 +907,8 @@ function RevisionPicker({
tone: "red" | "green";
}) {
const toneClass = tone === "red"
? "border-red-500/30 bg-red-500/10 text-red-300"
: "border-green-500/30 bg-green-500/10 text-green-300";
? "border-red-500/30 bg-red-500/10 text-red-700 dark:text-red-300"
: "border-green-500/30 bg-green-500/10 text-green-700 dark:text-green-300";
return (
<div className="flex items-center gap-2">
<span
@@ -941,8 +941,8 @@ function DiffTable({ rows }: { rows: DiffRow[] }) {
}
const lineClassesByKind: Record<DiffRow["kind"], string> = {
context: "bg-transparent",
removed: "bg-red-500/10 text-red-100",
added: "bg-green-500/10 text-green-100",
removed: "bg-red-500/10 text-red-900 dark:text-red-100",
added: "bg-green-500/10 text-green-900 dark:text-green-100",
};
const markerByKind: Record<DiffRow["kind"], string> = {
context: " ",
+1 -1
View File
@@ -79,7 +79,7 @@ export function RoutineSaveBar({
)}
>
{saveConflict ? (
<div className="flex items-center gap-2 text-sm text-amber-200">
<div className="flex items-center gap-2 text-sm text-amber-800 dark:text-amber-200">
<AlertTriangle className="h-4 w-4" />
<span>Routine changed elsewhere. Reload to merge.</span>
</div>
+7 -16
View File
@@ -116,23 +116,14 @@ export function ToggleField({
<span className="text-xs text-muted-foreground">{label}</span>
{hint && <HintIcon text={hint} />}
</div>
<button
data-slot="toggle"
{/* Gallery feedback r3: was a hand-rolled h-5 w-9 pill with a bg-green-600
track — the app's second switch implementation. Converged on the one
canonical ToggleSwitch (status-green on-state), DESIGN.md principle 1. */}
<ToggleSwitch
data-testid={toggleTestId}
type="button"
className={cn(
"relative inline-flex h-5 w-9 items-center rounded-full transition-colors",
checked ? "bg-green-600" : "bg-muted"
)}
onClick={() => onChange(!checked)}
>
<span
className={cn(
"inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform",
checked ? "translate-x-4.5" : "translate-x-0.5"
)}
/>
</button>
checked={checked}
onCheckedChange={onChange}
/>
</div>
);
}
@@ -3,9 +3,9 @@ import { getOutputFileGlyph, type OutputFileTone } from "@/lib/issue-output";
const TONE_CLASSES: Record<OutputFileTone, string> = {
video: "bg-indigo-500/15 text-indigo-300",
pdf: "bg-red-500/15 text-red-300",
zip: "bg-amber-500/15 text-amber-300",
image: "bg-emerald-500/15 text-emerald-300",
pdf: "bg-red-500/15 text-red-700 dark:text-red-300",
zip: "bg-amber-500/15 text-amber-700 dark:text-amber-300",
image: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-300",
bin: "bg-muted text-muted-foreground",
};
+29 -23
View File
@@ -42,17 +42,42 @@ export const issueStatusText: Record<string, string> = {
export const issueStatusTextDefault = "text-muted-foreground";
// ---------------------------------------------------------------------------
// Brand `.task-chip` status palette (PAP-75 / status-reference.html)
//
// Colour-named, 1px border, light + dark — values straight from paperclip.ing
// `brand.css`. Shared by the agents section (PAP-80) and the All Projects page
// (PAP-91); PAP-99 brings it to issue/task status chips, adding `violet` for
// `in_review`.
// ---------------------------------------------------------------------------
export type BrandChipColor = "gray" | "blue" | "amber" | "green" | "violet" | "red";
export const brandChipBadge: Record<BrandChipColor, string> = {
gray: "bg-[#F5F3F0] text-[#52585D] border-[#A8AEB2] dark:bg-[#6e696024] dark:text-[#9A958A] dark:border-[#9e958a73]",
blue: "bg-[#DBEAFE] text-[#1D4ED8] border-[#2563EB] dark:bg-[#2563eb2e] dark:text-[#2563EB] dark:border-[#2563eb73]",
amber: "bg-[#FEF3C7] text-[#B45309] border-[#F59E0B] dark:bg-[#f59e0b24] dark:text-[#F59E0B] dark:border-[#f59e0b73]",
green: "bg-[#DCFCE7] text-[#188A3C] border-[#22C55E] dark:bg-[#22c55e1f] dark:text-[#22C55E] dark:border-[#22c55e73]",
violet: "bg-[#EDE9FE] text-[#5B21B6] border-[#7C3AED] dark:bg-[#7c3aed2e] dark:text-[#7C3AED] dark:border-[#7c3aed73]",
red: "bg-[#FEE2E2] text-[#991B1B] border-[#DC2626] dark:bg-[#dc26262e] dark:text-[#DC2626] dark:border-[#dc262673]",
};
// ---------------------------------------------------------------------------
// Badge colors — used by StatusBadge for all entity types
// ---------------------------------------------------------------------------
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-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.
// Gallery feedback r3: agent chips route through the brand chip families
// (bordered .task-chip recipes) instead of ad-hoc tints. NOTE: `active` has
// no canonical agent status — user-ruled mapping to the brand GREEN family;
// `idle` is the gray family (was a yellow/amber tint); `error` rides the
// shared run-status red entry below.
active: `border ${brandChipBadge.green}`,
running: `border ${brandChipBadge.blue}`, // r1 made this blue; r3 routes through brandChipBadge.blue.
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",
paused: `border ${brandChipBadge.amber}`,
idle: `border ${brandChipBadge.gray}`,
archived: "bg-muted text-muted-foreground",
// Goal statuses
@@ -126,25 +151,6 @@ export const agentStatusMotion: Record<string, string> = {
error: "hb-blink",
};
// ---------------------------------------------------------------------------
// Brand `.task-chip` status palette (PAP-75 / status-reference.html)
//
// Colour-named, 1px border, light + dark — values straight from paperclip.ing
// `brand.css`. Shared by the agents section (PAP-80) and the All Projects page
// (PAP-91); PAP-99 brings it to issue/task status chips, adding `violet` for
// `in_review`.
// ---------------------------------------------------------------------------
export type BrandChipColor = "gray" | "blue" | "amber" | "green" | "violet" | "red";
export const brandChipBadge: Record<BrandChipColor, string> = {
gray: "bg-[#F5F3F0] text-[#52585D] border-[#A8AEB2] dark:bg-[#6e696024] dark:text-[#9A958A] dark:border-[#9e958a73]",
blue: "bg-[#DBEAFE] text-[#1D4ED8] border-[#2563EB] dark:bg-[#2563eb2e] dark:text-[#2563EB] dark:border-[#2563eb73]",
amber: "bg-[#FEF3C7] text-[#B45309] border-[#F59E0B] dark:bg-[#f59e0b24] dark:text-[#F59E0B] dark:border-[#f59e0b73]",
green: "bg-[#DCFCE7] text-[#188A3C] border-[#22C55E] dark:bg-[#22c55e1f] dark:text-[#22C55E] dark:border-[#22c55e73]",
violet: "bg-[#EDE9FE] text-[#5B21B6] border-[#7C3AED] dark:bg-[#7c3aed2e] dark:text-[#7C3AED] dark:border-[#7c3aed73]",
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
+9 -9
View File
@@ -963,7 +963,7 @@ export function AgentDetail() {
return (
<div className={cn("space-y-6", isMobile && showConfigActionBar && "pb-24")}>
{showLeftAgentNotice ? (
<div className="flex items-center gap-3 border border-yellow-300/35 bg-yellow-300/10 px-3 py-2 text-sm text-yellow-100">
<div className="flex items-center gap-3 border border-yellow-300/35 bg-yellow-300/10 px-3 py-2 text-sm text-yellow-900 dark:text-yellow-100">
<p className="min-w-0 flex-1">
You left this agent. It no longer appears in your sidebar.
</p>
@@ -988,7 +988,7 @@ export function AgentDetail() {
/>
<button
type="button"
className="h-6 w-6 shrink-0 text-yellow-100/70 hover:text-yellow-100"
className="h-6 w-6 shrink-0 text-yellow-900/70 hover:text-yellow-900 dark:text-yellow-100/70 dark:hover:text-yellow-100"
aria-label="Dismiss agent membership notice"
onClick={() => setDismissedLeftAgentIds((current) => new Set(current).add(agent.id))}
>
@@ -997,20 +997,20 @@ export function AgentDetail() {
</div>
) : null}
{hasInvalidOrgChain ? (
<div className="flex items-start gap-3 border border-amber-300/35 bg-amber-300/10 px-3 py-2 text-sm text-amber-100">
<div className="flex items-start gap-3 border border-amber-300/35 bg-amber-300/10 px-3 py-2 text-sm text-amber-900 dark:text-amber-100">
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
<div className="min-w-0 space-y-1">
<p className="font-medium">Invalid reporting chain</p>
<p className="text-amber-100/90">
<p className="text-amber-900/90 dark:text-amber-100/90">
{agent.name} cannot accept tasks or start runs until its reporting chain is repaired.
</p>
<p className="break-words font-mono text-xs text-amber-100/80">
<p className="break-words font-mono text-xs text-amber-900/80 dark:text-amber-100/80">
{formatOrgChainHealthPath(agent)}
</p>
{agent.orgChainHealth?.repairGuidance ? (
<p className="text-amber-100/85">{agent.orgChainHealth.repairGuidance}</p>
<p className="text-amber-900/85 dark:text-amber-100/85">{agent.orgChainHealth.repairGuidance}</p>
) : (
<p className="text-amber-100/85">
<p className="text-amber-900/85 dark:text-amber-100/85">
Assign this agent to an active manager/root, or explicitly pause or terminate the affected agent/subtree.
</p>
)}
@@ -2156,7 +2156,7 @@ function PromptsTab({
{(bundle?.warnings ?? []).length > 0 && (
<div className="space-y-2">
{(bundle?.warnings ?? []).map((warning) => (
<div key={warning} className="rounded-md border border-sky-500/25 bg-sky-500/10 px-3 py-2 text-xs text-sky-100">
<div key={warning} className="rounded-md border border-sky-500/25 bg-sky-500/10 px-3 py-2 text-xs text-sky-900 dark:text-sky-100">
{warning}
</div>
))}
@@ -2435,7 +2435,7 @@ function PromptsTab({
return (
<Tooltip>
<TooltipTrigger asChild>
<span className="ml-3 shrink-0 rounded border border-amber-500/40 bg-amber-500/10 text-amber-200 px-1.5 py-0.5 text-(length:--text-nano) uppercase tracking-wide cursor-help">
<span className="ml-3 shrink-0 rounded border border-amber-500/40 bg-amber-500/10 text-amber-800 dark:text-amber-200 px-1.5 py-0.5 text-(length:--text-nano) uppercase tracking-wide cursor-help">
virtual file
</span>
</TooltipTrigger>
+1 -1
View File
@@ -249,7 +249,7 @@ export function CompanyAccess() {
</div>
{access && !access.currentUserRole && (
<div className="rounded-xl border border-amber-500/40 px-4 py-3 text-sm text-amber-200">
<div className="rounded-xl border border-amber-500/40 px-4 py-3 text-sm text-amber-800 dark:text-amber-200">
This account can manage access here through instance-admin privileges, but it does not currently hold an active company membership.
</div>
)}
+10 -10
View File
@@ -422,13 +422,13 @@ function TrustChip({ level }: { level: CompanySkillTrustLevel }) {
icon: Folder,
label: "Includes assets",
tooltip: "Ships images, fonts, or other non-script files.",
className: "border-cyan-500/30 bg-cyan-500/10 text-cyan-200",
className: "border-cyan-500/30 bg-cyan-500/10 text-cyan-800 dark:text-cyan-200",
},
scripts_executables: {
icon: AlertTriangle,
label: "Includes scripts",
tooltip: "Ships executable scripts. Review before installing.",
className: "border-amber-500/40 bg-amber-500/10 text-amber-200",
className: "border-amber-500/40 bg-amber-500/10 text-amber-800 dark:text-amber-200",
},
} as const;
const config = map[level] ?? map.markdown_only;
@@ -453,7 +453,7 @@ function CompatChip({ compatibility }: { compatibility: CompanySkillCompatibilit
icon: HelpCircle,
label: "Unknown format",
tooltip: "Paperclip could not validate this skill as Agent Skills markdown. Install at your own risk.",
className: "border-yellow-500/40 bg-yellow-500/10 text-yellow-200",
className: "border-yellow-500/40 bg-yellow-500/10 text-yellow-800 dark:text-yellow-200",
},
invalid: {
icon: XOctagon,
@@ -864,7 +864,7 @@ function SkillCard({ card, onOpen }: { card: DiscoveryCard; onOpen: (card: Disco
</div>
<div className="mt-2 flex flex-wrap items-center gap-1">
{card.installed ? (
<span className="inline-flex items-center rounded-full border border-emerald-500/30 bg-emerald-500/10 px-2 py-0.5 text-(length:--text-nano) text-emerald-300">
<span className="inline-flex items-center rounded-full border border-emerald-500/30 bg-emerald-500/10 px-2 py-0.5 text-(length:--text-nano) text-emerald-700 dark:text-emerald-300">
Installed
</span>
) : null}
@@ -1727,7 +1727,7 @@ function CatalogDetailPane({
);
} else if (hashOutOfSync) {
cta = (
<Button onClick={onUpdate} disabled={loadingPrimaryAction} className="border-amber-500/40 bg-amber-500/20 text-amber-100 hover:bg-amber-500/30">
<Button onClick={onUpdate} disabled={loadingPrimaryAction} className="border-amber-500/40 bg-amber-500/20 text-amber-900 dark:text-amber-100 hover:bg-amber-500/30">
<ArrowUpCircle className="mr-1.5 h-3.5 w-3.5" />
Update from catalog
</Button>
@@ -1770,7 +1770,7 @@ function CatalogDetailPane({
{hashOutOfSync ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="inline-flex items-center gap-1 rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-(length:--text-micro) text-amber-200">
<span className="inline-flex items-center gap-1 rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-(length:--text-micro) text-amber-800 dark:text-amber-200">
<ArrowUpCircle className="h-3 w-3" aria-hidden="true" />
Update available
</span>
@@ -1908,7 +1908,7 @@ function InstallPreviewDialog({
{skill.trustLevel === "markdown_only" ? (
<span className="text-muted-foreground">Safe</span>
) : skill.trustLevel === "scripts_executables" ? (
<span className="text-amber-200">Review required</span>
<span className="text-amber-800 dark:text-amber-200">Review required</span>
) : (
<span className="text-muted-foreground">Non-script assets</span>
)}
@@ -1952,7 +1952,7 @@ function InstallPreviewDialog({
</div>
{conflict ? (
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-xs text-amber-200">
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-xs text-amber-800 dark:text-amber-200">
An existing skill with key <span className="font-mono">{conflict.key}</span> is installed (
{conflict.sourceLabel ?? conflict.sourceType}). Installing will {defaultAction === "update" ? "overwrite the catalog content" : "replace the existing skill"}.
</div>
@@ -2456,8 +2456,8 @@ function SkillVersionDiffDialog({
);
const lineClassesByKind: Record<DiffRow["kind"], string> = {
context: "bg-transparent",
removed: "bg-red-500/10 text-red-100",
added: "bg-green-500/10 text-green-100",
removed: "bg-red-500/10 text-red-900 dark:text-red-100",
added: "bg-green-500/10 text-green-900 dark:text-green-100",
};
const markerByKind: Record<DiffRow["kind"], string> = {
context: " ",
+4 -4
View File
@@ -221,17 +221,17 @@ export function Dashboard() {
{data.budgets.activeIncidents > 0 ? (
<div className="flex items-start justify-between gap-3 rounded-xl border border-red-500/20 bg-(image:--gradient-extract-1) px-4 py-3">
<div className="flex items-start gap-2.5">
<PauseCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-300" />
<PauseCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-700 dark:text-red-300" />
<div>
<p className="text-sm font-medium text-red-50">
<p className="text-sm font-medium text-red-950 dark:text-red-50">
{data.budgets.activeIncidents} active budget incident{data.budgets.activeIncidents === 1 ? "" : "s"}
</p>
<p className="text-xs text-red-100/70">
<p className="text-xs text-red-900/70 dark:text-red-100/70">
{data.budgets.pausedAgents} agents paused · {data.budgets.pausedProjects} projects paused · {data.budgets.pendingApprovals} pending budget approvals
</p>
</div>
</div>
<Link to="/costs" className="text-sm underline underline-offset-2 text-red-100">
<Link to="/costs" className="text-sm underline underline-offset-2 text-red-900 dark:text-red-100">
Open budgets
</Link>
</div>
+4 -9
View File
@@ -10,6 +10,7 @@ import { EmptyState } from "../components/EmptyState";
import { PageSkeleton } from "../components/PageSkeleton";
import { ChevronRight, GitBranch } from "lucide-react";
import { cn } from "../lib/utils";
import { agentStatusDot, agentStatusDotDefault } from "../lib/status-colors";
function OrgTree({
nodes,
@@ -67,15 +68,9 @@ function OrgTreeNode({
<span
className={cn(
"h-2 w-2 rounded-full shrink-0",
node.status === "active"
? "bg-green-400"
: node.status === "paused"
? "bg-yellow-400"
: node.status === "pending_approval"
? "bg-amber-400"
: node.status === "error"
? "bg-red-400"
: "bg-neutral-400"
// Gallery feedback r3: route through the canonical agentStatusDot
// map (identical hues for existing keys; adds the blue running dot).
agentStatusDot[node.status] ?? agentStatusDotDefault,
)}
/>
<span className="font-medium flex-1">{node.name}</span>
+3 -3
View File
@@ -729,7 +729,7 @@ export function ProjectDetail() {
return (
<div className="space-y-6">
{showLeftProjectNotice ? (
<div className="flex items-center gap-3 border border-yellow-300/35 bg-yellow-300/10 px-3 py-2 text-sm text-yellow-100">
<div className="flex items-center gap-3 border border-yellow-300/35 bg-yellow-300/10 px-3 py-2 text-sm text-yellow-900 dark:text-yellow-100">
<p className="min-w-0 flex-1">
You left this project. It no longer appears in your sidebar.
</p>
@@ -754,7 +754,7 @@ export function ProjectDetail() {
/>
<button
type="button"
className="h-6 w-6 shrink-0 text-yellow-100/70 hover:text-yellow-100"
className="h-6 w-6 shrink-0 text-yellow-900/70 hover:text-yellow-900 dark:text-yellow-100/70 dark:hover:text-yellow-100"
aria-label="Dismiss project membership notice"
onClick={() => setDismissedLeftProjectIds((current) => new Set(current).add(project.id))}
>
@@ -779,7 +779,7 @@ export function ProjectDetail() {
className="text-xl font-bold"
/>
{project.pauseReason === "budget" ? (
<div className="inline-flex items-center gap-2 rounded-full border border-red-500/30 bg-red-500/10 px-3 py-1 text-(length:--text-micro) font-medium uppercase tracking-(--tracking-caps) text-red-200">
<div className="inline-flex items-center gap-2 rounded-full border border-red-500/30 bg-red-500/10 px-3 py-1 text-(length:--text-micro) font-medium uppercase tracking-(--tracking-caps) text-red-800 dark:text-red-200">
<span className="h-2 w-2 rounded-full bg-red-400" />
Paused by budget hard stop
</div>