From d2f9ade30c1ea9ba2ae36b458e86d331820b75bb Mon Sep 17 00:00:00 2001 From: Forgotten Date: Mon, 23 Feb 2026 19:44:02 -0600 Subject: [PATCH] fix(ui): mobile viewport, scrollable popovers, and actor labels - Set viewport-fit=cover and disable user scaling for mobile PWA feel - Wrap assignee/project popover lists in scrollable containers - Remove rounded-t-sm from stacked chart bars for cleaner rendering - Prevent filter bar icons from shrinking on narrow screens - Show "Board" instead of raw user IDs in activity feeds and toasts - Surface server error message in health API failures Co-Authored-By: Claude Opus 4.6 --- ui/index.html | 2 +- ui/src/api/health.ts | 3 +- ui/src/components/ActivityRow.tsx | 3 +- ui/src/components/IssueProperties.tsx | 100 +++++++++++++------------ ui/src/components/IssuesList.tsx | 2 +- ui/src/components/NewIssueDialog.tsx | 88 +++++++++++----------- ui/src/context/LiveUpdatesProvider.tsx | 15 +--- ui/src/pages/IssueDetail.tsx | 3 +- 8 files changed, 107 insertions(+), 109 deletions(-) diff --git a/ui/index.html b/ui/index.html index 8f0a47e3..3a033cb4 100644 --- a/ui/index.html +++ b/ui/index.html @@ -2,7 +2,7 @@ - + Paperclip diff --git a/ui/src/api/health.ts b/ui/src/api/health.ts index 272896d3..991ef946 100644 --- a/ui/src/api/health.ts +++ b/ui/src/api/health.ts @@ -13,7 +13,8 @@ export const healthApi = { headers: { Accept: "application/json" }, }); if (!res.ok) { - throw new Error(`Failed to load health (${res.status})`); + const payload = await res.json().catch(() => null) as { error?: string } | null; + throw new Error(payload?.error ?? `Failed to load health (${res.status})`); } return res.json(); }, diff --git a/ui/src/components/ActivityRow.tsx b/ui/src/components/ActivityRow.tsx index 68fcc9ca..35c55f0e 100644 --- a/ui/src/components/ActivityRow.tsx +++ b/ui/src/components/ActivityRow.tsx @@ -101,12 +101,13 @@ export function ActivityRow({ event, agentMap, entityNameMap, className }: Activ : entityLink(event.entityType, event.entityId, name); const actor = event.actorType === "agent" ? agentMap.get(event.actorId) : null; + const actorName = actor?.name ?? (event.actorType === "system" ? "System" : event.actorType === "user" ? "Board" : event.actorId || "Unknown"); const inner = (

diff --git a/ui/src/components/IssueProperties.tsx b/ui/src/components/IssueProperties.tsx index efa0825e..ffc3d7d6 100644 --- a/ui/src/components/IssueProperties.tsx +++ b/ui/src/components/IssueProperties.tsx @@ -106,35 +106,37 @@ export function IssueProperties({ issue, onUpdate }: IssuePropertiesProps) { onChange={(e) => setAssigneeSearch(e.target.value)} autoFocus /> - - {(agents ?? []) - .filter((a) => a.status !== "terminated") - .filter((a) => { - if (!assigneeSearch.trim()) return true; - const q = assigneeSearch.toLowerCase(); - return a.name.toLowerCase().includes(q); - }) - .map((a) => ( +

- ))} + {(agents ?? []) + .filter((a) => a.status !== "terminated") + .filter((a) => { + if (!assigneeSearch.trim()) return true; + const q = assigneeSearch.toLowerCase(); + return a.name.toLowerCase().includes(q); + }) + .map((a) => ( + + ))} +
{issue.assigneeAgentId && ( @@ -176,37 +178,39 @@ export function IssueProperties({ issue, onUpdate }: IssuePropertiesProps) { onChange={(e) => setProjectSearch(e.target.value)} autoFocus /> - - {(projects ?? []) - .filter((p) => { - if (!projectSearch.trim()) return true; - const q = projectSearch.toLowerCase(); - return p.name.toLowerCase().includes(q); - }) - .map((p) => ( +
- ))} + {(projects ?? []) + .filter((p) => { + if (!projectSearch.trim()) return true; + const q = projectSearch.toLowerCase(); + return p.name.toLowerCase().includes(q); + }) + .map((p) => ( + + ))} +
{issue.projectId && ( diff --git a/ui/src/components/IssuesList.tsx b/ui/src/components/IssuesList.tsx index b62698f1..c99c9b7c 100644 --- a/ui/src/components/IssuesList.tsx +++ b/ui/src/components/IssuesList.tsx @@ -214,7 +214,7 @@ export function IssuesList({ New Issue -
+
{/* Filter */} diff --git a/ui/src/components/NewIssueDialog.tsx b/ui/src/components/NewIssueDialog.tsx index 1649d882..123802cd 100644 --- a/ui/src/components/NewIssueDialog.tsx +++ b/ui/src/components/NewIssueDialog.tsx @@ -396,35 +396,37 @@ export function NewIssueDialog() { onChange={(e) => setAssigneeSearch(e.target.value)} autoFocus /> - - {(agents ?? []) - .filter((a) => a.status !== "terminated") - .filter((a) => { - if (!assigneeSearch.trim()) return true; - const q = assigneeSearch.toLowerCase(); - return a.name.toLowerCase().includes(q); - }) - .map((a) => ( +
- ))} + {(agents ?? []) + .filter((a) => a.status !== "terminated") + .filter((a) => { + if (!assigneeSearch.trim()) return true; + const q = assigneeSearch.toLowerCase(); + return a.name.toLowerCase().includes(q); + }) + .map((a) => ( + + ))} +
@@ -449,31 +451,33 @@ export function NewIssueDialog() { - - {(projects ?? []).map((p) => ( +
- ))} + {(projects ?? []).map((p) => ( + + ))} +
diff --git a/ui/src/context/LiveUpdatesProvider.tsx b/ui/src/context/LiveUpdatesProvider.tsx index 19bd0fbc..9a271feb 100644 --- a/ui/src/context/LiveUpdatesProvider.tsx +++ b/ui/src/context/LiveUpdatesProvider.tsx @@ -39,18 +39,6 @@ function truncate(text: string, max: number): string { return text.slice(0, max - 1) + "\u2026"; } -function looksLikeUuid(value: string): boolean { - return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value); -} - -function titleCase(value: string): string { - return value - .split(" ") - .filter((part) => part.length > 0) - .map((part) => part[0]!.toUpperCase() + part.slice(1)) - .join(" "); -} - function resolveActorLabel( queryClient: QueryClient, companyId: string, @@ -62,8 +50,7 @@ function resolveActorLabel( } if (actorType === "system") return "System"; if (actorType === "user" && actorId) { - if (looksLikeUuid(actorId)) return `User ${shortId(actorId)}`; - return titleCase(actorId.replace(/[_-]+/g, " ")); + return "Board"; } return "Someone"; } diff --git a/ui/src/pages/IssueDetail.tsx b/ui/src/pages/IssueDetail.tsx index e88f3557..043909b8 100644 --- a/ui/src/pages/IssueDetail.tsx +++ b/ui/src/pages/IssueDetail.tsx @@ -110,7 +110,8 @@ function ActorIdentity({ evt, agentMap }: { evt: ActivityEvent; agentMap: Map; } if (evt.actorType === "system") return ; - return ; + if (evt.actorType === "user") return ; + return ; } export function IssueDetail() {