diff --git a/src/client/format.ts b/src/client/format.ts index e17e588..87a4d79 100644 --- a/src/client/format.ts +++ b/src/client/format.ts @@ -39,16 +39,16 @@ export function fmtTime(t: number): string { /** * Share of a whole as a compact leading percentage for the slice rows: - * '—' when nothing totals, '0%' for empty slices, '<1%' for non-zero crumbs - * a rounded-down integer would erase. Shares cap at 100% (parallel tool time - * can over-run the wall it belongs to). + * '—' when nothing totals, '0.0%' for empty slices, '<0.1%' for non-zero + * crumbs a 0.1%-precision figure would erase. One decimal everywhere; shares + * cap at 100% (parallel tool time can over-run the wall it belongs to). */ export function fmtShare(part: number, total: number): string { if (!Number.isFinite(part) || !Number.isFinite(total) || total <= 0) return '—' - if (part <= 0) return '0%' + if (part <= 0) return '0.0%' const pct = Math.min(1, part / total) * 100 - if (pct < 1) return '<1%' - return `${Math.round(pct)}%` + if (pct < 0.1) return '<0.1%' + return `${pct.toFixed(1)}%` } /** diff --git a/tests/client/components/statsTiming.spec.ts b/tests/client/components/statsTiming.spec.ts index bb6474a..3058890 100644 --- a/tests/client/components/statsTiming.spec.ts +++ b/tests/client/components/statsTiming.spec.ts @@ -47,10 +47,10 @@ describe('StatsTiming', () => { assert.ok(text(m.container).includes('Timing Stats')) assert.equal(query(m.container, '.lc-donut-center b').textContent, '10m 0s') assert.equal(query(m.container, '.lc-donut-center span').textContent, 'Active Time') - assert.deepEqual(rowOf(m.container, 0), { pct: '40%', label: 'Model calls', count: '4m 0s · 10 calls', dim: false }) + assert.deepEqual(rowOf(m.container, 0), { pct: '40.0%', label: 'Model calls', count: '4m 0s · 10 calls', dim: false }) // The tools row keeps the true 5m sum even though the ring clamps it. - assert.deepEqual(rowOf(m.container, 1), { pct: '50%', label: 'Tool runs', count: '5m 0s · 25 runs', dim: false }) - assert.deepEqual(rowOf(m.container, 2), { pct: '10%', label: 'Overhead', count: '1m 0s', dim: false }) + assert.deepEqual(rowOf(m.container, 1), { pct: '50.0%', label: 'Tool runs', count: '5m 0s · 25 runs', dim: false }) + assert.deepEqual(rowOf(m.container, 2), { pct: '10.0%', label: 'Overhead', count: '1m 0s', dim: false }) // Three ring segments painted (lm + tools + overhead); exactly three rows. assert.equal(queryAll(m.container, '.lc-donut circle').length, 3) assert.equal(queryAll(m.container, '.lc-sl-row').length, 3) @@ -60,7 +60,7 @@ describe('StatsTiming', () => { test('the zh locale formats durations and counts in Chinese units', async () => { const m = await mount(h(StatsTimingZh, { timing: TIMING, locale: 'zh' })) assert.equal(query(m.container, '.lc-donut-center b').textContent, '10分0秒') - assert.deepEqual(rowOf(m.container, 0), { pct: '40%', label: '模型调用', count: '4分0秒 · 10次', dim: false }) + assert.deepEqual(rowOf(m.container, 0), { pct: '40.0%', label: '模型调用', count: '4分0秒 · 10次', dim: false }) await m.unmount() }) @@ -68,12 +68,12 @@ describe('StatsTiming', () => { // 9 parallel 22s calls inside a 100s wall: tools sum 200s > wall. const timing: TimingTotals = { wallMs: 100_000, lmMs: 60_000, calls: 2, toolsMs: 200_000, toolCalls: 9, tools: { bash: { calls: 9, ms: 200_000 } } } const m = await mount(h(StatsTiming, { timing, locale: 'en' })) - assert.deepEqual(rowOf(m.container, 1), { pct: '100%', label: 'Tool runs', count: '3m 20s · 9 runs', dim: false }) + assert.deepEqual(rowOf(m.container, 1), { pct: '100.0%', label: 'Tool runs', count: '3m 20s · 9 runs', dim: false }) // The ring clamps tools into the 40s post-model window; the zero // overhead segment is skipped: 2 circles, not 3. assert.equal(queryAll(m.container, '.lc-donut circle').length, 2) // The zero overhead row dims whole. - assert.deepEqual(rowOf(m.container, 2), { pct: '0%', label: 'Overhead', count: '—', dim: true }) + assert.deepEqual(rowOf(m.container, 2), { pct: '0.0%', label: 'Overhead', count: '—', dim: true }) // The hover link: the zero-overhead row tints itself but leaves the ring // at rest (no painted arc to light); a painted row dims the ring. const rows = queryAll(m.container, '.lc-sl-row') diff --git a/tests/client/components/statsTokens.spec.ts b/tests/client/components/statsTokens.spec.ts index 1b620cf..189eead 100644 --- a/tests/client/components/statsTokens.spec.ts +++ b/tests/client/components/statsTokens.spec.ts @@ -33,9 +33,9 @@ describe('StatsTokens', () => { assert.equal(query(m.container, '.lc-donut-center span').textContent, 'Cache Hit') // Zero buckets stay hidden: the always-zero DeepSeek cache write drops out. assert.equal(queryAll(m.container, '.lc-sl-row').length, 3) - assert.deepEqual(rowOf(m.container, 0), { pct: '67%', label: 'Cached Input', count: '300' }) - assert.deepEqual(rowOf(m.container, 1), { pct: '22%', label: 'Uncached Input', count: '100' }) - assert.deepEqual(rowOf(m.container, 2), { pct: '11%', label: 'Output', count: '50 · incl. reasoning' }) + assert.deepEqual(rowOf(m.container, 0), { pct: '66.7%', label: 'Cached Input', count: '300' }) + assert.deepEqual(rowOf(m.container, 1), { pct: '22.2%', label: 'Uncached Input', count: '100' }) + assert.deepEqual(rowOf(m.container, 2), { pct: '11.1%', label: 'Output', count: '50 · incl. reasoning' }) await m.unmount() }) @@ -58,10 +58,10 @@ describe('StatsTokens', () => { // Hit = 300 / (100 + 300 + 50) = 66.66%; rows normalize over billed 500. assert.equal(query(m.container, '.lc-donut-center b').textContent, '66.66%') assert.equal(queryAll(m.container, '.lc-sl-row').length, 4) - assert.deepEqual(rowOf(m.container, 0), { pct: '60%', label: 'Cached Input', count: '300' }) - assert.deepEqual(rowOf(m.container, 1), { pct: '10%', label: 'Cache Write', count: '50' }) - assert.deepEqual(rowOf(m.container, 2), { pct: '20%', label: 'Uncached Input', count: '100' }) - assert.deepEqual(rowOf(m.container, 3), { pct: '10%', label: 'Output', count: '50 · incl. reasoning' }) + assert.deepEqual(rowOf(m.container, 0), { pct: '60.0%', label: 'Cached Input', count: '300' }) + assert.deepEqual(rowOf(m.container, 1), { pct: '10.0%', label: 'Cache Write', count: '50' }) + assert.deepEqual(rowOf(m.container, 2), { pct: '20.0%', label: 'Uncached Input', count: '100' }) + assert.deepEqual(rowOf(m.container, 3), { pct: '10.0%', label: 'Output', count: '50 · incl. reasoning' }) await m.unmount() }) diff --git a/tests/client/format.spec.ts b/tests/client/format.spec.ts index d6f0083..b13f611 100644 --- a/tests/client/format.spec.ts +++ b/tests/client/format.spec.ts @@ -77,18 +77,21 @@ describe('fmtShare', () => { assert.equal(fmtShare(5, Infinity), '—') }) - test('empty slices are 0%; crumbs below a point round up to <1%', () => { - assert.equal(fmtShare(0, 100), '0%') - assert.equal(fmtShare(-1, 100), '0%') - assert.equal(fmtShare(0.5, 100), '<1%') - assert.equal(fmtShare(1, 300), '<1%') + test('empty slices are 0.0%; crumbs below 0.1% degrade to <0.1%', () => { + assert.equal(fmtShare(0, 100), '0.0%') + assert.equal(fmtShare(-1, 100), '0.0%') + assert.equal(fmtShare(1, 2000), '<0.1%') + assert.equal(fmtShare(1, 1000), '0.1%') + assert.equal(fmtShare(0.5, 100), '0.5%') + assert.equal(fmtShare(1, 300), '0.3%') }) - test('shares round to whole percents and cap at 100', () => { - assert.equal(fmtShare(844, 1000), '84%') - assert.equal(fmtShare(996, 1000), '100%') - assert.equal(fmtShare(3000, 1000), '100%') - assert.equal(fmtShare(1, 3), '33%') + test('shares carry one decimal and cap at 100', () => { + assert.equal(fmtShare(844, 1000), '84.4%') + assert.equal(fmtShare(738.4, 1000), '73.8%') + assert.equal(fmtShare(996, 1000), '99.6%') + assert.equal(fmtShare(3000, 1000), '100.0%') + assert.equal(fmtShare(1, 3), '33.3%') }) })