dsh-token-total 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +53 -107
- package/package.json +1 -1
- package/src/client/SessionUsageView.module.css +19 -17
- package/src/client/SessionUsageView.tsx +17 -22
package/lib/client.js
CHANGED
|
@@ -32876,7 +32876,7 @@ window.__ModuleLoader__.load({
|
|
|
32876
32876
|
}
|
|
32877
32877
|
//#endregion
|
|
32878
32878
|
//#region \0token-total-css:/home/runner/work/dsh-token-total/dsh-token-total/src/client/SessionUsageView.module.css?inline
|
|
32879
|
-
const css = "/* A light, self-contained Usage surface. Tables fit the available width:\n * no horizontal scroll containers, fixed column contracts, titles for names.\n * Stat cards follow the reference dashboard: tinted left edge + icon chip,\n * label, big value, small subtitle. Request log folds by conversation turn. */\n.scroll_900471699 { height:100%; min-height:0; overflow-y:auto; background:#ffffff; color:#252936; color-scheme:light; container-type:inline-size; }\n.page_900471699 { box-sizing:border-box; width:100%; max-width:1160px; margin:0 auto; padding:24px 26px 32px; font-family:system-ui,-apple-system,\"Segoe UI\",sans-serif; font-size:13px; }\n.page_900471699 * { box-sizing:border-box; }\n/* Freshness header: live badge + last-read stamp on the left, manual refresh\n * pinned to the right edge. */\n.head_900471699 { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:0 0 12px; }\n.headStatus_900471699 { display:flex; align-items:center; gap:7px; min-width:0; font-size:11px; color:#8790a3; }\n.liveDot_900471699 { width:6px; height:6px; border-radius:50%; background:#18a97b; box-shadow:0 0 0 3px #e5f6f0; flex-shrink:0; }\n.asOf_900471699 { color:#9aa3b3; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.refresh_900471699 { display:inline-flex; align-items:center; gap:5px; flex:none; color:#6f7a8d; background:#fff; border:1px solid #e4e7ee; border-radius:8px; padding:5px 11px; font:inherit; font-size:11px; cursor:pointer; }\n.refresh_900471699:hover { color:#279cb6; border-color:#cbd3e0; }\n.refresh_900471699:disabled { opacity:.55; cursor:default; }\n.scope_900471699 { color:#8790a3; font-size:11px; margin:0 0 14px; }\n.stats_900471699 { display:grid; gap:12px; margin-bottom:18px; }\n.statRow_900471699 { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; }\n.stat_900471699 { min-width:0; position:relative; overflow:hidden; background:#fff; border:1px solid #e4e7ee; border-left:4px solid var(--tone); border-radius:12px; padding:12px 16px 11px; }\n.statHead_900471699 { display:flex; align-items:center; gap:8px; margin-bottom:8px; }\n.statIcon_900471699 { display:flex; align-items:center; justify-content:center; width:32px; height:32px; border-radius:10px; color:#fff; background:linear-gradient(135deg, var(--tone), var(--tone-soft)); flex-shrink:0; box-shadow:0 3px 8px var(--tone-soft); }\n.statMark_900471699 { position:absolute; right:-14px; bottom:-18px; width:92px; height:92px; color:var(--tone); pointer-events:none; }\n.statLabel_900471699 { font-size:12px; color:#7d8495; }\n.statValue_900471699 { display:block; font-size:25px; font-weight:650; letter-spacing:-0.5px; white-space:nowrap; font-variant-numeric:tabular-nums; }\n.statSub_900471699 { display:block; margin-top:5px; font-size:11px; color:#939aac; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.toneBlue_900471699 { --tone:#4f79f6; --tone-soft:#eaf0fe; }\n.tonePurple_900471699 { --tone:#8b5cf6; --tone-soft:#f1ebfe; }\n.toneGreen_900471699 { --tone:#18a97b; --tone-soft:#e5f6f0; }\n.toneOrange_900471699 { --tone:#e8892c; --tone-soft:#fcf1e4; }\n.toneTeal_900471699 { --tone:#16a4b8; --tone-soft:#e4f5f8; }\n.tonePink_900471699 { --tone:#d9538c; --tone-soft:#fbeaf1; }\n.card_900471699 { min-width:0; background:#fff; border:1px solid #e4e7ee; border-radius:12px; margin-bottom:18px; overflow:hidden; }\n.cardHead_900471699 { display:flex; align-items:center; justify-content:space-between; gap:10px; min-height:54px; padding:15px 18px; border-bottom:1px solid #eef0f4; }\n.cardHead_900471699 h2 { font-size:14px; font-weight:600; margin:0; }\n.cardHead_900471699 > span { display:flex; gap:7px; align-items:center; font-size:11px; }\n.count_900471699 { display:inline-flex; align-items:center; justify-content:center; min-width:21px; padding:2px 6px; font-size:10px; color:#8b93a3; background:#f3f5f8; border-radius:5px; margin-left:5px; font-weight:500; }\n.mid_900471699 { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:18px; align-items:stretch; }\n.mid_900471699 .card_900471699 { height:100%; margin-bottom:0; }\n.mid_900471699 { margin-bottom:18px; }\n.table_900471699 { border-collapse:collapse; width:100%; table-layout:fixed; font-size:11px; }\n.table_900471699 th { color:#929aaa; font-weight:500; padding:12px 5px; text-align:right; vertical-align:middle; line-height:1.3; overflow-wrap:anywhere; }\n.table_900471699 td { padding:13px 5px; text-align:right; border-top:1px solid #eef0f4; color:#626c80; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.table_900471699 th:first-child,.table_900471699 td:first-child { text-align:left; padding-left:16px; }\n.table_900471699 th:last-child,.table_900471699 td:last-child { padding-right:16px; }\n.childName_900471699 { width:28%; }\n.childRequests_900471699 { width:11%; }\n.modelName_900471699 { width:30%; }\n.modelRequests_900471699 { width:11%; }\n.modelHit_900471699 { width:16%; }\n.ellipsis_900471699 { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:#394354; }\n/* By-model rows lead with the model's brand logo; the name keeps truncating\n * inside the flexible remainder of the cell. */\n.modelNameCell_900471699 { display:flex; align-items:center; gap:6px; min-width:0; }\n.modelNameCell_900471699 .ellipsis_900471699 { flex:1; min-width:0; }\n/* Gray provider label under a model name shown by several rows (同名模型). */\n.modelProvider_900471699 { display:block; margin-top:2px; font-size:10px; color:#939aaa; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.number_900471699 { font-variant-numeric:tabular-nums; }\n.speedCell_900471699 { display:inline-flex; align-items:baseline; gap:3px; white-space:nowrap; }\n.speedCell_900471699 small { font-size:9px; color:#929aaa; }\n.good_900471699 { color:#21a17b; }\n.warn_900471699 { color:#d69a21; }\n.bad_900471699 { color:#e06571; }\n.live_900471699 { color:#169bb3; }\n.muted_900471699 { color:#939aaa; }\n.empty_900471699 { margin:0; padding:28px 18px; color:#9aa2b1; font-size:12px; text-align:center; }\n.action_900471699 { color:#818b9e; background:none; border:0; padding:3px 0; font:inherit; font-size:11px; cursor:pointer; }\n.action_900471699:hover { color:#279cb6; }\n.action_900471699:focus-visible,.turnHead_900471699:focus-visible,.refresh_900471699:focus-visible { outline:2px solid #2ab1c6; outline-offset:-2px; }\n.turnList_900471699 { padding:0 14px 14px; }\n.turn_900471699 { margin-top:10px; border:1px solid #e7eaf0; border-radius:9px; background:#fff; overflow:hidden; }\n.turnHead_900471699 { display:grid; grid-template-columns:12px minmax(0,1fr) auto; align-items:center; gap:12px; padding:13px 14px; width:100%; text-align:left; background:#f8f9fc; border:0; cursor:pointer; color:#616c7f; font:inherit; font-size:11px; }\n.chevron_900471699 { color:#99a1af; transition:transform 0.2s; }\n.rotated_900471699 { transform:rotate(90deg); }\n.turnTitle_900471699 { display:flex; align-items:center; gap:6px; min-width:0; font-size:12px; font-weight:600; color:#394354; }\n/* Turn topic is the group title now (the page header owns the session name),\n * so it carries the primary text color and truncates before the metrics. */\n.turnTopic_900471699 { min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }\n/* A subagent's turns look like this session's turns apart from the agent tag;\n * the tag keeps the full session name in its tooltip. */\n.turnAgent_900471699 { flex:none; padding:1px 6px; border-radius:5px; background:#e9edf4; color:#6f7889; font-size:10px; font-weight:500; }\n.turnSummary_900471699 { display:flex; gap:5px; align-items:center; white-space:nowrap; }\n.turnSummary_900471699 > span:last-child { margin-left:8px; }\n.turnBody_900471699 { border-top:1px solid #eef0f4; }\n.turnBody_900471699[hidden] { display:none; }\n/* A request row is one line: time, what answered, and its metrics pinned to the\n * row end. The metrics never wrap; they truncate with a tooltip before the head\n * gives up its model name. */\n.turnRequest_900471699 { display:grid; grid-template-columns:64px minmax(0,1fr) auto; align-items:center; gap:12px; padding:10px 16px; border-top:1px solid #f3f5f9; }\n.turnRequest_900471699:first-child { border-top:0; }\n.turnRequestHead_900471699 { display:flex; align-items:center; gap:8px; font-size:11px; color:#616c7f; min-width:0; }\n.requestTime_900471699 { color:#9aa3b3; font-variant-numeric:tabular-nums; flex-shrink:0; }\n.statusDot_900471699 { width:5px; height:5px; border-radius:50%; background:currentColor; flex-shrink:0; }\n/* Per-model brand logo: colors are baked into each logo's markup, only the\n * layout contract lives here. Unknown models get the neutral gray fallback. */\n.modelLogo_900471699 { flex-shrink:0; }\n.recordModel_900471699 { min-width:0; display:flex; align-items:center; gap:6px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#394354; font-weight:500; }\n.routeText_900471699 { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#8b93a4; flex-shrink:1; }\n.effort_900471699 { white-space:nowrap; color:#9aa3b3; background:#f1f3f7; border-radius:5px; padding:1px 6px; }\n.recordMeta_900471699 { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:right; font-size:10px; color:#949cab; font-variant-numeric:tabular-nums; }\n.note_900471699 { font-size:10px; line-height:1.7; color:#9aa3b3; margin:0; }\n.error_900471699 { background:#fff4f1; color:#b86751; border:1px solid #f4d8ce; border-radius:9px; padding:12px 16px; margin-bottom:16px; font-size:12px; display:flex; gap:16px; justify-content:space-between; }\n@container (max-width:1050px) {\n .mid_900471699 { grid-template-columns:minmax(0,1fr); }\n .statRow_900471699 { grid-template-columns:repeat(2,minmax(0,1fr)); }\n .statValue_900471699 { font-size:23px; }\n .table_900471699 { font-size:12px; }\n .childName_900471699 { width:34%; }\n}\n@container (max-width:700px) {\n .page_900471699 { padding:18px 14px 24px; }\n .stat_900471699 { padding:12px 12px 11px; }\n .statValue_900471699 { font-size:20px; }\n .table_900471699 { font-size:10px; }\n .table_900471699 th,.table_900471699 td { padding:10px 3px; }\n .table_900471699 th:first-child,.table_900471699 td:first-child { padding-left:10px; }\n .table_900471699 th:last-child,.table_900471699 td:last-child { padding-right:10px; }\n .childName_900471699 { width:25%; }\n .childRequests_900471699 { width:11%; }\n .modelName_900471699 { width:25%; }\n .modelRequests_900471699 { width:11%; }\n .modelHit_900471699 { width:17%; }\n .turnHead_900471699 { gap:8px; padding:11px 10px; }\n .turnSummary_900471699 { grid-column:2; font-size:10px; }\n .turnRequest_900471699 { grid-template-columns:56px minmax(120px,1fr) auto; gap:8px; padding:10px; }\n .turnRequestHead_900471699 { gap:6px; }\n .routeText_900471699 { display:none; }\n}\n@media (prefers-reduced-motion:reduce) {\n .chevron_900471699 { transition:none; }\n}\n";
|
|
32879
|
+
const css = "/* A light, self-contained Usage surface. Tables fit the available width:\n * no horizontal scroll containers, fixed column contracts, titles for names.\n * Six stat cards sit in one row (≤1050px: 3-up, ≤700px: 2-up) as quiet white\n * surfaces on a pale canvas: no accent edges or watermarks, a flat tone chip\n * leading each label, hierarchy from the type scale and a hairline above the\n * footnote. Request log folds by conversation turn. */\n.scroll_900471699 { height:100%; min-height:0; overflow-y:auto; background:#f6f7f9; color:#252936; color-scheme:light; container-type:inline-size; }\n.page_900471699 { box-sizing:border-box; width:100%; max-width:1160px; margin:0 auto; padding:24px 26px 32px; font-family:system-ui,-apple-system,\"Segoe UI\",sans-serif; font-size:13px; }\n.page_900471699 * { box-sizing:border-box; }\n/* Freshness header: live badge + last-read stamp on the left, manual refresh\n * pinned to the right edge. */\n.head_900471699 { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:0 0 12px; }\n.headStatus_900471699 { display:flex; align-items:center; gap:7px; min-width:0; font-size:11px; color:#8790a3; }\n.liveDot_900471699 { width:6px; height:6px; border-radius:50%; background:#18a97b; box-shadow:0 0 0 3px #e5f6f0; flex-shrink:0; }\n.asOf_900471699 { color:#9aa3b3; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.refresh_900471699 { display:inline-flex; align-items:center; gap:5px; flex:none; color:#6f7a8d; background:#fff; border:1px solid #e4e7ee; border-radius:8px; padding:5px 11px; font:inherit; font-size:11px; cursor:pointer; }\n.refresh_900471699:hover { color:#279cb6; border-color:#cbd3e0; }\n.refresh_900471699:disabled { opacity:.55; cursor:default; }\n.scope_900471699 { color:#8790a3; font-size:11px; margin:0 0 14px; }\n.stats_900471699 { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:8px; margin-bottom:16px; }\n.stat_900471699 { min-width:0; position:relative; background:#fff; border:1px solid #e7eaf0; border-radius:12px; padding:12px 14px 11px; transition:border-color .18s ease, box-shadow .18s ease, transform .18s ease; }\n.stat_900471699:hover { border-color:#d9dfe9; box-shadow:0 14px 30px -22px rgba(23,32,54,.32); transform:translateY(-1px); }\n.statHead_900471699 { display:flex; align-items:center; gap:5px; margin-bottom:6px; }\n.statIcon_900471699 { display:flex; align-items:center; justify-content:center; width:20px; height:20px; border-radius:6px; color:var(--tone); background:var(--tone-soft); flex-shrink:0; }\n.statLabel_900471699 { font-size:12px; font-weight:500; color:#7b8494; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }\n.statValue_900471699 { display:block; font-size:21px; font-weight:650; letter-spacing:-0.4px; color:#1b2130; white-space:nowrap; font-variant-numeric:tabular-nums; }\n.statSub_900471699 { display:block; margin-top:8px; padding-top:7px; border-top:1px solid #f2f4f8; font-size:11px; color:#939aab; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.toneBlue_900471699 { --tone:#4f79f6; --tone-soft:#eaf0fe; }\n.tonePurple_900471699 { --tone:#8b5cf6; --tone-soft:#f1ebfe; }\n.toneGreen_900471699 { --tone:#18a97b; --tone-soft:#e5f6f0; }\n.toneOrange_900471699 { --tone:#e8892c; --tone-soft:#fcf1e4; }\n.toneTeal_900471699 { --tone:#16a4b8; --tone-soft:#e4f5f8; }\n.card_900471699 { min-width:0; background:#fff; border:1px solid #e4e7ee; border-radius:12px; margin-bottom:18px; overflow:hidden; }\n.cardHead_900471699 { display:flex; align-items:center; justify-content:space-between; gap:10px; min-height:54px; padding:15px 18px; border-bottom:1px solid #eef0f4; }\n.cardHead_900471699 h2 { font-size:14px; font-weight:600; margin:0; }\n.cardHead_900471699 > span { display:flex; gap:7px; align-items:center; font-size:11px; }\n.count_900471699 { display:inline-flex; align-items:center; justify-content:center; min-width:21px; padding:2px 6px; font-size:10px; color:#8b93a3; background:#f3f5f8; border-radius:5px; margin-left:5px; font-weight:500; }\n.mid_900471699 { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:18px; align-items:stretch; }\n.mid_900471699 .card_900471699 { height:100%; margin-bottom:0; }\n.mid_900471699 { margin-bottom:18px; }\n.table_900471699 { border-collapse:collapse; width:100%; table-layout:fixed; font-size:11px; }\n.table_900471699 th { color:#929aaa; font-weight:500; padding:12px 5px; text-align:right; vertical-align:middle; line-height:1.3; overflow-wrap:anywhere; }\n.table_900471699 td { padding:13px 5px; text-align:right; border-top:1px solid #eef0f4; color:#626c80; font-variant-numeric:tabular-nums; white-space:nowrap; }\n.table_900471699 th:first-child,.table_900471699 td:first-child { text-align:left; padding-left:16px; }\n.table_900471699 th:last-child,.table_900471699 td:last-child { padding-right:16px; }\n.childName_900471699 { width:28%; }\n.childRequests_900471699 { width:11%; }\n.modelName_900471699 { width:30%; }\n.modelRequests_900471699 { width:11%; }\n.modelHit_900471699 { width:16%; }\n.ellipsis_900471699 { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:#394354; }\n/* By-model rows lead with the model's brand logo; the name keeps truncating\n * inside the flexible remainder of the cell. */\n.modelNameCell_900471699 { display:flex; align-items:center; gap:6px; min-width:0; }\n.modelNameCell_900471699 .ellipsis_900471699 { flex:1; min-width:0; }\n/* Gray provider label under a model name shown by several rows (同名模型). */\n.modelProvider_900471699 { display:block; margin-top:2px; font-size:10px; color:#939aaa; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.number_900471699 { font-variant-numeric:tabular-nums; }\n.speedCell_900471699 { display:inline-flex; align-items:baseline; gap:3px; white-space:nowrap; }\n.speedCell_900471699 small { font-size:9px; color:#929aaa; }\n.good_900471699 { color:#21a17b; }\n.warn_900471699 { color:#d69a21; }\n.bad_900471699 { color:#e06571; }\n.live_900471699 { color:#169bb3; }\n.muted_900471699 { color:#939aaa; }\n.empty_900471699 { margin:0; padding:28px 18px; color:#9aa2b1; font-size:12px; text-align:center; }\n.action_900471699 { color:#818b9e; background:none; border:0; padding:3px 0; font:inherit; font-size:11px; cursor:pointer; }\n.action_900471699:hover { color:#279cb6; }\n.action_900471699:focus-visible,.turnHead_900471699:focus-visible,.refresh_900471699:focus-visible { outline:2px solid #2ab1c6; outline-offset:-2px; }\n.turnList_900471699 { padding:0 14px 14px; }\n.turn_900471699 { margin-top:10px; border:1px solid #e7eaf0; border-radius:9px; background:#fff; overflow:hidden; }\n.turnHead_900471699 { display:grid; grid-template-columns:12px minmax(0,1fr) auto; align-items:center; gap:12px; padding:13px 14px; width:100%; text-align:left; background:#f8f9fc; border:0; cursor:pointer; color:#616c7f; font:inherit; font-size:11px; }\n.chevron_900471699 { color:#99a1af; transition:transform 0.2s; }\n.rotated_900471699 { transform:rotate(90deg); }\n.turnTitle_900471699 { display:flex; align-items:center; gap:6px; min-width:0; font-size:12px; font-weight:600; color:#394354; }\n/* Turn topic is the group title now (the page header owns the session name),\n * so it carries the primary text color and truncates before the metrics. */\n.turnTopic_900471699 { min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }\n/* A subagent's turns look like this session's turns apart from the agent tag;\n * the tag keeps the full session name in its tooltip. */\n.turnAgent_900471699 { flex:none; padding:1px 6px; border-radius:5px; background:#e9edf4; color:#6f7889; font-size:10px; font-weight:500; }\n.turnSummary_900471699 { display:flex; gap:5px; align-items:center; white-space:nowrap; }\n.turnSummary_900471699 > span:last-child { margin-left:8px; }\n.turnBody_900471699 { border-top:1px solid #eef0f4; }\n.turnBody_900471699[hidden] { display:none; }\n/* A request row is one line: time, what answered, and its metrics pinned to the\n * row end. The metrics never wrap; they truncate with a tooltip before the head\n * gives up its model name. */\n.turnRequest_900471699 { display:grid; grid-template-columns:64px minmax(0,1fr) auto; align-items:center; gap:12px; padding:10px 16px; border-top:1px solid #f3f5f9; }\n.turnRequest_900471699:first-child { border-top:0; }\n.turnRequestHead_900471699 { display:flex; align-items:center; gap:8px; font-size:11px; color:#616c7f; min-width:0; }\n.requestTime_900471699 { color:#9aa3b3; font-variant-numeric:tabular-nums; flex-shrink:0; }\n.statusDot_900471699 { width:5px; height:5px; border-radius:50%; background:currentColor; flex-shrink:0; }\n/* Per-model brand logo: colors are baked into each logo's markup, only the\n * layout contract lives here. Unknown models get the neutral gray fallback. */\n.modelLogo_900471699 { flex-shrink:0; }\n.recordModel_900471699 { min-width:0; display:flex; align-items:center; gap:6px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#394354; font-weight:500; }\n.routeText_900471699 { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#8b93a4; flex-shrink:1; }\n.effort_900471699 { white-space:nowrap; color:#9aa3b3; background:#f1f3f7; border-radius:5px; padding:1px 6px; }\n.recordMeta_900471699 { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:right; font-size:10px; color:#949cab; font-variant-numeric:tabular-nums; }\n.note_900471699 { font-size:10px; line-height:1.7; color:#9aa3b3; margin:0; }\n.error_900471699 { background:#fff4f1; color:#b86751; border:1px solid #f4d8ce; border-radius:9px; padding:12px 16px; margin-bottom:16px; font-size:12px; display:flex; gap:16px; justify-content:space-between; }\n@container (max-width:1050px) {\n .mid_900471699 { grid-template-columns:minmax(0,1fr); }\n .stats_900471699 { grid-template-columns:repeat(3,minmax(0,1fr)); }\n .table_900471699 { font-size:12px; }\n .childName_900471699 { width:34%; }\n}\n@container (max-width:700px) {\n .page_900471699 { padding:18px 14px 24px; }\n .stats_900471699 { grid-template-columns:repeat(2,minmax(0,1fr)); }\n .stat_900471699 { padding:10px 12px 9px; }\n .statValue_900471699 { font-size:18px; }\n .table_900471699 { font-size:10px; }\n .table_900471699 th,.table_900471699 td { padding:10px 3px; }\n .table_900471699 th:first-child,.table_900471699 td:first-child { padding-left:10px; }\n .table_900471699 th:last-child,.table_900471699 td:last-child { padding-right:10px; }\n .childName_900471699 { width:25%; }\n .childRequests_900471699 { width:11%; }\n .modelName_900471699 { width:25%; }\n .modelRequests_900471699 { width:11%; }\n .modelHit_900471699 { width:17%; }\n .turnHead_900471699 { gap:8px; padding:11px 10px; }\n .turnSummary_900471699 { grid-column:2; font-size:10px; }\n .turnRequest_900471699 { grid-template-columns:56px minmax(120px,1fr) auto; gap:8px; padding:10px; }\n .turnRequestHead_900471699 { gap:6px; }\n .routeText_900471699 { display:none; }\n}\n@media (prefers-reduced-motion:reduce) {\n .chevron_900471699 { transition:none; }\n .stat_900471699 { transition:none; }\n .stat_900471699:hover { transform:none; }\n}\n";
|
|
32880
32880
|
const tagId = "dsh-token-total/SessionUsageView.module.css";
|
|
32881
32881
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId + "\"]") === null) {
|
|
32882
32882
|
const tag = document.createElement("style");
|
|
@@ -32895,11 +32895,9 @@ window.__ModuleLoader__.load({
|
|
|
32895
32895
|
"refresh": "refresh_900471699",
|
|
32896
32896
|
"scope": "scope_900471699",
|
|
32897
32897
|
"stats": "stats_900471699",
|
|
32898
|
-
"statRow": "statRow_900471699",
|
|
32899
32898
|
"stat": "stat_900471699",
|
|
32900
32899
|
"statHead": "statHead_900471699",
|
|
32901
32900
|
"statIcon": "statIcon_900471699",
|
|
32902
|
-
"statMark": "statMark_900471699",
|
|
32903
32901
|
"statLabel": "statLabel_900471699",
|
|
32904
32902
|
"statValue": "statValue_900471699",
|
|
32905
32903
|
"statSub": "statSub_900471699",
|
|
@@ -32908,7 +32906,6 @@ window.__ModuleLoader__.load({
|
|
|
32908
32906
|
"toneGreen": "toneGreen_900471699",
|
|
32909
32907
|
"toneOrange": "toneOrange_900471699",
|
|
32910
32908
|
"toneTeal": "toneTeal_900471699",
|
|
32911
|
-
"tonePink": "tonePink_900471699",
|
|
32912
32909
|
"card": "card_900471699",
|
|
32913
32910
|
"cardHead": "cardHead_900471699",
|
|
32914
32911
|
"count": "count_900471699",
|
|
@@ -32998,50 +32995,20 @@ window.__ModuleLoader__.load({
|
|
|
32998
32995
|
hit: "M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0ZM16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z",
|
|
32999
32996
|
speed: "M13 2 4 14h7l-1 8 9-12h-7l1-8Z",
|
|
33000
32997
|
input: "M12 3v12M7 10l5 5 5-5M4 21h16",
|
|
33001
|
-
output: "M12 21V9M7 14l5-5 5 5M4 3h16"
|
|
33002
|
-
read: "M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7ZM15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z",
|
|
33003
|
-
write: "M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5Z"
|
|
32998
|
+
output: "M12 21V9M7 14l5-5 5 5M4 3h16"
|
|
33004
32999
|
};
|
|
33005
33000
|
function Stat({ tone, name, label, value, sub, title }) {
|
|
33006
33001
|
const icon = icons[name];
|
|
33007
33002
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
33008
33003
|
className: `${SessionUsageView_module_default.stat} ${tone}`,
|
|
33009
33004
|
children: [
|
|
33010
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
33011
|
-
className: SessionUsageView_module_default.statMark,
|
|
33012
|
-
viewBox: "0 0 24 24",
|
|
33013
|
-
"aria-hidden": "true",
|
|
33014
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
|
|
33015
|
-
id: `usage-mark-${name}`,
|
|
33016
|
-
x1: "0",
|
|
33017
|
-
y1: "0",
|
|
33018
|
-
x2: "1",
|
|
33019
|
-
y2: "1",
|
|
33020
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
33021
|
-
offset: "0",
|
|
33022
|
-
stopColor: "currentColor",
|
|
33023
|
-
stopOpacity: "0.3"
|
|
33024
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
33025
|
-
offset: "1",
|
|
33026
|
-
stopColor: "currentColor",
|
|
33027
|
-
stopOpacity: "0.03"
|
|
33028
|
-
})]
|
|
33029
|
-
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
33030
|
-
d: icon,
|
|
33031
|
-
fill: `url(#usage-mark-${name})`,
|
|
33032
|
-
stroke: `url(#usage-mark-${name})`,
|
|
33033
|
-
strokeWidth: "1.1",
|
|
33034
|
-
strokeLinecap: "round",
|
|
33035
|
-
strokeLinejoin: "round"
|
|
33036
|
-
})]
|
|
33037
|
-
}),
|
|
33038
33005
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33039
33006
|
className: SessionUsageView_module_default.statHead,
|
|
33040
33007
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
33041
33008
|
className: SessionUsageView_module_default.statIcon,
|
|
33042
33009
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
33043
|
-
width: "
|
|
33044
|
-
height: "
|
|
33010
|
+
width: "12",
|
|
33011
|
+
height: "12",
|
|
33045
33012
|
viewBox: "0 0 24 24",
|
|
33046
33013
|
fill: "none",
|
|
33047
33014
|
stroke: "currentColor",
|
|
@@ -33074,76 +33041,53 @@ window.__ModuleLoader__.load({
|
|
|
33074
33041
|
const failRate = detail.requests > 0 ? (failed / detail.requests * 100).toFixed(1) : "0.0";
|
|
33075
33042
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
33076
33043
|
className: SessionUsageView_module_default.stats,
|
|
33077
|
-
children: [
|
|
33078
|
-
|
|
33079
|
-
|
|
33080
|
-
|
|
33081
|
-
|
|
33082
|
-
|
|
33083
|
-
|
|
33084
|
-
|
|
33085
|
-
|
|
33086
|
-
|
|
33087
|
-
|
|
33088
|
-
|
|
33089
|
-
|
|
33090
|
-
|
|
33091
|
-
|
|
33092
|
-
|
|
33093
|
-
|
|
33094
|
-
|
|
33095
|
-
|
|
33096
|
-
|
|
33097
|
-
|
|
33098
|
-
|
|
33099
|
-
|
|
33100
|
-
|
|
33101
|
-
|
|
33102
|
-
|
|
33103
|
-
|
|
33104
|
-
|
|
33105
|
-
|
|
33106
|
-
|
|
33107
|
-
|
|
33108
|
-
|
|
33109
|
-
|
|
33110
|
-
|
|
33111
|
-
|
|
33112
|
-
|
|
33113
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
33114
|
-
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
|
|
33118
|
-
|
|
33119
|
-
|
|
33120
|
-
|
|
33121
|
-
|
|
33122
|
-
|
|
33123
|
-
|
|
33124
|
-
label: t("outputFull"),
|
|
33125
|
-
value: formatCompact(detail.totals.outputTokens),
|
|
33126
|
-
title: formatTokens(detail.totals.outputTokens),
|
|
33127
|
-
sub: formatTokens(detail.totals.outputTokens)
|
|
33128
|
-
}),
|
|
33129
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33130
|
-
tone: SessionUsageView_module_default.tonePurple,
|
|
33131
|
-
name: "read",
|
|
33132
|
-
label: t("readFull"),
|
|
33133
|
-
value: formatCompact(detail.totals.cacheReadTokens),
|
|
33134
|
-
title: formatTokens(detail.totals.cacheReadTokens),
|
|
33135
|
-
sub: formatTokens(detail.totals.cacheReadTokens)
|
|
33136
|
-
}),
|
|
33137
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33138
|
-
tone: SessionUsageView_module_default.tonePink,
|
|
33139
|
-
name: "write",
|
|
33140
|
-
label: t("writeFull"),
|
|
33141
|
-
value: formatCompact(detail.totals.cacheWriteTokens),
|
|
33142
|
-
title: formatTokens(detail.totals.cacheWriteTokens),
|
|
33143
|
-
sub: formatTokens(detail.totals.cacheWriteTokens)
|
|
33144
|
-
})
|
|
33145
|
-
]
|
|
33146
|
-
})]
|
|
33044
|
+
children: [
|
|
33045
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33046
|
+
tone: SessionUsageView_module_default.toneBlue,
|
|
33047
|
+
name: "requests",
|
|
33048
|
+
label: t("requestsCount"),
|
|
33049
|
+
value: formatTokens(detail.requests),
|
|
33050
|
+
sub: `${t("failed")} ${failed} · ${failRate}%`
|
|
33051
|
+
}),
|
|
33052
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33053
|
+
tone: SessionUsageView_module_default.tonePurple,
|
|
33054
|
+
name: "tokens",
|
|
33055
|
+
label: t("tokens"),
|
|
33056
|
+
value: formatCompact(billed),
|
|
33057
|
+
title: formatTokens(billed),
|
|
33058
|
+
sub: `${formatTokens(billed)} tokens`
|
|
33059
|
+
}),
|
|
33060
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33061
|
+
tone: SessionUsageView_module_default.toneTeal,
|
|
33062
|
+
name: "input",
|
|
33063
|
+
label: t("inputFull"),
|
|
33064
|
+
value: formatCompact(detail.totals.uncachedInputTokens),
|
|
33065
|
+
title: formatTokens(detail.totals.uncachedInputTokens),
|
|
33066
|
+
sub: formatTokens(detail.totals.uncachedInputTokens)
|
|
33067
|
+
}),
|
|
33068
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33069
|
+
tone: SessionUsageView_module_default.toneOrange,
|
|
33070
|
+
name: "output",
|
|
33071
|
+
label: t("outputFull"),
|
|
33072
|
+
value: formatCompact(detail.totals.outputTokens),
|
|
33073
|
+
title: formatTokens(detail.totals.outputTokens),
|
|
33074
|
+
sub: formatTokens(detail.totals.outputTokens)
|
|
33075
|
+
}),
|
|
33076
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33077
|
+
tone: SessionUsageView_module_default.toneGreen,
|
|
33078
|
+
name: "hit",
|
|
33079
|
+
label: t("hit"),
|
|
33080
|
+
value: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Hit, { totals: detail.totals }),
|
|
33081
|
+
sub: `${formatCompact(detail.totals.cacheReadTokens)} ${t("cacheHit")}`
|
|
33082
|
+
}),
|
|
33083
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stat, {
|
|
33084
|
+
tone: SessionUsageView_module_default.toneOrange,
|
|
33085
|
+
name: "speed",
|
|
33086
|
+
label: t("speed"),
|
|
33087
|
+
value: speed(detail.tokensPerSecond),
|
|
33088
|
+
sub: `${t("first")} ${seconds(detail.averageFirstTokenMs)}`
|
|
33089
|
+
})
|
|
33090
|
+
]
|
|
33147
33091
|
});
|
|
33148
33092
|
}
|
|
33149
33093
|
function Tables({ detail, t }) {
|
|
@@ -33317,6 +33261,8 @@ window.__ModuleLoader__.load({
|
|
|
33317
33261
|
}
|
|
33318
33262
|
group.rows.push(record);
|
|
33319
33263
|
}
|
|
33264
|
+
const latest = (group) => group.rows.reduce((max, row) => Math.max(max, row.startedAt), 0);
|
|
33265
|
+
groups.sort((a, b) => latest(b) - latest(a));
|
|
33320
33266
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
33321
33267
|
className: SessionUsageView_module_default.card,
|
|
33322
33268
|
children: [
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/* A light, self-contained Usage surface. Tables fit the available width:
|
|
2
2
|
* no horizontal scroll containers, fixed column contracts, titles for names.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
|
|
3
|
+
* Six stat cards sit in one row (≤1050px: 3-up, ≤700px: 2-up) as quiet white
|
|
4
|
+
* surfaces on a pale canvas: no accent edges or watermarks, a flat tone chip
|
|
5
|
+
* leading each label, hierarchy from the type scale and a hairline above the
|
|
6
|
+
* footnote. Request log folds by conversation turn. */
|
|
7
|
+
.scroll { height:100%; min-height:0; overflow-y:auto; background:#f6f7f9; color:#252936; color-scheme:light; container-type:inline-size; }
|
|
6
8
|
.page { box-sizing:border-box; width:100%; max-width:1160px; margin:0 auto; padding:24px 26px 32px; font-family:system-ui,-apple-system,"Segoe UI",sans-serif; font-size:13px; }
|
|
7
9
|
.page * { box-sizing:border-box; }
|
|
8
10
|
/* Freshness header: live badge + last-read stamp on the left, manual refresh
|
|
@@ -15,21 +17,19 @@
|
|
|
15
17
|
.refresh:hover { color:#279cb6; border-color:#cbd3e0; }
|
|
16
18
|
.refresh:disabled { opacity:.55; cursor:default; }
|
|
17
19
|
.scope { color:#8790a3; font-size:11px; margin:0 0 14px; }
|
|
18
|
-
.stats { display:grid; gap:
|
|
19
|
-
.
|
|
20
|
-
.stat {
|
|
21
|
-
.statHead { display:flex; align-items:center; gap:
|
|
22
|
-
.statIcon { display:flex; align-items:center; justify-content:center; width:
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
.statSub { display:block; margin-top:5px; font-size:11px; color:#939aac; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
20
|
+
.stats { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:8px; margin-bottom:16px; }
|
|
21
|
+
.stat { min-width:0; position:relative; background:#fff; border:1px solid #e7eaf0; border-radius:12px; padding:12px 14px 11px; transition:border-color .18s ease, box-shadow .18s ease, transform .18s ease; }
|
|
22
|
+
.stat:hover { border-color:#d9dfe9; box-shadow:0 14px 30px -22px rgba(23,32,54,.32); transform:translateY(-1px); }
|
|
23
|
+
.statHead { display:flex; align-items:center; gap:5px; margin-bottom:6px; }
|
|
24
|
+
.statIcon { display:flex; align-items:center; justify-content:center; width:20px; height:20px; border-radius:6px; color:var(--tone); background:var(--tone-soft); flex-shrink:0; }
|
|
25
|
+
.statLabel { font-size:12px; font-weight:500; color:#7b8494; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
|
|
26
|
+
.statValue { display:block; font-size:21px; font-weight:650; letter-spacing:-0.4px; color:#1b2130; white-space:nowrap; font-variant-numeric:tabular-nums; }
|
|
27
|
+
.statSub { display:block; margin-top:8px; padding-top:7px; border-top:1px solid #f2f4f8; font-size:11px; color:#939aab; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
27
28
|
.toneBlue { --tone:#4f79f6; --tone-soft:#eaf0fe; }
|
|
28
29
|
.tonePurple { --tone:#8b5cf6; --tone-soft:#f1ebfe; }
|
|
29
30
|
.toneGreen { --tone:#18a97b; --tone-soft:#e5f6f0; }
|
|
30
31
|
.toneOrange { --tone:#e8892c; --tone-soft:#fcf1e4; }
|
|
31
32
|
.toneTeal { --tone:#16a4b8; --tone-soft:#e4f5f8; }
|
|
32
|
-
.tonePink { --tone:#d9538c; --tone-soft:#fbeaf1; }
|
|
33
33
|
.card { min-width:0; background:#fff; border:1px solid #e4e7ee; border-radius:12px; margin-bottom:18px; overflow:hidden; }
|
|
34
34
|
.cardHead { display:flex; align-items:center; justify-content:space-between; gap:10px; min-height:54px; padding:15px 18px; border-bottom:1px solid #eef0f4; }
|
|
35
35
|
.cardHead h2 { font-size:14px; font-weight:600; margin:0; }
|
|
@@ -102,15 +102,15 @@
|
|
|
102
102
|
.error { background:#fff4f1; color:#b86751; border:1px solid #f4d8ce; border-radius:9px; padding:12px 16px; margin-bottom:16px; font-size:12px; display:flex; gap:16px; justify-content:space-between; }
|
|
103
103
|
@container (max-width:1050px) {
|
|
104
104
|
.mid { grid-template-columns:minmax(0,1fr); }
|
|
105
|
-
.
|
|
106
|
-
.statValue { font-size:23px; }
|
|
105
|
+
.stats { grid-template-columns:repeat(3,minmax(0,1fr)); }
|
|
107
106
|
.table { font-size:12px; }
|
|
108
107
|
.childName { width:34%; }
|
|
109
108
|
}
|
|
110
109
|
@container (max-width:700px) {
|
|
111
110
|
.page { padding:18px 14px 24px; }
|
|
112
|
-
.
|
|
113
|
-
.
|
|
111
|
+
.stats { grid-template-columns:repeat(2,minmax(0,1fr)); }
|
|
112
|
+
.stat { padding:10px 12px 9px; }
|
|
113
|
+
.statValue { font-size:18px; }
|
|
114
114
|
.table { font-size:10px; }
|
|
115
115
|
.table th,.table td { padding:10px 3px; }
|
|
116
116
|
.table th:first-child,.table td:first-child { padding-left:10px; }
|
|
@@ -128,4 +128,6 @@
|
|
|
128
128
|
}
|
|
129
129
|
@media (prefers-reduced-motion:reduce) {
|
|
130
130
|
.chevron { transition:none; }
|
|
131
|
+
.stat { transition:none; }
|
|
132
|
+
.stat:hover { transform:none; }
|
|
131
133
|
}
|
|
@@ -45,19 +45,15 @@ const icons = {
|
|
|
45
45
|
speed: 'M13 2 4 14h7l-1 8 9-12h-7l1-8Z',
|
|
46
46
|
input: 'M12 3v12M7 10l5 5 5-5M4 21h16',
|
|
47
47
|
output: 'M12 21V9M7 14l5-5 5 5M4 3h16',
|
|
48
|
-
read: 'M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7ZM15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z',
|
|
49
|
-
write: 'M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5Z',
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
function Stat({ tone, name, label, value, sub, title }: { tone: string; name: keyof typeof icons; label: string; value: string | JSX.Element; sub?: string; title?: string }): JSX.Element {
|
|
53
51
|
const icon = icons[name]
|
|
54
52
|
return <article className={`${css.stat} ${tone}`}>
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
</svg>
|
|
60
|
-
<div className={css.statHead}><span className={css.statIcon}><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d={icon} /></svg></span><span className={css.statLabel}>{label}</span></div>
|
|
53
|
+
<div className={css.statHead}>
|
|
54
|
+
<span className={css.statIcon}><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d={icon} /></svg></span>
|
|
55
|
+
<span className={css.statLabel}>{label}</span>
|
|
56
|
+
</div>
|
|
61
57
|
<strong className={css.statValue} title={title}>{value}</strong>
|
|
62
58
|
{sub !== undefined && <span className={css.statSub}>{sub}</span>}
|
|
63
59
|
</article>
|
|
@@ -68,18 +64,12 @@ function Metrics({ detail, t }: { detail: SessionUsageDetail; t: T }): JSX.Eleme
|
|
|
68
64
|
const failed = detail.records.filter(row => row.status === 'failed').length
|
|
69
65
|
const failRate = detail.requests > 0 ? (failed / detail.requests * 100).toFixed(1) : '0.0'
|
|
70
66
|
return <div className={css.stats}>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<div className={css.statRow}>
|
|
78
|
-
<Stat tone={css.toneTeal} name="input" label={t('inputFull')} value={formatCompact(detail.totals.uncachedInputTokens)} title={formatTokens(detail.totals.uncachedInputTokens)} sub={formatTokens(detail.totals.uncachedInputTokens)} />
|
|
79
|
-
<Stat tone={css.toneOrange} name="output" label={t('outputFull')} value={formatCompact(detail.totals.outputTokens)} title={formatTokens(detail.totals.outputTokens)} sub={formatTokens(detail.totals.outputTokens)} />
|
|
80
|
-
<Stat tone={css.tonePurple} name="read" label={t('readFull')} value={formatCompact(detail.totals.cacheReadTokens)} title={formatTokens(detail.totals.cacheReadTokens)} sub={formatTokens(detail.totals.cacheReadTokens)} />
|
|
81
|
-
<Stat tone={css.tonePink} name="write" label={t('writeFull')} value={formatCompact(detail.totals.cacheWriteTokens)} title={formatTokens(detail.totals.cacheWriteTokens)} sub={formatTokens(detail.totals.cacheWriteTokens)} />
|
|
82
|
-
</div>
|
|
67
|
+
<Stat tone={css.toneBlue} name="requests" label={t('requestsCount')} value={formatTokens(detail.requests)} sub={`${t('failed')} ${failed} · ${failRate}%`} />
|
|
68
|
+
<Stat tone={css.tonePurple} name="tokens" label={t('tokens')} value={formatCompact(billed)} title={formatTokens(billed)} sub={`${formatTokens(billed)} tokens`} />
|
|
69
|
+
<Stat tone={css.toneTeal} name="input" label={t('inputFull')} value={formatCompact(detail.totals.uncachedInputTokens)} title={formatTokens(detail.totals.uncachedInputTokens)} sub={formatTokens(detail.totals.uncachedInputTokens)} />
|
|
70
|
+
<Stat tone={css.toneOrange} name="output" label={t('outputFull')} value={formatCompact(detail.totals.outputTokens)} title={formatTokens(detail.totals.outputTokens)} sub={formatTokens(detail.totals.outputTokens)} />
|
|
71
|
+
<Stat tone={css.toneGreen} name="hit" label={t('hit')} value={<Hit totals={detail.totals} />} sub={`${formatCompact(detail.totals.cacheReadTokens)} ${t('cacheHit')}`} />
|
|
72
|
+
<Stat tone={css.toneOrange} name="speed" label={t('speed')} value={speed(detail.tokensPerSecond)} sub={`${t('first')} ${seconds(detail.averageFirstTokenMs)}`} />
|
|
83
73
|
</div>
|
|
84
74
|
}
|
|
85
75
|
|
|
@@ -150,6 +140,11 @@ function Records({ detail, t }: { detail: SessionUsageDetail; t: T }): JSX.Eleme
|
|
|
150
140
|
}
|
|
151
141
|
group.rows.push(record)
|
|
152
142
|
}
|
|
143
|
+
// Newest turn on top: groups order by the time of their most recent request,
|
|
144
|
+
// not by first appearance, so the live work leads and finished history trails
|
|
145
|
+
// (records arrive root-session first, subagents after, regardless of time).
|
|
146
|
+
const latest = (group: { rows: Row[] }): number => group.rows.reduce((max, row) => Math.max(max, row.startedAt), 0)
|
|
147
|
+
groups.sort((a, b) => latest(b) - latest(a))
|
|
153
148
|
return <section className={css.card}><header className={css.cardHead}><h2>{t('records')} <span className={css.count}>{detail.records.length}</span></h2><button type="button" className={css.action} onClick={toggleAll}>{t(allOpen ? 'collapse' : 'expand')}</button></header>
|
|
154
149
|
<div className={css.turnList}>{groups.map(group => {
|
|
155
150
|
const open = allOpen !== opened.has(group.key)
|
|
@@ -175,8 +170,8 @@ function Records({ detail, t }: { detail: SessionUsageDetail; t: T }): JSX.Eleme
|
|
|
175
170
|
</span>
|
|
176
171
|
</button>
|
|
177
172
|
<div id={`usage-turn-${encodeURIComponent(group.key)}`} className={css.turnBody} hidden={!open}>
|
|
178
|
-
{/*
|
|
179
|
-
|
|
173
|
+
{/* Newest first inside and out: every turn lists its newest request
|
|
174
|
+
first, and turn groups themselves lead with the latest turn. */}
|
|
180
175
|
{group.rows.slice().reverse().map(record => {
|
|
181
176
|
const duration = record.finishedAt !== null ? record.finishedAt - record.startedAt : null
|
|
182
177
|
const billed = billedTotal(record.totals)
|