authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-02 03:55:23-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-02 03:55:23-04:00
loga19f01fc19d209d9962c4bb66372ed9e03e320a9
tree64546d40239c1a6b180310bc02d7e8145be07581
parent6ff537f03c4df500da7283c0566fe6f8feca709b
signature Signed by PGP key B5690EEEBB952194

feat(web): make context window indicator opt-in (#9190)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

6 files changed, 45 insertions(+), 1 deletions(-)

apps/desktop/src/settings/DesktopClientSettings.test.ts+1
......@@ -23,6 +23,7 @@ const clientSettings: ClientSettings = {
2323 confirmThreadArchive: true,
2424 confirmThreadDelete: false,
2525 confirmThreadUnpin: false,
26 contextWindowMeterEnabled: false,
2627 dismissedProviderUpdateNotificationKeys: [],
2728 diffIgnoreWhitespace: true,
2829 environmentIdentificationMode: "artwork",
apps/web/src/components/chat/ChatComposer.tsx+3-1
......@@ -4339,7 +4339,9 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
43394339 ) : null}
43404340 <ComposerFooterPrimaryActions
43414341 compact={isComposerPrimaryActionsCompact}
4342 activeContextWindow={activeContextWindow}
4342 activeContextWindow={
4343 settings.contextWindowMeterEnabled ? activeContextWindow : null
4344 }
43434345 activeThreadModelDisplayName={activeThreadModelDisplayName}
43444346 pendingAction={pendingPrimaryAction}
43454347 isRunning={phase === "running"}
apps/web/src/components/settings/SettingsPanels.tsx+19
......@@ -522,6 +522,9 @@ export function useSettingsRestore(onRestored?: () => void) {
522522 ...(settings.showSkillsInSlashMenu !== DEFAULT_UNIFIED_SETTINGS.showSkillsInSlashMenu
523523 ? ["Show skills in slash menu"]
524524 : []),
525 ...(settings.contextWindowMeterEnabled !== DEFAULT_UNIFIED_SETTINGS.contextWindowMeterEnabled
526 ? ["Context window indicator"]
527 : []),
525528 ...(settings.enableLegacyTokenStreaming !==
526529 DEFAULT_UNIFIED_SETTINGS.enableLegacyTokenStreaming
527530 ? ["Stream token by token"]
......@@ -576,6 +579,7 @@ export function useSettingsRestore(onRestored?: () => void) {
576579 settings.newWorktreesStartFromOrigin,
577580 settings.diffIgnoreWhitespace,
578581 settings.environmentIdentificationMode,
582 settings.contextWindowMeterEnabled,
579583 settings.fontFamilyCode,
580584 settings.fontFamilyComposer,
581585 settings.fontFamilySans,
......@@ -668,6 +672,7 @@ export function useSettingsRestore(onRestored?: () => void) {
668672 wordWrap: DEFAULT_UNIFIED_SETTINGS.wordWrap,
669673 diffIgnoreWhitespace: DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace,
670674 showSkillsInSlashMenu: DEFAULT_UNIFIED_SETTINGS.showSkillsInSlashMenu,
675 contextWindowMeterEnabled: DEFAULT_UNIFIED_SETTINGS.contextWindowMeterEnabled,
671676 environmentIdentificationMode: DEFAULT_UNIFIED_SETTINGS.environmentIdentificationMode,
672677 glassOpacity: DEFAULT_UNIFIED_SETTINGS.glassOpacity,
673678 sidebarThreadPreviewCount: DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount,
......@@ -1750,6 +1755,7 @@ function AutoSettleDaysInput({
17501755// expand the section before its target can mount and scroll.
17511756const LEGACY_FEATURE_TARGET_IDS: ReadonlySet<string> = new Set([
17521757 "legacy-plan-mode",
1758 "legacy-context-window-indicator",
17531759 "legacy-token-streaming",
17541760 "legacy-sidebar",
17551761]);
......@@ -1826,6 +1832,19 @@ function LegacyFeaturesSection() {
18261832 />
18271833 }
18281834 />
1835 <SettingsRow
1836 {...searchableSetting("legacy-context-window-indicator")}
1837 description="Shows context window usage as a circular indicator in the composer."
1838 control={
1839 <Switch
1840 checked={settings.contextWindowMeterEnabled}
1841 onCheckedChange={(checked) =>
1842 updateSettings({ contextWindowMeterEnabled: Boolean(checked) })
1843 }
1844 aria-label="Context window indicator (legacy)"
1845 />
1846 }
1847 />
18291848 <SettingsRow
18301849 serverScoped
18311850 {...searchableSetting("legacy-token-streaming")}
apps/web/src/components/settings/settingsSearch.ts+6
......@@ -261,6 +261,12 @@ export const SETTINGS_SEARCH_ITEMS = [
261261 to: "/settings/general",
262262 searchTerms: ["build plan composer old"],
263263 },
264 {
265 id: "legacy-context-window-indicator",
266 title: "Context window indicator (legacy)",
267 to: "/settings/general",
268 searchTerms: ["composer meter usage tokens circle old"],
269 },
264270 {
265271 id: "legacy-token-streaming",
266272 title: "Stream token by token (legacy)",
packages/contracts/src/settings.test.ts+12
......@@ -191,6 +191,18 @@ describe("ClientSettings sidebar", () => {
191191 });
192192});
193193
194describe("ClientSettings context window meter", () => {
195 it("defaults off and preserves an explicit legacy opt-in", () => {
196 expect(decodeClientSettings({}).contextWindowMeterEnabled).toBe(false);
197 expect(
198 decodeClientSettings({ contextWindowMeterEnabled: true }).contextWindowMeterEnabled,
199 ).toBe(true);
200 expect(
201 decodeClientSettingsPatch({ contextWindowMeterEnabled: true }).contextWindowMeterEnabled,
202 ).toBe(true);
203 });
204});
205
194206describe("ServerSettings thread settlement", () => {
195207 it("defaults merge settlement on and inactivity settlement to three days", () => {
196208 const settings = decodeServerSettings({});
packages/contracts/src/settings.ts+4
......@@ -265,6 +265,9 @@ export const ClientSettingsSchema = Schema.Struct({
265265 // default UI; this beta flag restores it (plus the /plan and /default slash
266266 // commands) for users who still rely on the old workflow.
267267 planModeEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
268 // Legacy context window meter. The composer hides it by default; users who
269 // still want the old usage indicator can restore it from Settings.
270 contextWindowMeterEnabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
268271 showSkillsInSlashMenu: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
269272 // Legacy sidebar (the original per-project tree). Deliberately a fresh key
270273 // (was `sidebarV2Enabled` + `sidebarV2ConfiguredByUser`): decoding drops the
......@@ -990,6 +993,7 @@ export const ClientSettingsPatch = Schema.Struct({
990993 ),
991994 ),
992995 planModeEnabled: Schema.optionalKey(Schema.Boolean),
996 contextWindowMeterEnabled: Schema.optionalKey(Schema.Boolean),
993997 showSkillsInSlashMenu: Schema.optionalKey(Schema.Boolean),
994998 legacySidebarEnabled: Schema.optionalKey(Schema.Boolean),
995999 sidebarProjectGroupingMode: Schema.optionalKey(SidebarProjectGroupingMode),