mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 20:03:13 +08:00
3754d1f2a5
Sessions get a Chat / Trajectory switch in the header. The trajectory is a dense one-line-per-event ledger (system prompt, user, injected context such as skills and system reminders, assistant responses, tool calls) with a three-lane minimap, turn folding, search, and a detail panel per row. - CLI: record a deduplicated prompt snapshot sidecar for desktop sessions (system prompt, tool catalog, user context) without touching the transcript. - Server: project transcript records into trajectory rows with bounded, cursor-paged reads, live appends, a turn index, row detail by byte range, snapshot blobs, and a time-window lookup into the raw trace capture. - Desktop: windowed ledger, minimap, detail panel with a raw request summary, chat <-> trajectory navigation, subagent drill-in. - Remove the Settings trace list, trace tabs and the standalone trace window; migrate persisted trace tabs to session tabs.
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
import type { TraceCallUsage } from '../../types/trace'
|
|
import { formatTokenCount } from '../formatTokenCount'
|
|
import type { NormalizedUsage } from './types'
|
|
|
|
export { formatTokenCount }
|
|
|
|
export function formatDurationMs(ms?: number): string {
|
|
if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) return '--'
|
|
if (ms < 1000) return `${Math.round(ms)}ms`
|
|
if (ms < 10_000) return `${(ms / 1000).toFixed(2)}s`
|
|
if (ms < 60_000) return `${Math.round(ms / 1000)}s`
|
|
const totalSeconds = Math.round(ms / 1000)
|
|
const minutes = Math.floor(totalSeconds / 60)
|
|
const seconds = totalSeconds % 60
|
|
return `${minutes}m ${String(seconds).padStart(2, '0')}s`
|
|
}
|
|
|
|
export function formatUsageBrief(u?: NormalizedUsage | TraceCallUsage): string {
|
|
if (!u) return '--'
|
|
return `${formatTokenCount(u.inputTokens)} → ${formatTokenCount(u.outputTokens)}`
|
|
}
|
|
|
|
export function formatClockTime(iso: string): string {
|
|
const date = new Date(iso)
|
|
if (Number.isNaN(date.getTime())) return iso
|
|
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })
|
|
}
|
|
|
|
/** Pretty-print a JSON string or value; a non-JSON string passes through unchanged. */
|
|
export function formatTraceJson(value: unknown): string {
|
|
if (typeof value === 'string') {
|
|
const parsed = parseJsonText(value)
|
|
return parsed === null ? value : JSON.stringify(parsed, null, 2)
|
|
}
|
|
try {
|
|
// JSON.stringify(undefined) yields undefined, not a string.
|
|
return JSON.stringify(value, null, 2) ?? String(value)
|
|
} catch {
|
|
return String(value)
|
|
}
|
|
}
|
|
|
|
function parseJsonText(value: string): unknown | null {
|
|
const trimmed = value.trim()
|
|
if (!trimmed || (!trimmed.startsWith('{') && !trimmed.startsWith('['))) return null
|
|
try {
|
|
return JSON.parse(trimmed)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|