Files
dsh-context/src/client/headline.ts
T
bowenliang123 bf4cdd1e58 feat: add /context slash command with a centered context modal
Typing /context in the composer opens a centered dialog with the current
composition (provider-anchored headline, stacked bar, legend) and the
last-10-turn trend chart — the same pushed contextTimeline projection the
tab renders, no RPC, no polling.

The command is the plugin's own '/' trigger source, fully client-side:
no host descriptor, no session log records, nothing model-visible. The
composer token is consumed only when the modal closes (deferred
slash/input-consume-token with span / bare-token guards, fail-soft if the
draft changed meanwhile). Esc, backdrop click, and the close button all
close it and restore composer focus.

Also extracts the provider-anchored headline derivation shared by the tab
and the modal (headline.ts).
2026-08-16 22:26:46 +08:00

43 lines
2.1 KiB
TypeScript

/**
* Provider-anchored headline derivation, shared by the Context tab and the
* /context popup: the best-known occupancy of the next request, the route
* capacity it scales against, and the composition parts anchored to that
* total (heuristic ratios, provider-anchored sum — the same trick the
* official ContextMeter uses).
*/
import type { ContextTimeline } from '../shared/types'
import { anchoredParts, partsOf, type PartsPart } from './categories'
export interface Headline {
/** Best-known occupancy of the next request (projected ?? derived ?? heuristic total). */
tokens: number
/** Route capacity the headline scales against (may be unknown). */
window?: number
/** tokens / window, clamped to 100; null without a window. */
pct: number | null
/** Composition parts anchored to the provider total when one exists. */
parts: PartsPart[]
/** True when no provider anchor applied (parts sum to the heuristic total). */
estimated: boolean
}
export function headlineOf(data: ContextTimeline): Headline {
const current = data.current
const occ = data.occupancy
const projected = occ !== undefined && typeof occ.projectedTokens === 'number' ? occ.projectedTokens : undefined
const requests = data.requests || []
const lastReq = requests.length > 0 ? requests[requests.length - 1] : null
// Fallback anchor: the newest request's provider prompt plus the heuristic
// surface movement since it was logged.
const derived = lastReq !== null && typeof lastReq.prompt === 'number'
? lastReq.prompt + (current.total - lastReq.total)
: undefined
const occupancyTokens = projected ?? derived ?? null
const window = occ !== undefined && typeof occ.contextWindow === 'number' ? occ.contextWindow : data.contextWindow
const tokens = occupancyTokens ?? current.total
const pct = window !== undefined && window > 0 ? Math.min(100, Math.round(tokens / window * 100)) : null
const parts = anchoredParts(partsOf(current), occupancyTokens !== null && tokens > 0 ? tokens : null)
return { tokens, window, pct, parts, estimated: occupancyTokens === null }
}