Skip to content

Chat font and max-output-token settings, plus a settings save that no longer drops keys - #225

Open
leonidasbarkas98-cpu wants to merge 26 commits into
andrepimenta:mainfrom
leonidasbarkas98-cpu:feat/settings-modal-polish
Open

leonidasbarkas98-cpu wants to merge 26 commits into
andrepimenta:mainfrom
leonidasbarkas98-cpu:feat/settings-modal-polish

Conversation

@leonidasbarkas98-cpu

Copy link
Copy Markdown

Stacked on top of #223 — please review/merge that one first; only the last 12 commits (from 9c03a2e onwards) are new here. Everything below 9c03a2e in the diff belongs to that PR.

This branch bundles three settings the chat UI was missing and the fix for a settings-save path that silently dropped keys:

  • A shortcut for raising the output token limit. New claudeCodeChat.advanced.maxOutputTokens setting (integer, 0 = CLI default) feeds CLAUDE_CODE_MAX_OUTPUT_TOKENS on both the native and the WSL spawn path. When the CLI reports API Error: ... output token maximum — which arrives as ordinary assistant text, so it is matched there — the chat shows a hint with a button that opens the settings UI already filtered to that setting, instead of leaving the user to guess which env variable the CLI wanted.
  • Configurable chat font family and size. Two new settings under claudeCodeChat.ui apply through CSS custom properties set with style.setProperty, so nothing is interpolated into markup. The .messages/.input-field font-size rules and the per-bubble font-family rules (user, tool-result, thinking) all read the variables so a custom font stays consistent across bubble types, and the input box re-measures its height when the size changes.
  • All three surfaced in the gear modal. The settings exist in the manifest, but the in-chat settings modal is where users actually look; they now round-trip through the existing updateSettings/settingsData mechanism and are clamped the same way the manifest declares. claudeCodeChat.advanced is added to the config-change broadcast so a second open surface cannot overwrite a fresh value with a stale one.
  • One failing settings key no longer discards the rest of the batch. _updateSettings wrote the whole batch from the webview in a single loop inside one try/catch, so a rejected config.update() — for instance a setting not yet registered right after a version bump — aborted the loop and silently dropped every key after it. In the order the webview sends them, a rejection on advanced.maxOutputTokens would have taken environment.variables, router.enabled, diff.autoOpen, ui.fontFamily and ui.fontSize with it, with no indication in the UI. Each key now gets its own try/catch through a new vscode-free src/settings-batch.ts: failures are collected rather than thrown, _sendCurrentSettings() and the balance refresh still run on partial failure, and a single summary error names every failed key plus the first error's reason. Covered by a mocha suite (npm run test:settings-batch).
  • Enabling YOLO mode no longer zeroes those settings. enableYoloMode() — the button on every permission-error banner — flips the checkbox and calls updateSettings(), which now also reads the three fields above. Those are only populated by the settingsData round-trip that runs when the gear modal opens, so clicking Enable YOLO Mode without ever opening the modal left them at their empty '' default: parseInt('')NaN was clamped to 0 and sent as advanced.maxOutputTokens/ui.fontSize, resetting a real configured value. updateSettings() now only includes those keys when their DOM element actually holds a value.

The remaining commits are review follow-ups on the above, not new behaviour: a settings-batch callback parameter that no real caller ever passed was removed together with its tests and the comment that justified it (the comment described a log line _updateSettings does not write and a function that does not exist in this codebase); an overstated claim about the config-change broadcast in a code comment was corrected against the one listener that actually exists; and test fixtures/comments referencing setting keys and sibling modules that do not exist in this branch were replaced with real ones. Comments referencing our fork's internal issue tracker are written as fork-issue-NN so they do not auto-link to unrelated issues on this repository.

This is one of several PRs from our fork submitted together as part of a broader, thematically grouped series (permission/session handling, settings/UI, security hardening, rendering, etc. split across separate branches/PRs). Note that src/settings-batch.ts also exists, with a different API and a different purpose (workspace-then-global write fallback), in the security-hardening PR of this series — an add/add conflict is expected if both are merged, and the two are straightforward to reconcile since neither imports the other.

