diff --git a/changelogs/unreleased.md b/changelogs/unreleased.md index 135e0f541..a73260333 100644 --- a/changelogs/unreleased.md +++ b/changelogs/unreleased.md @@ -20,4 +20,4 @@ - Show execution mode, plugin names, and response ownership as timeline chips. Reuse profile runner labels and classify built-in Agent replies as system/core while preserving plugin senders. - Remove the outer gray trace card and tinted list shell, using a flat tab layout with toolbar/table separators in both themes. - Top hint and separate recording switch → a toolbar Start/Pause/Connecting button. Filters, recording, and refresh share gray icon-and-text styling and 36px input height; search fills available width and includes the message count in its placeholder. -- Use Vuetify datatable for the trace list, retaining fixed headers, stable trace row IDs, and explicit detail selection without adding pagination or automatic selection. +- Use Vuetify datatable with native rows, typography, spacing, and hover styling for the trace list; customize individual cells while retaining fixed headers, stable trace row IDs, keyboard activation, and explicit detail selection without adding pagination or automatic selection. diff --git a/dashboard/src/components/shared/TraceDisplayer.vue b/dashboard/src/components/shared/TraceDisplayer.vue index f454d30df..a33dee2e1 100644 --- a/dashboard/src/components/shared/TraceDisplayer.vue +++ b/dashboard/src/components/shared/TraceDisplayer.vue @@ -135,14 +135,14 @@ const traceRows = computed(() => filteredEvents.value.map((event) => ({ }))); const traceHeaders = computed(() => [ - { title: tm("columns.status"), key: "status", width: 100 }, - { title: tm("columns.platform"), key: "platform", width: 56 }, - { title: tm("columns.timestamp"), key: "timestamp", width: 132 }, - { title: tm("columns.duration"), key: "duration", width: 92 }, - { title: tm("columns.message"), key: "message", width: "26%" }, - { title: "UMO", key: "umo", width: "28%" }, - { title: tm("columns.sender"), key: "sender", width: 110 }, - { title: "Spans", key: "spans", width: 64 }, + { title: tm("columns.status"), key: "status", width: 120 }, + { title: tm("columns.platform"), key: "platform", width: 80 }, + { title: tm("columns.timestamp"), key: "timestamp", width: 160 }, + { title: tm("columns.duration"), key: "duration", width: 120 }, + { title: tm("columns.message"), key: "message", minWidth: 240 }, + { title: "UMO", key: "umo", minWidth: 240 }, + { title: tm("columns.sender"), key: "sender", minWidth: 120 }, + { title: "Spans", key: "spans", width: 80 }, ]); const selectedStats = computed(() => { @@ -593,44 +593,56 @@ onBeforeUnmount(() => { item-value="event.span_id" :items-per-page="-1" :mobile="false" - density="compact" + hover + :row-props="({ item }) => ({ + class: { 'trace-list-row': true, selected: item.event.span_id === selectedSpanId }, + 'data-trace-id': item.event.span_id, + 'aria-selected': item.event.span_id === selectedSpanId, + tabindex: 0, + onKeydown: (event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + selectEvent(item.event.span_id); + } + }, + })" + @click:row="(_, { item }) => selectEvent(item.event.span_id)" fixed-header height="100%" hide-default-footer disable-sort > -