From 38e0fc8ea04befe0364840cc1f56ba297b76ff86 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Sun, 3 May 2026 20:12:21 -0700 Subject: [PATCH] canvas/TracesTab: semantic status dots + aria-expanded on row expanders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small UIUX fixes for the workspace Traces tab — same pattern shipped on EventsTab. 1. Status dots were hardcoded bg-red-400 / bg-emerald-400 — semantic- token misses. Switched to bg-bad / bg-good so they pin to the canvas-wide ramp instead of Tailwind raw tones. 2. Trace expander rows had no aria-expanded — SR users heard a generic "button" with no toggle indication. Added aria-expanded + aria-controls pointing to the detail panel id. 3. Refresh + each expander button now carry focus-visible:ring-accent so keyboard users see where focus lands. Both were hover-only before. Co-Authored-By: Claude Opus 4.7 (1M context) --- canvas/src/components/tabs/TracesTab.tsx | 139 +++++++++++++---------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/canvas/src/components/tabs/TracesTab.tsx b/canvas/src/components/tabs/TracesTab.tsx index 17668566..aa6bd650 100644 --- a/canvas/src/components/tabs/TracesTab.tsx +++ b/canvas/src/components/tabs/TracesTab.tsx @@ -55,7 +55,13 @@ export function TracesTab({ workspaceId }: Props) {
{traces.length} traces -
@@ -79,66 +85,79 @@ export function TracesTab({ workspaceId }: Props) {
) : (
- {traces.map((trace) => ( -
- - - {expanded === trace.id && ( -
- {trace.input && ( -
-
Input
-
-                        {String(typeof trace.input === "string" ? trace.input : JSON.stringify(trace.input, null, 2))}
-                      
-
- )} - {trace.output && ( -
-
Output
-
-                        {String(typeof trace.output === "string" ? trace.output : JSON.stringify(trace.output, null, 2))}
-                      
-
- )} - {trace.totalCost != null && ( -
- Cost: ${trace.totalCost.toFixed(6)} -
- )} -
- {trace.id} + {traces.map((trace) => { + const isOpen = expanded === trace.id; + const panelId = `trace-detail-${trace.id}`; + return ( +
+
- ))} +
+ {trace.latency != null && ( + + {trace.latency > 1000 ? `${(trace.latency / 1000).toFixed(1)}s` : `${trace.latency}ms`} + + )} + {trace.usage?.total != null && ( + + {trace.usage.total} tok + + )} + +
+ + + {isOpen && ( +
+ {trace.input && ( +
+
Input
+
+                          {String(typeof trace.input === "string" ? trace.input : JSON.stringify(trace.input, null, 2))}
+                        
+
+ )} + {trace.output && ( +
+
Output
+
+                          {String(typeof trace.output === "string" ? trace.output : JSON.stringify(trace.output, null, 2))}
+                        
+
+ )} + {trace.totalCost != null && ( +
+ Cost: ${trace.totalCost.toFixed(6)} +
+ )} +
+ {trace.id} +
+
+ )} +
+ ); + })}
)}