diff --git a/api/config.py b/api/config.py index 1bdd2a89f..737ee0f6f 100644 --- a/api/config.py +++ b/api/config.py @@ -7734,8 +7734,7 @@ _SETTINGS_DEFAULTS = { "onboarding_completed": False, "send_key": "enter", # 'enter' or 'ctrl+enter' "show_token_usage": False, # show input/output token badge below assistant messages - "show_quota_chip": True, # show ambient provider quota chip in composer footer by default - "show_quota_chip_opt_out": False, # post-default-flip marker for explicit user opt-out + "show_quota_chip": False, # show ambient provider quota chip in composer footer (default off; wide desktop only when enabled, see style.css @media) "show_conversation_outline": False, # show opt-in desktop jump-to-question outline panel "hide_empty_state_suggestions": False, # hide the default new-chat suggestion buttons "virtualize_transcript": False, # #4343: virtualize long (>80 msg) transcripts. EXPERIMENTAL, opt-IN (default OFF). Was opt-out/default-on in #4325 but caused scroll-up flicker on long sessions with tall tool-call rows (variable-height anchor oscillation) — flipped off for everyone in #4343; re-enabling requires an explicit opt-in (see virtualize_transcript_optin migration in load_settings). @@ -7935,11 +7934,6 @@ def load_settings() -> dict: # Honor a stored True only when that marker is present. if not bool(stored.get("virtualize_transcript_optin")): settings["virtualize_transcript"] = False - # show_quota_chip used to be persisted as False by default. After - # flipping it visible-by-default, treat an old stored False as - # stale unless a post-flip opt-out marker is present. - if not bool(stored.get("show_quota_chip_opt_out")): - settings["show_quota_chip"] = True except Exception: logger.debug("Failed to load settings from %s", SETTINGS_FILE) @@ -7984,7 +7978,6 @@ _SETTINGS_BOOL_KEYS = { "onboarding_completed", "show_token_usage", "show_quota_chip", - "show_quota_chip_opt_out", "show_conversation_outline", "hide_empty_state_suggestions", "virtualize_transcript", diff --git a/static/boot.js b/static/boot.js index 6481e2f14..b699b4efd 100644 --- a/static/boot.js +++ b/static/boot.js @@ -2370,7 +2370,7 @@ window._applyTitlebarProfileVisibility=_applyTitlebarProfileVisibility; // and workspace actions (New file/folder) work before the first session (#804). if(s.default_workspace) S._profileDefaultWorkspace=s.default_workspace; window._showTokenUsage=!!s.show_token_usage; - window._showQuotaChip=s.show_quota_chip!==false; + window._showQuotaChip=s.show_quota_chip===true; window._showConversationOutline=s.show_conversation_outline===true; document.documentElement.dataset.conversationOutline=window._showConversationOutline?'enabled':'disabled'; if(typeof applyConversationOutlinePreference==='function') applyConversationOutlinePreference(); @@ -2504,7 +2504,7 @@ window._applyTitlebarProfileVisibility=_applyTitlebarProfileVisibility; }catch(e){ window._sendKey='enter'; window._showTokenUsage=false; - window._showQuotaChip=true; + window._showQuotaChip=false; window._showConversationOutline=false; document.documentElement.dataset.conversationOutline='disabled'; if(typeof applyConversationOutlinePreference==='function') applyConversationOutlinePreference(); diff --git a/static/panels.js b/static/panels.js index 745c2268b..07f102aa1 100644 --- a/static/panels.js +++ b/static/panels.js @@ -7377,10 +7377,7 @@ function _preferencesPayloadFromUi(){ const showUsageCb=$('settingsShowTokenUsage'); if(showUsageCb) payload.show_token_usage=showUsageCb.checked; const showQuotaChipCb=$('settingsShowQuotaChip'); - if(showQuotaChipCb){ - payload.show_quota_chip=showQuotaChipCb.checked; - payload.show_quota_chip_opt_out=!showQuotaChipCb.checked; - } + if(showQuotaChipCb) payload.show_quota_chip=showQuotaChipCb.checked; const showConversationOutlineCb=$('settingsShowConversationOutline'); if(showConversationOutlineCb) payload.show_conversation_outline=showConversationOutlineCb.checked; const hideSuggestionsCb=$('settingsHideSuggestions'); @@ -7815,10 +7812,11 @@ async function loadSettingsPanel(){ maxTokensField.dataset.initialValue=maxTokensField.value; maxTokensField.addEventListener('input',_markSettingsDirty,{once:false}); } - // Ambient provider quota chip toggle — default on when provider quota data is available. + // Ambient provider quota chip toggle — default off; only shows at ≥1400px viewport + // when enabled (see style.css @media (max-width:1399.98px) rule). const showQuotaChipCb=$('settingsShowQuotaChip'); if(showQuotaChipCb){ - showQuotaChipCb.checked=settings.show_quota_chip!==false; + showQuotaChipCb.checked=settings.show_quota_chip===true; window._showQuotaChip=showQuotaChipCb.checked; showQuotaChipCb.addEventListener('change',()=>{ window._showQuotaChip=showQuotaChipCb.checked; @@ -10559,7 +10557,6 @@ async function saveSettings(andClose){ } } body.show_quota_chip=showQuotaChip===true; - body.show_quota_chip_opt_out=showQuotaChip!==true; body.show_conversation_outline=showConversationOutline===true; body.show_tps=showTps; body.fade_text_effect=fadeTextEffect; diff --git a/tests/test_quota_chip_settings_toggle.py b/tests/test_quota_chip_settings_toggle.py index 62530689b..13387873e 100644 --- a/tests/test_quota_chip_settings_toggle.py +++ b/tests/test_quota_chip_settings_toggle.py @@ -1,7 +1,7 @@ -"""Regression test for #show-quota-chip-toggle — Settings toggle for the ambient quota chip. +"""Regression test for #show-quota-chip-toggle — Settings toggle to opt into the ambient quota chip. -Quota chip default state is ON so provider quota is visible when available, while -users can still opt out via Settings -> Preferences. +Quota chip default state is now OFF (per Nathan's directive 2026-05-16, immediately +after the stage-371 release of #2082). Users opt in via Settings → Preferences. """ from pathlib import Path @@ -21,13 +21,11 @@ def test_quota_chip_settings_field_present(): assert 'data-i18n="settings_desc_quota_chip"' in html -def test_quota_chip_default_on_in_config_defaults(): +def test_quota_chip_default_off_in_config_defaults(): src = CONFIG.read_text(encoding="utf-8") - assert '"show_quota_chip": True' in src, "show_quota_chip must default to True" + assert '"show_quota_chip": False' in src, "show_quota_chip must default to False (opt-in)" # Must be in the writable settings allow-list (bool keys) assert '"show_quota_chip",' in src, "show_quota_chip must be in _SETTINGS_BOOL_KEYS" - assert '"show_quota_chip_opt_out": False' in src, "opt-out marker must default to False" - assert '"show_quota_chip_opt_out",' in src, "opt-out marker must be in _SETTINGS_BOOL_KEYS" def test_quota_chip_render_short_circuits_when_disabled(): @@ -50,8 +48,6 @@ def test_quota_chip_render_short_circuits_when_disabled(): assert guard_idx < text_call_idx, ( "Disabled-chip guard must run before the indicator-text computation" ) - assert "composerMobileQuotaAction" in render_body - assert "composerMobileQuotaLabel" in render_body # Refresher must short-circuit fetch when disabled refresh_start = js.index("async function refreshProviderQuotaIndicator(){") @@ -61,19 +57,17 @@ def test_quota_chip_render_short_circuits_when_disabled(): assert "window._showQuotaChip!==true" in refresh_head, ( "refreshProviderQuotaIndicator must skip the fetch when chip is disabled" ) - assert "composerMobileQuotaAction" in refresh_head - assert "composerMobileQuotaLabel" in refresh_head -def test_quota_chip_boot_initializes_default_on(): +def test_quota_chip_boot_initializes_default_off(): js = BOOT.read_text(encoding="utf-8") # Both success path (reads from settings) and failure path (defaults block) # must set window._showQuotaChip - assert "window._showQuotaChip=s.show_quota_chip!==false" in js, ( - "Boot must initialize _showQuotaChip from settings.show_quota_chip, defaulting on" + assert "window._showQuotaChip=s.show_quota_chip===true" in js, ( + "Boot must initialize _showQuotaChip from settings.show_quota_chip" ) - assert "window._showQuotaChip=true" in js, ( - "Boot must default _showQuotaChip to true in the settings-fetch-failed branch" + assert "window._showQuotaChip=false" in js, ( + "Boot must default _showQuotaChip to false in the settings-fetch-failed branch" ) @@ -82,12 +76,10 @@ def test_quota_chip_panels_round_trip(): # Payload read assert "const showQuotaChipCb=$('settingsShowQuotaChip');" in js assert "payload.show_quota_chip=showQuotaChipCb.checked;" in js - assert "payload.show_quota_chip_opt_out=!showQuotaChipCb.checked;" in js # Body assignment assert "body.show_quota_chip=showQuotaChip===true;" in js - assert "body.show_quota_chip_opt_out=showQuotaChip!==true;" in js # Settings panel load — checkbox is initialized from saved settings - assert "showQuotaChipCb.checked=settings.show_quota_chip!==false;" in js + assert "showQuotaChipCb.checked=settings.show_quota_chip===true;" in js # Window-state propagation assert "window._showQuotaChip=showQuotaChip===true;" in js # Live refresh on toggle (immediate visual feedback) @@ -98,35 +90,3 @@ def test_quota_chip_localized_in_all_locales(): js = I18N.read_text(encoding="utf-8") assert js.count("settings_label_quota_chip:") == 14, "12 locales expected" assert js.count("settings_desc_quota_chip:") == 14, "12 locales expected" - - -def test_quota_chip_migrates_old_persisted_false_without_opt_out(tmp_path, monkeypatch): - import json - import api.config as config - - settings_file = tmp_path / "settings.json" - settings_file.write_text( - json.dumps({"onboarding_completed": True, "show_quota_chip": False}), - encoding="utf-8", - ) - monkeypatch.setattr(config, "SETTINGS_FILE", settings_file) - - assert config.load_settings()["show_quota_chip"] is True - - -def test_quota_chip_honors_explicit_post_flip_opt_out(tmp_path, monkeypatch): - import json - import api.config as config - - settings_file = tmp_path / "settings.json" - settings_file.write_text( - json.dumps({ - "onboarding_completed": True, - "show_quota_chip": False, - "show_quota_chip_opt_out": True, - }), - encoding="utf-8", - ) - monkeypatch.setattr(config, "SETTINGS_FILE", settings_file) - - assert config.load_settings()["show_quota_chip"] is False