From a8daa9988490502c49ba43814043cf407816eee6 Mon Sep 17 00:00:00 2001 From: TyperBody Date: Sun, 27 Sep 2026 02:39:09 +0800 Subject: [PATCH] fix(operation-trace): repair pageOf key placement, numbered pager, right column - pageOf was inserted under the wrong namespace (the locale files already had a hideDetails in toolCalls), so the UI rendered the raw key. Removed it and replaced the dropdown with a numbered pager (first/last always visible, a one-page window around the current page, gaps as an ellipsis). - Restore the right-hand column (duration + record hash) that the previous declutter removed; the digest is still opt-in, but the hash stays visible. - Hide the status code on success: a 200 is the default, so only >=400 shows. --- .../OperationTracePanel.tsx | 119 ++++++++++++------ web/src/i18n/locales/en-US.ts | 1 - web/src/i18n/locales/es-ES.ts | 1 - web/src/i18n/locales/ja-JP.ts | 1 - web/src/i18n/locales/ru-RU.ts | 1 - web/src/i18n/locales/th-TH.ts | 1 - web/src/i18n/locales/vi-VN.ts | 1 - web/src/i18n/locales/zh-Hans.ts | 1 - web/src/i18n/locales/zh-Hant.ts | 1 - 9 files changed, 80 insertions(+), 47 deletions(-) diff --git a/web/src/app/home/components/workspace-settings/OperationTracePanel.tsx b/web/src/app/home/components/workspace-settings/OperationTracePanel.tsx index f3ba224be..c2a4ab69d 100644 --- a/web/src/app/home/components/workspace-settings/OperationTracePanel.tsx +++ b/web/src/app/home/components/workspace-settings/OperationTracePanel.tsx @@ -17,6 +17,7 @@ import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Item, + ItemActions, ItemContent, ItemDescription, ItemMedia, @@ -139,6 +140,32 @@ function groupRecords(records: OperationLogRecord[]): GroupedRecord[] { return grouped; } +/** + * Build a compact page list: the first and last page are always shown, with a + * one-page window around the current page and gaps collapsed to an ellipsis. + * ``'…'`` entries are rendered as non-clickable separators. + */ +function pageWindow( + current: number, + count: number, + span = 1, +): (number | '…')[] { + if (count <= 0) return []; + const pages = new Set([1, count]); + for (let page = current - span; page <= current + span; page += 1) { + if (page >= 1 && page <= count) pages.add(page); + } + const sorted = [...pages].sort((a, b) => a - b); + const result: (number | '…')[] = []; + let previous = 0; + for (const page of sorted) { + if (previous && page - previous > 1) result.push('…'); + result.push(page); + previous = page; + } + return result; +} + /** Compact timestamp: time-only for today, date+time otherwise. */ function formatTimestamp(value: string | null): string { if (!value) return ''; @@ -744,7 +771,10 @@ export default function OperationTracePanel({ {record.http_method} - {record.status_code !== null + {/* A successful 2xx is the default; only surface the + status when it carries a signal (an error). */} + {record.status_code !== null && + record.status_code >= 400 ? ` ${record.status_code}` : ''} @@ -793,21 +823,29 @@ export default function OperationTracePanel({ redactedLabel={t('operationTrace.redacted')} /> ))} -
- {record.duration_ms}ms - {record.record_hash && ( - - - {record.record_hash.slice(0, 8)} - - )} -
)} + {/* Restore the always-visible right column: each row is + anchored by its duration and tamper-evidence hash, so the + digest stays one click away instead of being the only way + to see it. */} + +
+ + {record.duration_ms}ms + + {record.record_hash && ( + + + {record.record_hash.slice(0, 8)} + + )} +
+
); })} @@ -836,32 +874,35 @@ export default function OperationTracePanel({ > - {/* Jump straight to a page instead of only stepping one at a - time; with hundreds of records the arrows alone are painful. */} - + {/* Numbered pager: the first and last page stay visible with an + ellipsis between, so any page is one click away without the + arrows filling the bar with "Page x / y" rows. */} + {pageWindow(pageIndex, pageCount).map((item, index) => + item === '…' ? ( + + … + + ) : ( + + ), + )}