tsc --noEmit clean, no new dependencies; full unit run at this tip is 80 passing, 0 failing.

🤖 Generated with Claude Code

Jonas Kunert and others added 26 commits July 28, 2026 13:11
Adds a fork-issue-27 context indicator to the status bar, together with
the Claude subscription usage: five-hour window and weekly utilization
percentages with reset times, fetched from the unofficial OAuth usage
endpoint using the CLI's own OAuth token, throttled to one request per
five minutes and failing silently (stale or hidden display) on any
error. A new rate_limit_event case additionally captures the five-hour
reset time for free from the CLI stream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The usage indicator's _usageLimits type gains sevenDayOpus/sevenDaySonnet
fields, and the oauth/usage response parser now reads the per-model-tier
weekly buckets seven_day_opus (shown as Fable) and seven_day_sonnet
through the existing defensive parseWindow, alongside the five-hour and
week windows it already handled. getUsageIndicatorHtml renders both new
buckets in the status-bar usage indicator when the account's usage
response includes them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ue-35)

The oauth/usage endpoint now returns per-model weekly windows only as
limits[] entries (kind "weekly_scoped" with a model scope, field
"percent", 0-100 scale); the legacy seven_day_opus/seven_day_sonnet
fields are null. Fall back to those entries, preferring is_active ones.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ssue-38)

WSL-to-Windows path mapping, case-insensitive workspace-relative path
computation (undefined for the root itself and anything outside), NUL-based
binary detection and the deterministic claude-diff URI build/parse pair.
vscode-free so the 21-case mocha suite (test:diff-utils) runs headless.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…point (fork-issue-38)

Replaces the racy fileContentBefore diff (button hidden on tool_result,
one-slot pending data, per-click Date.now tabs, global renderSideBySide
mutation, dead openDiffByIndex path) with vscode.diff against the shadow
backup repo: left side is the checkpoint before the turn (git show via
execFile, 16MB buffer, 2MB/binary guards, BOM strip), right side is the
real editable file; stable per-(sha,path) URIs dedupe tabs and the
provider re-resolves restored tabs itself. The Open Diff button stays
visible permanently (dataset-based, works after history load via the
persisted showRestoreOption sha). Auto-opens once per file per turn after
a successful Edit/MultiEdit/Write with preserveFocus, silently skipping
files outside the workspace (scratchpad/memory edits are routine);
gitignored paths never fake an all-new baseline. Configurable via
claudeCodeChat.diff.autoOpen (default on) in settings and the modal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…the 5-minute cache (fork-issue-54)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… check (fork-issue-50)

_commits is cleared when a history load switches conversations, but the
replayed showRestoreOption messages keep their Restore buttons alive, so
_restoreToCommit answered every click with "Commit not found". On a
_commits miss, confirm the sha directly against the shadow backup repo
(execFile git cat-file -e <sha>^{commit}) and rehydrate the display info
from the replayed entry; shas are format-validated before reaching any
git command. Live-session behavior is unchanged, and a genuinely missing
commit still reports "Commit not found". New pure helpers in
restore-commit-utils.ts with 15 unit tests (test:restore-commit-utils).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
extension.ts's _usageLimits comment still described the opus-lineage
bucket as covering "the Fable model shown to users" -- there is no
Fable model in this branch (_setSelectedModel only accepts
opus/sonnet/default), so that clause is just wrong; dropped it.

