From 634ae1298fca40d8755060b8d505460db73bdcb5 Mon Sep 17 00:00:00 2001 From: scotttong Date: Mon, 13 Jul 2026 08:53:03 -0700 Subject: [PATCH] fix(ui): consolidate live/running blues; stop inbox unread badge indenting the row (#9383) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The web UI leans on a shared design-token + component system so surfaces stay visually consistent as they grow > - Two small inconsistencies had crept in: several distinct blues were used to signal "live/running" agent state across the sidebar, task header, and chat thread; and in the Inbox an unread task's mark-read dot was pushing that row's status icon and title one column right of read rows > - Both read as "not quite aligned" in daily use and undercut the polish of the lists work that just landed > - This pull request consolidates the live/running blues onto one shared recipe and stops the unread dot from indenting the row > - The benefit is one consistent "live" blue everywhere and Inbox rows that line up whether read or unread ## Linked Issues or Issue Description No public GitHub issue exists for this work; describing inline per the bug-report template. - **Problem**: (1) the same concept — an agent actively working — rendered in three visibly different blues: the sidebar `N live` dot, the task-detail "Live" badge, and the chat-thread "RUNNING" badge each used a different token/recipe. (2) In the Inbox, unread rows carry a leading mark-read dot that occupies the chevron column, but a per-row spacer was still rendering in that same column — so on unread rows the status icon + title were shifted one column (~24px) further right than read rows. Most visible when grouped by workspace. - **Steps to reproduce**: open the Inbox with a mix of read and unread tasks (group by workspace). The unread rows' status icons sit further right than the read rows'. Separately, compare the blue of the sidebar `N live` dot, a task's "Live" header badge, and a chat "RUNNING" badge — they don't match. - **Expected behavior**: unread and read rows align on the same status column, with the unread dot centered on the workspace group chevron; and all three "live/running" affordances share one blue. ## What Changed - Added a shared `liveBlueBadge` recipe in `ui/src/lib/status-colors.ts` and pointed the task-detail **Live** badge (`IssueDetail.tsx`) and the chat-thread **RUNNING** badge (`IssueChatThread.tsx`) at it; removed the now-redundant `brandChipBadge` usage from the chat thread and a stray `🔵` breadcrumb prefix. - Changed the sidebar **`N live`** dot (`SidebarNavItem.tsx`) to the same `blue-600 / dark:blue-400` as its adjacent label text. - **Inbox** (`Inbox.tsx`): skip the per-row leading spacer when the unread mark-read dot is present, so the dot alone fills the chevron column. Unread rows' status icon + title now sit in the same column as read rows, and the dot centers on the workspace group chevron. - **Test** (`Inbox.test.tsx`): added a regression test asserting an unread leaf row renders the mark-read dot and drops the spacer, while a read row keeps the spacer. ## Verification - `pnpm typecheck` — clean (all packages) - `pnpm check:token-gates` — 3/3 CLEAN - `cd ui && pnpm vitest run src/pages/Inbox.test.tsx` — 14/14 (includes the new regression test) - Full Storybook visual suite (514 stories, both themes) — green locally (CI cannot run this suite yet — the baseline-manifest archive is unpublished, a pre-existing condition from #9134) - Manual (workspace-grouped Inbox, 2× dark): measured the unread badge center at the same x as the workspace chevron (276 = 276) and the unread-row status icon at the same x as read-row status icons (292 = 292). Before/after screenshots in a PR comment below. ## Risks Low risk — presentation only. No data, routing, or state changes. The blue consolidation is a token/class swap; the Inbox change removes a redundant spacer element on unread rows only (read rows and non-grouped/mobile views are unaffected). The unread-row behavior is covered by the new unit test. ## Model Used Claude (Anthropic), Opus 4.8 — model id `claude-opus-4-8`; extended thinking + tool use, driving local verification (typecheck, token gates, vitest, Playwright visual suite + pixel measurements). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Claude Fable 5 --- ui/src/components/IssueChatThread.tsx | 8 ++-- ui/src/components/SidebarNavItem.tsx | 8 ++-- ui/src/lib/status-colors.ts | 7 +++ ui/src/pages/Inbox.test.tsx | 62 +++++++++++++++++++++++++++ ui/src/pages/Inbox.tsx | 6 +++ ui/src/pages/IssueDetail.tsx | 3 +- 6 files changed, 85 insertions(+), 9 deletions(-) diff --git a/ui/src/components/IssueChatThread.tsx b/ui/src/components/IssueChatThread.tsx index c7a954d41c..aed1de750d 100644 --- a/ui/src/components/IssueChatThread.tsx +++ b/ui/src/components/IssueChatThread.tsx @@ -165,7 +165,7 @@ import { } from "../lib/transcriptPresentation"; import { buildAgentMentionHref } from "@paperclipai/shared"; import { cn, formatDateTime, formatShortDate } from "../lib/utils"; -import { brandChipBadge } from "../lib/status-colors"; +import { liveBlueBadge } 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"; @@ -1928,12 +1928,12 @@ function IssueChatAssistantMessage({ ) : null} {isRunning ? ( - // Gallery feedback r1: running chip uses the canonical brand blue - // (brandChipBadge.blue), not cyan; layout/size classes unchanged. + // Running chip shares the liveness-blue badge recipe with the + // issue header's "Live" badge (one live/running blue). diff --git a/ui/src/components/SidebarNavItem.tsx b/ui/src/components/SidebarNavItem.tsx index b9856e2ad7..a9603af7ab 100644 --- a/ui/src/components/SidebarNavItem.tsx +++ b/ui/src/components/SidebarNavItem.tsx @@ -144,8 +144,8 @@ export function SidebarNavItem({ icon. The icon markup is untouched so it stays pixel-aligned. */} {rail && !alert && hasLive && (