The card's reading was the ring's integer (`Math.round`), so a 101.6k / 1.0M session printed 10% while the kernel's own occupancy read 10.2%. The percent now comes from one shared `occupancyPercent` helper that mirrors dsh's `contextOccupancy` — the composer ring's derivation (`min(100, round(used / window * 100))`, `used` = `contextPressure`'s `projectedTokens ?? pressureTokens`) — on a 0.1 grid, and keeps the kernel's numerator precedence ahead of the plugin's derived anchor. The kernel symbol is unreachable from a client plugin (no package export, a self-registering browser bundle), so the formula is mirrored by necessity, as docs/compatibility.md now records. The Agent Network rings read the same helper (whole tenths stay compact there), and the number row wraps so the wider reading folds to its own line instead of spilling on the narrowest panes.
58 lines
2.9 KiB
TypeScript
58 lines
2.9 KiB
TypeScript
// CurrentComposition (src/client/components/currentComposition.tsx) rendered
|
|
// with real React over the real StackedBar/Legend pair.
|
|
|
|
import { createElement as h } from 'react'
|
|
import assert from 'node:assert/strict'
|
|
import { describe, test } from 'vitest'
|
|
import { makeCurrentComposition } from '../../../src/client/components/currentComposition'
|
|
import { makeLegend, makeStackedBar } from '../../../src/client/components/stackedBar'
|
|
import type { Headline } from '../../../src/client/headline'
|
|
import { hover, makeKit, mount, query, queryAll, text } from '../helpers/kit'
|
|
|
|
const kit = makeKit()
|
|
const CurrentComposition = makeCurrentComposition(kit, makeStackedBar(kit), makeLegend(kit))
|
|
|
|
const PARTS = [{ key: 'user', color: '#123456', value: 5000 }]
|
|
|
|
function headOf(over: Partial<Headline> = {}): Headline {
|
|
return { tokens: 5000, pct: null, parts: PARTS, ...over }
|
|
}
|
|
|
|
describe('CurrentComposition header', () => {
|
|
test('windowed head: tokens/window figure, used percentage, reserve band, subtitle', async () => {
|
|
const m = await mount(h(CurrentComposition, {
|
|
head: headOf({ window: 10000, pct: 50.4 }),
|
|
subtitle: 'deepseek-v4-flash',
|
|
}))
|
|
assert.ok(text(m.container).includes('Current Context'))
|
|
assert.equal(query(m.container, '.lc-card-sub').textContent, 'deepseek-v4-flash')
|
|
const num = query(m.container, '.lc-overview-num')
|
|
assert.ok(text(num).includes('5.0k / 10.0k tokens'))
|
|
// The reading keeps one decimal, a whole tenth included (headline.ts).
|
|
assert.equal(query(num, '.lc-overview-pct b').textContent, '50.4%')
|
|
await m.update(h(CurrentComposition, { head: headOf({ window: 10000, pct: 50 }) }))
|
|
assert.equal(query(num, '.lc-overview-pct b').textContent, '50.0%')
|
|
assert.ok(text(num).includes('of context used'))
|
|
// The reserve band mirrors the 80% auto-compaction threshold.
|
|
const band = query(m.container, '.lc-reserve')
|
|
await hover(band)
|
|
const tip = query(m.container, '.lc-bar-tip')
|
|
assert.ok(tip.textContent!.includes('Auto-compaction reserve'))
|
|
assert.ok(tip.textContent!.includes('80%'))
|
|
await m.unmount()
|
|
})
|
|
|
|
test('windowless head: estimate label, no percentage, no reserve, no subtitle', async () => {
|
|
const m = await mount(h(CurrentComposition, { head: headOf() }))
|
|
assert.ok(text(m.container).includes('tokens (estimated)'))
|
|
assert.equal(queryAll(m.container, '.lc-overview-pct').length, 0)
|
|
assert.equal(queryAll(m.container, '.lc-reserve').length, 0)
|
|
assert.equal(queryAll(m.container, '.lc-card-sub').length, 0)
|
|
// An empty subtitle renders nothing either; a zero window stays reserve-free.
|
|
await m.update(h(CurrentComposition, { head: headOf({ window: 0 }), subtitle: '' }))
|
|
assert.equal(queryAll(m.container, '.lc-card-sub').length, 0)
|
|
assert.equal(queryAll(m.container, '.lc-reserve').length, 0)
|
|
assert.ok(text(m.container).includes('tokens (estimated)'))
|
|
await m.unmount()
|
|
})
|
|
}) |