Also fixes the usage-limits status line (src/script.ts): the German
"Wo" abbreviation becomes "Week", and the sevenDayOpus bucket's "Fable"
label becomes "Opus" (it's the Opus-tier weekly window, mirroring the
existing "Sonnet" label for sevenDaySonnet) instead of naming a model
that isn't selectable in this branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
getContextWindow() claimed a 1M-token native window for opus/sonnet
(and a fable entry that isn't even selectable in this branch) instead
of the real 200K, so the status-bar Ctx percentage under-reported
usage. Removed the fable entry and reset opus/sonnet to 200000.

Also cleaned up references that only made sense inside the private
fork: two comments pointed at shell-utils/auto-model-switch, a module
already removed from this branch and one that never existed here; a
few inline code-review shorthand notes were shortened; example paths
using a real name became generic placeholders; and bare #NN issue
markers now read fork-issue-NN so they aren't mistaken for this
project's own issue tracker.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…line

The fork-issue-27 context indicator (getContextIndicatorHtml) was reading
a currentContextTokens variable that was never assigned anywhere, because
the extension.ts half of the plumbing (the _currentContextTokens field,
computing it per turn, and sending it on the updateTokens postMessage) had
been dropped. The function silently always returned '', which had quietly
replaced the previous "N tokens" status-line display with nothing for
non-subscription users.

Restores the field, the per-turn ctx calculation (input + cache read +
cache creation tokens), the three reset points (compact boundary, new
session, loaded conversation), and the webview-side assignment/resets in
script.ts. Also adds the missing .ctx-warn/.ctx-crit CSS rules the 80%/95%
warning classes rely on, which existed nowhere in ui-styles.ts.
A first pass missed several inline code-review shorthand notes sprinkled
through the fork-issue-38 turn-diff code. Neutralized all 15 spots,
keeping the substantive explanation in each comment.
_permLog (fork-issue-15) writes unrotated plaintext to os.tmpdir(), so the
auto-skip diagnostic for _openTurnDiffFallback was leaking the full,
potentially sensitive file path on every skip. path.basename(filePath) is
enough to diagnose which file was involved without exposing the path.
The diff-content-provider comment justified skipping per-panel routing
by pointing at a "New Claude Chat (Separate)" (fork-issue-24) feature
that doesn't exist in this branch (no such command in package.json, no
second panel type) -- reworded to describe what's actually true: a
single shared ClaudeChatProvider instance backs both the panel command
and the sidebar webview, so there's only ever one provider to resolve
against regardless of who opened the tab.

Also removes two blank lines that an earlier commit in this branch
introduced incidentally (one in extension.ts before the
rate_limit_event usage-limits refresh, one in script.ts before
renderEnvVariables) and that don't match the surrounding code's
spacing.
…eak, fix stale cat-file comment

Review-round follow-up: KNOWN_ENDPOINT_MARKERS was declared but never
read -- _isOpenCredits() kept checking the same two literals by hand.
Now _isOpenCredits() actually uses the constant, so there's a single
place to add a future endpoint marker.

_openTurnDiffFallback's auto-skip perm-log line still leaked the full
backup-repo/file path through its reason= field even after the earlier
basename-only fix for file=: _resolveTurnDiffBaseline wraps raw
execFile failures whose message starts with the full command line.
reason now goes through the same basename collapsing before logging.

Also corrects a comment that misattributed a "plain cat-file, exit 1"
contrast to _isPathIgnoredInBackupRepo (which uses `git check-ignore
-q`, not cat-file) instead of the actual unpeeled `git cat-file -e`
case it meant to describe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ssue-42)

New setting claudeCodeChat.advanced.maxOutputTokens (integer, 0 = CLI
default) feeds the env variable on both the native and the WSL spawn
path. When the CLI reports 'API Error: ... output token maximum' - it
arrives as assistant text, verified against the live CLI - the chat
shows a hint with a button that opens the settings UI filtered to the
new setting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two new settings under claudeCodeChat.ui apply via CSS variables set
with style.setProperty (no markup interpolation). The .messages and
.input-field font-size rules, and the per-bubble font-family rules
(user, tool-result, thinking), respect the variables so the custom
font stays consistent, and the input height re-measures when the size
changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…modal (fork-issue-44, fork-issue-42)

The three settings already exist in the manifest; this surfaces them in the
gear modal via the existing updateSettings/settingsData roundtrip, clamps
them like the manifest does, and adds claudeCodeChat.advanced to the
config-change broadcast so a second open surface cannot overwrite a fresh
value with a stale one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_updateSettings ran the whole settings batch from the webview in one loop
wrapped in a single try/catch, so a rejected config.update() -- e.g. a
setting not yet registered right after a version bump -- aborted the loop
and silently dropped every key that came after it in the same batch. In
the order the webview sends them, a rejection on e.g.
advanced.maxOutputTokens would have dropped environment.variables,
router.enabled, diff.autoOpen, ui.fontFamily and ui.fontSize with no
indication in the UI.

