Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ Runtime logs are in `~/.config/Claude/logs/`. When 3P mode is active (an `infere
```
patches/ # Nim patch sources (.nim) + Makefile, compiled to native binaries (ls patches/*/*.nim)
patches/linux/ # Linux compatibility - always on, not user-configurable (37)
patches/community/ # Opt-in features, each with a switch in Settings -> Extra -> Community Features (17)
patches/community/ # Opt-in features, each with a switch in Settings -> Extra -> Community Features (16)
patches/core/ # Always-on infrastructure: Extra settings pages, theme engine, GrowthBook overrides, multi-profile (7)
js/ # Shared JS snippets embedded by Nim patches via staticRead ("../../js/..." from a patch)
scripts/ # Build, validation, and launcher scripts
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to the claude-desktop-extra packages will be documented in this file.

## 2026-10-29

### The "Calm the Cowork glow" switch is gone

- **claude.ai no longer has the glow it calmed.** Cowork now lives in the Claude tab, and the pulsing hero glow went with the old Cowork home: `.cowork-hero-glow` and its `cowork-hero-glow-pulse` keyframes are in none of claude.ai's current stylesheets, so the switch matched nothing and changed nothing. `add_feature_cowork_glow` and its Community Features row are removed; a leftover `coworkGlow` / `coworkGlowOpacity` key in `claude-desktop-extra.json(c)` is ignored.

## 2026-10-28

### Upstream v4.29.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ Override Anthropic's server-side feature flags in `~/.config/Claude/claude-deskt

## Patches

We patch the official `app.asar` at repackage time: [`patches/community/`](docs/patches.md#community-features) (17 patches, opt-in features), [`patches/core/`](docs/patches.md#core-infrastructure) (7, infrastructure) and [`patches/linux/`](docs/patches.md#linux-compatibility) (37, Linux fixes). A patch that stops matching fails the build, and a patch is removed once upstream ships the behavior. Catalog: [docs/patches.md](docs/patches.md).
We patch the official `app.asar` at repackage time: [`patches/community/`](docs/patches.md#community-features) (16 patches, opt-in features), [`patches/core/`](docs/patches.md#core-infrastructure) (7, infrastructure) and [`patches/linux/`](docs/patches.md#linux-compatibility) (37, Linux fixes). A patch that stops matching fails the build, and a patch is removed once upstream ships the behavior. Catalog: [docs/patches.md](docs/patches.md).

## Command-line flags

Expand Down
15 changes: 0 additions & 15 deletions docs/claude-desktop-extra.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,6 @@
{
// "activeTheme": "mario",

// coworkGlow: for laptops and machines without much graphics power. The glow
// behind the Cowork hero pulses between 55% and 100% opacity every 3.2s and
// never stops, so the compositor never settles - and where the launcher falls
// back to software rendering (--disable-gpu-compositing / --disable-gpu) those
// frames are redrawn on the CPU. "calm" stops the pulse and holds the glow at a
// fixed opacity; omit it (or "pulse") for claude.ai's own behaviour.
// Settings -> Extra -> Features has a switch for this and applies it live; a
// value set HERE wins the merge, so that switch shows itself as locked while
// this line is active.
// "coworkGlow": "calm",

// coworkGlowOpacity: the fixed opacity "calm" holds, 0..1 (default 0.55, the
// dim end of upstream's own pulse). Hand edits need a restart.
// "coworkGlowOpacity": 0.55,

// transcriptLimits: for long Code sessions, especially ones with many browser
// screenshots. The app only loads the last 50 MiB of a session's transcript, plus
// one shared 32 MiB for all of its subagents' transcripts, and hides the rest behind
Expand Down
5 changes: 2 additions & 3 deletions docs/patches.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Each row below says what a patch does and why you would want it. The mechanism -

## Community features

**17 patches**, each with a switch in Settings → **Extra** → **Community Features**. They reshape first-party surfaces, so they are asked for rather than assumed: turning one off is a full retreat to upstream's own behavior. Off by default, except the theme picker.
**16 patches**, each with a switch in Settings → **Extra** → **Community Features**. They reshape first-party surfaces, so they are asked for rather than assumed: turning one off is a full retreat to upstream's own behavior. Off by default, except the theme picker.

The Community Features panel also holds one **Experimental** switch that has no patch of its own: [Computer Use on a background display](computer-use.md#computer-use-on-a-background-display-experimental), drawn by the Extra settings page and read by the Computer Use executor.

Expand All @@ -27,7 +27,6 @@ The Community Features panel also holds one **Experimental** switch that has no
| [`add_feature_theme_picker.nim`](../patches/community/add_feature_theme_picker.nim) | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd> opens a [searchable gallery](../themes/README.md#theme-picker-ctrlshiftt) of every bundled theme; one click applies it live in every open window and saves your choice |
| [`add_feature_allow_non_anthropic_models.nim`](../patches/community/add_feature_allow_non_anthropic_models.nim) | Opt-in **Allow non-Anthropic models** switch for third-party gateways: relaxes Anthropic's built-in name check that drops model IDs which do not look Anthropic, so a gateway route like `deepseek-v4.1-flash` or `zai-org/glm-5.3` is kept instead of being silently removed with the session falling back to the default model. Covers every gateway check: the Setup model list, the picker filter, the runtime session gate, `/v1/models` discovery and the tier pins. Off by default (behaves exactly as stock); the hand-owned `<userData>/claude-desktop-extra.jsonc` key `allowNonAnthropicModels` locks the switch. The setting is per mode, so set it while in 3P mode (`~/.config/Claude-3p`). The model list is built at startup, so the switch takes effect on the next start. Only the Anthropic-shape check is relaxed - the HIPAA (`model_restricted`), admin allowlist (`not_in_allowed_models`) and app-catalog (`desktop_update_required`) gates are untouched |
| [`add_feature_allow_non_anthropic_models_ion.nim`](../patches/community/add_feature_allow_non_anthropic_models_ion.nim) | The Setup window half of **Allow non-Anthropic models**: Developer → Configure Third-Party Inference checks the Model list in the window itself, so with the switch on it no longer marks a gateway route as "Doesn't look like an Anthropic model" or blocks **Apply Changes** with "Invalid: Model list". Like the rest of the feature, a change to the switch takes effect on the next start |
| [`add_feature_cowork_glow.nim`](../patches/community/add_feature_cowork_glow.nim) | Holds the pulsing Cowork glow still - it otherwise never stops redrawing, which costs real power on laptops and machines without much graphics muscle |
| [`add_feature_dev_tools_toggle.nim`](../patches/community/add_feature_dev_tools_toggle.nim) | Adds a **Developer tools** switch: writes the official build's own opt-in file (`developer_settings.json` in the profile directory, `allowDevTools`), after which Ctrl+Alt+I opens Chromium DevTools for the focused window and "Toggle Dev Tools" appears in the app menu after a restart. The upstream gate itself is untouched: a workspace with developer mode disabled keeps DevTools off |
| [`add_feature_diff_views.nim`](../patches/community/add_feature_diff_views.nim) | Adds a scope dropdown to the Code tab's diff panel (working tree, branch changes, latest turn), an expand/collapse-all button, and picks the base branch your work actually forked from |
| [`add_feature_diff_views_bridge.nim`](../patches/community/add_feature_diff_views_bridge.nim) | The narrow preload bridge the diff dropdown talks through: one fixed channel per call, because the page behind it is remote code |
Expand Down Expand Up @@ -111,7 +110,7 @@ Every feature on this page started as one `.nim` file. If Claude Desktop doesn't
Pick where it goes: `patches/community/` if users should be able to turn it off - it gets a switch in Settings → **Extra** → **Community Features** for the cost of one `renderToggleRow` spec, and the setting is persisted per profile in `<userData>/claude-desktop-extra.jsonc`/`.json`, so people change their mind without rebuilding anything. `patches/core/` if it is always-on infrastructure others build on, and `patches/linux/` if it is a compatibility fix that everyone needs and nobody should have to find. The five steps below cover the community case; drop steps 2 and 3 for the other two. `add_feature_panel_tabs.nim` and its `js/panel_tabs_*.js` modules are the reference implementation to copy from.

1. **Write the patch.** Create `patches/community/<name>.nim` with the usual `# @patch-target:` and `# @patch-type: nim` headers. Match minified code with `[\w$]+` wildcards and capture/replace rather than hardcoded names, and keep the real logic in a `js/` file embedded with `staticRead("../../js/<name>_main.js")` so it stays readable and testable. Every sub-patch must fail loud and the "already patched" branch must assert your own injected result is present - see the patch strictness rules in [AGENTS.md](../AGENTS.md#patch-strictness-rules). If the patch embeds any `js/` file, add a prerequisite line for it in `patches/Makefile`, otherwise editing the JS will not rebuild the binary.
2. **Give it one config key.** Pick a single top-level key in `<userData>/claude-desktop-extra.jsonc` (hand-owned, wins) / `.json` (written by the UI), named after the feature - `panelTabs`, `diffViewModes`, `coworkGlow`, `themePicker`. Read it leniently at runtime: a missing key means the default, a hand-set key in the `.jsonc` locks the switch. `PREF_KEY` in [`js/panel_tabs_main.js`](../js/panel_tabs_main.js) is the reference reader.
2. **Give it one config key.** Pick a single top-level key in `<userData>/claude-desktop-extra.jsonc` (hand-owned, wins) / `.json` (written by the UI), named after the feature - `panelTabs`, `diffViewModes`, `themePicker`. Read it leniently at runtime: a missing key means the default, a hand-set key in the `.jsonc` locks the switch. `PREF_KEY` in [`js/panel_tabs_main.js`](../js/panel_tabs_main.js) is the reference reader.
3. **Add the switch.** Three small pieces: an IPC read/set handler pair in your own patch (`cdb-tabs:pref-read` / `cdb-tabs:pref-set` is the pattern), two wrapper methods in [`js/extra_settings_bridge.js`](../js/extra_settings_bridge.js), and one `renderToggleRow` spec in the Community Features panel of [`js/extra_settings_page.js`](../js/extra_settings_page.js). The row handles the locked-by-`.jsonc` state and the restart notice for you.
4. **Register it.** Bump `EXPECTED_PATCH_COUNT` in `scripts/apply_patches.py` - the orchestrator refuses to run if discovery and that number disagree.
5. **Document it.** Add a row to the Community features table above stating what the patch does and why it exists, and a `CHANGELOG.md` entry.
8 changes: 4 additions & 4 deletions js/diff_views_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,8 @@
var prefEnabled = PREF_DEFAULT;
var prefSource = "default"; // "default" | "json" | "jsonc-locked"

// Same stripper and the same .json-then-.jsonc precedence the cowork-glow
// patch uses, legacy claude-desktop-bin.* fallback included: the one-time
// Same stripper and the same .json-then-.jsonc precedence the theme engine
// uses, legacy claude-desktop-bin.* fallback included: the one-time
// rename migration lives in the custom-themes patch and same-anchor
// injections stack in reverse, so it may not have run yet when we read.
function cfgStrip(s) {
Expand Down Expand Up @@ -2424,8 +2424,8 @@
// as locked instead of silently disagreeing with the file. A separate
// boolean rather than a value-or-null field, because the value here IS a
// boolean and `false` must not read as "not locked". The FIELD NAME
// matches the glow handler's (js/extra_settings_main.js) so the settings
// page speaks of exactly one spelling.
// matches the other rows' handlers so the settings page speaks of
// exactly one spelling.
lockedByJsonc: prefSource === "jsonc-locked",
key: PREF_KEY,
jsonPath: p.json,
Expand Down
10 changes: 0 additions & 10 deletions js/extra_settings_bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,6 @@
return ipcRenderer.invoke("cdb-flags:unset", id);
},

// Cowork glow. set() takes only the fixed strings "pulse"/"calm"; the main
// side re-validates and rejects anything else, so the page cannot smuggle a
// stylesheet or an arbitrary opacity through here.
glowRead: function () {
return ipcRenderer.invoke("cdb-glow:read");
},
glowSet: function (mode) {
return ipcRenderer.invoke("cdb-glow:set", String(mode));
},

// Theme picker (Ctrl+Shift+T). The window and the hotkey are owned by
// patches/community/add_feature_theme_picker.nim; these two channels only
// read and persist the pref, which that patch re-reads on every press.
Expand Down
36 changes: 1 addition & 35 deletions js/extra_settings_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2118,44 +2118,10 @@
return { ok: true, paths: __cdbEx_paths() };
},

// Cowork glow. The live half lives in patches/add_feature_cowork_glow.nim
// (globalThis.__cdbCoworkGlow); persistence is ours because this file is the
// single writer of the .json.
"cdb-glow:read": function () {
var g = globalThis.__cdbCoworkGlow;
if (!g) return { ok: false, error: "the Cowork glow patch is not installed in this build" };
var s = g.read();
var jsonc = __cdbEx_readFileKey(__cdbEx_paths().jsonc, "coworkGlow");
return {
ok: true,
mode: s.mode,
opacity: s.opacity,
defaultOpacity: s.defaultOpacity,
// A hand-edited .jsonc wins the per-key merge at startup, so the switch
// must show itself as locked rather than silently disagree with the file.
lockedByJsonc: jsonc === "pulse" || jsonc === "calm" ? jsonc : null
};
},

"cdb-glow:set": function (mode) {
var g = globalThis.__cdbCoworkGlow;
if (!g) return { ok: false, error: "the Cowork glow patch is not installed in this build" };
if (mode !== "pulse" && mode !== "calm") return { ok: false, error: "mode must be \"pulse\" or \"calm\"" };
var live = g.set(mode);
if (!live || live.ok !== true) return live || { ok: false, error: "could not apply the glow mode" };
var res = __cdbEx_writeCfg(function (cfg) {
if (mode === "calm") cfg.coworkGlow = "calm";
else delete cfg.coworkGlow;
return mode;
});
if (!res.ok) return { ok: false, error: "applied to " + live.windows + " window(s) but could not save: " + res.error };
return { ok: true, mode: mode, windows: live.windows, path: res.path };
},

// Theme picker (Ctrl+Shift+T). The window and the hotkey live in
// patches/community/add_feature_theme_picker.nim, which reads this same key
// itself on every press - persistence is ours because this file is the
// single writer of the .json, the same cross-patch split as the glow above.
// single writer of the .json.
//
// ON IS THE DEFAULT: the shortcut is how a fresh install finds the themes,
// so an absent key means enabled and only an explicit false takes it away.
Expand Down
41 changes: 3 additions & 38 deletions js/extra_settings_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -1179,9 +1179,8 @@

// A hand-edited .jsonc wins the startup merge, so the switch must not
// pretend it can override it.
// Truthy, not === true: the two handlers answer differently in KIND - the
// diff-views one with a boolean, the glow one with the locking value
// itself ("pulse"/"calm") or null. Both mean "the .jsonc decided this".
// Truthy, not === true: some handlers answer null rather than false when
// the .jsonc leaves the key alone.
if (res.lockedByJsonc) {
toggle.title = "Edit " + spec.lockFile + " to change this";
stateLine.textContent = describe() + " - set in " + spec.lockFile;
Expand Down Expand Up @@ -1965,7 +1964,7 @@
// activeSubagentMiB) next to the saved value, so describe() can say "after a
// restart" for exactly as long as that is true, and stop saying it once the
// app has been restarted. The numbers themselves stay hand-edited in the
// .jsonc (like coworkGlowOpacity); this row only shows them.
// .jsonc; this row only shows them.
function renderTranscriptLimitsRow(panel) {
return renderToggleRow(panel, {
section: "Sessions",
Expand Down Expand Up @@ -2436,37 +2435,6 @@
}
}

// --- motion: the pulsing Cowork glow ------------------------------------
// Ours and it applies live, so it is a row in Community Features rather than a
// nav entry of its own.
function renderGlowRow(panel) {
return renderToggleRow(panel, {
section: "Motion",
title: "Calm the Cowork glow",
// Keep this short: the mechanism and the GPU reasoning belong in the README
// and the patch header, not in front of the user.
note: "Stops the Cowork hero glow from pulsing and dims it. Easier on laptops and weak GPUs. " +
"Applies live.",
ariaLabel: "calm the Cowork glow",
read: "glowRead",
write: "glowSet",
lockFile: "claude-desktop-extra.jsonc",
isOn: function (res) { return res.mode === "calm"; },
describe: function (on, res) {
return on
? "calm - static at " + Math.round((res.opacity || 0) * 100) + "% opacity"
: "pulsing (claude.ai default)";
},
writeArg: function (next) { return next ? "calm" : "pulse"; },
toast: function (next, r) {
return next
? "Cowork glow calmed in " + r.windows + " window(s)"
: "Cowork glow restored to pulsing";
},
errorPrefix: "Could not change the glow: "
});
}

// --- shortcuts: the Ctrl+Shift+T theme gallery ---------------------------
// The one row here that is ON unless you say otherwise: the shortcut is how a
// fresh install finds the themes at all, so an absent key means on and only an
Expand Down Expand Up @@ -2770,8 +2738,6 @@
// expand button with it.
// * panel tabs - same shape: the page half polls state() every 5s and
// setEnabled(false) removes the tab bar and untags the columns.
// * cowork glow - the set() handler insertCSS/removeInsertedCSS-es every
// tracked claude.ai view immediately, so it is instant.
// * theme picker - the hotkey re-reads the config file on every press.
// The two polled ones are the reason their notes promise "within a few
// seconds" rather than "instantly".
Expand All @@ -2796,7 +2762,6 @@
renderWindowControlsRow,
renderNativeTitlebarRow,
renderWindowTransparencyRow,
renderGlowRow,
renderThemePickerRow,
renderDevToolsRow,
renderCuBackgroundRow
Expand Down
4 changes: 2 additions & 2 deletions js/files_quick_open_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
try { return _app.getPath("userData"); } catch (e) { return null; }
}
// Same as every other config consumer (js/diff_views_main.js, extra_settings_main.js,
// growthbook_overrides.js, patches/add_feature_cowork_glow.nim): the one-time
// growthbook_overrides.js): the one-time
// claude-desktop-bin.* -> claude-desktop-extra.* rename migration is installed by
// the custom-themes patch and same-anchor prefix injections stack in reverse, so
// it may not have run yet when we get here. Nudging it before every path
Expand Down Expand Up @@ -193,7 +193,7 @@
}
mirrorEnv(readPrefFromDisk().value === true);

// Exact-origin allowlist, same posture as the diff-views and cowork-glow
// Exact-origin allowlist, same posture as the diff-views and Extra settings
// sender checks: comparing parsed origins instead of testing whether the raw
// URL STRING contains "claude.ai"/"claude.com" anywhere. A substring test
// would also pass a lookalike host such as "https://evil.example/?next=
Expand Down
Loading
Loading