qwen-code/packages/web-shell/client/utils/contextUsage.ts
Shaojin Wen 27216eb4ca
feat(web-shell): show context usage as a mini progress pill in the status bar (#8794)
* feat(web-shell): show context usage as a mini progress pill in the status bar

Replace the plain "X% context used" text in the status bar with a compact
pill: a 52px progress bar plus the bare percentage. The fill follows the
same thresholds as the /context panel (>60% warning, >80% error), the fill
width caps at 100% while the number keeps reporting overflow, and the full
wording moves to aria-label so the accessible name is unchanged. Clicking
still opens the /context breakdown.

* fix(sdk): stop rendering usage_update frames as debug transcript text

The ui normalizer had no case for the usage_update session update, so the
frame fell through to the debug default and every model round appended a
raw-JSON bullet to the assistant turn in the web UI. Context occupancy is
surfaced by the status bar pill; the transcript drops the frame like
current_mode_update.

* feat(web-shell): move the context indicator into the composer toolbar as a ring

Review rework: replace the status-bar pill with a compact circular
progress ring in the composer toolbar's right cluster, immediately left
of the voice actions. The ring keeps the /context thresholds (>60%
warning, >80% error) and the visual 100% cap, hovers a Tooltip with the
full used/total detail (e.g. 53.6k / 1.0M tokens (5.4%)), keeps the full
wording on aria-label, and still opens /context on click. It ships as a
new contextUsage entry in composerToolbarActions so embedders can hide
it, hides while usage or the window is unknown, and follows the
toolbar's mobile-voice hiding. The StatusBar changes are reverted so the
indicator lives in exactly one place.

* fix(web-shell): let Radix position the shared tooltip arrow

The shared TooltipContent drew its arrow with a ::before pinned at the
content's horizontal center. Near a viewport edge Radix collision
avoidance shifts the content, so the arrow stopped pointing at the
trigger (about 35-40px off for the composer's context ring, which sits
at the far right). Replace the pseudo-element with TooltipPrimitive.Arrow,
which computes the offset from the trigger and the collision-shifted
content, keeping the tip on target for every tooltip and side.

* refactor(web-shell): share context-usage thresholds and token formatting (review)

Review round 3 suggestions:
- The 60/80 severity thresholds now live in one shared helper used by
  both the composer ring and the /context panel, so the two surfaces
  cannot drift.
- The ring tooltip's token formatter moves into the shared token-count
  utils and the /context panel uses it too, giving both surfaces the
  same k/M rendering (the panel previously showed a 1M window as
  1000.0k).
- The ring arc's transition is disabled under prefers-reduced-motion,
  matching the file's other decorative motion.
- New tests: exactly-80% stays warning (pins the strict threshold), the
  App wiring from connection usage to the ring props, click-through
  reaching the context-usage request, and the 0 fallbacks before any
  usage arrives.

* fix(sdk): resolve usage_update overlap with main

The same normalizer fix landed on main via #8790 while this PR was in
review; the merge auto-combined both edits into a duplicate case and a
duplicate test. Keep main's version — this branch now carries no
sdk-typescript delta.

* refactor(web-shell): consolidate the remaining token-count formatter copies (review)

Review round 4: the task-status panel's local formatter was
byte-identical to the shared one, and the collapsed-turn footer's copy
lacked the M branch — a collapsed turn with a >=1M-token input rendered
1048.6k while the ring tooltip and /context panel said 1.0M for the same
session. Both now import the shared formatter. Also pin the tooltip
arrow's positioning classes in the test, so a shadcn regeneration that
drops them fails instead of passing on a bare existence check.

* fix(web-shell): restore tooltip spacing and finish formatter consolidation (review)

Review round 5:
- With a Radix Arrow child, the offset middleware computes
  sideOffset + arrowHeight, so keeping the pseudo-element-era default of
  8 pushed every tooltip ~10px farther from its trigger. Default now 0;
  measured in a real browser the content edge sits 10px from the trigger
  (8px before the arrow change) with the tip 6px away.
- Drop the formatContextTokens-as-formatTokenCount aliases: the alias
  reused the exact name of the module's other, differently-behaving
  export, inviting wrong-import drift. Call sites use the real name.
- Colocate the pure-logic tests: utils/contextUsage.test.ts pins the
  strict-> boundaries, utils/formatTokenCount.test.ts owns the formatter
  cases (moved from ChatEditor.test.tsx), and ContextUsageMessage gains
  progress-bar color cases at 60/61/80/81 so the panel half of the
  shared-threshold contract is pinned too.
2026-08-10 03:48:06 +00:00

15 lines
555 B
TypeScript

/**
* Shared severity thresholds for context-window occupancy, used by the
* composer ring and the /context panel so the two surfaces never disagree
* about when usage turns warning or error.
*/
export const CONTEXT_USAGE_WARNING_PCT = 60;
export const CONTEXT_USAGE_ERROR_PCT = 80;
export type ContextUsageLevel = 'normal' | 'warning' | 'error';
export function getContextUsageLevel(pct: number): ContextUsageLevel {
if (pct > CONTEXT_USAGE_ERROR_PCT) return 'error';
if (pct > CONTEXT_USAGE_WARNING_PCT) return 'warning';
return 'normal';
}