Each key now gets its own try/catch through the new vscode-free
settings-batch module: failures are collected instead of aborting,
_sendCurrentSettings() and the balance refresh run even on partial
failure, and one summary error message names every failed key plus the
first error's reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rk-issue-56)

applySettingsBatch's onSettled callback was never wired up by any real
caller (extension.ts's _updateSettings only ever passes two arguments) --
only its own tests exercised it. The "review follow-up" comment that
justified it was also wrong: _updateSettings writes no perm-log line, and
the refreshSettingsOnConfigChange function it names does not exist
anywhere in this codebase. Dropped the parameter, its tests, and the
misleading comment.
…tings-modal commits

Bare #NN in comments/suite titles auto-links to this fork's own GitHub
issues instead of our private tracker. Requalified as fork-issue-NN in
extension.ts, script.ts, settings-batch.ts and its test suite (including
the suite title, which shows up verbatim in the mocha reporter output).
Genuine "upstream andrepimenta#150" references are left untouched.
That commit's message says it "adds claudeCodeChat.advanced to the
config-change broadcast", but it only adds a key to the plain
settingsData payload _sendCurrentSettings already sends. There is no
onDidChangeConfiguration/affectsConfiguration listener for it -- the
only one (activate(), above) still filters on claudeCodeChat.wsl only.
6257620 isn't the tip of this stack, so a code comment corrects the
record instead of an amend.
…ling-file refs

The module comment and toErrorMessage's comment both claimed extension.ts's
injected updateSetting callback (and _updateSettings) call _permLog as a
side effect; neither does -- _permLog's only call sites (extension.ts:3893,
3971, 4304) are the unrelated usageLimits/turndiff logging. Named the real
caller (_updateSettings) instead.

Both files also pointed at "shell-utils" and "perm-log-redact"/
"markdown-restore" as sibling test-pattern files; none of those exist in
this branch, only restore-commit-utils does. Corrected the references, and
fixed the malformed-input test's title, which named a
"extension.ts's key=<batch> marker" that doesn't exist -- the actual outer
catch in _updateSettings just logs and shows an error message.
settings-batch.ts's module comment and the matching test fixture both
invented a "real-world hit on 2026-07-26" involving ui.renderMath and
six settings (font family/size, completion popup/sound, send-on-enter)
that don't exist anywhere in this branch's package.json -- the only
settings registered here are wsl.*, thinking.intensity,
permissions.yoloMode, executable.path, advanced.maxOutputTokens,
environment.*, router.enabled, diff.autoOpen, ui.fontFamily and
ui.fontSize. Replaced the illustration with the actual key order
_updateSettings receives from the webview, so a rejection on
advanced.maxOutputTokens is shown dropping the real keys that follow
it (environment.variables, router.enabled, diff.autoOpen,
ui.fontFamily, ui.fontSize). Updated the test fixture accordingly.
…setting

settings-batch.test.ts's first suite plumbed 'ui.compactMode' through
applySettingsBatch, but that setting doesn't exist in this branch's
package.json (only wsl.*, thinking.intensity, permissions.yoloMode,
executable.path, advanced.maxOutputTokens, environment.*,
router.enabled, diff.autoOpen, ui.fontFamily and ui.fontSize do).
Swapped it for advanced.maxOutputTokens, already used the same way in
the second suite below.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…s never opened

enableYoloMode() (the button on every permission-error banner) flips
the yolo-mode checkbox and calls updateSettings(), which now also
reads max-output-tokens, chat-font-family and chat-font-size. Those
three fields are only populated by the settingsData roundtrip that
runs when the gear modal opens; clicking Enable Yolo Mode without ever
opening the modal left them at their empty '' default, so
parseInt('') -> NaN got clamped to 0 above and was sent as
advanced.maxOutputTokens/ui.fontSize, silently resetting a real value
back to 0.

updateSettings() now only adds these three keys to the settings batch
it sends when their DOM element actually holds a value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant