@bakapiano/ccsm 0.21.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/codexSeed.js CHANGED
@@ -52,16 +52,23 @@ function execWithTimeout(exe, args, { timeoutMs = 8000 } = {}) {
52
52
  });
53
53
  }
54
54
 
55
- // Parse `CODEX_HOME <path> (dir)` out of `codex doctor` output. Codex
56
- // formats it with variable whitespace; the `(dir)` / `(file)` suffix is
57
- // the easiest anchor to identify the path end. Some wrappers colour
58
- // the label (`\x1b[7mCODEX_HOME\x1b[0m`), so strip ANSI SGR codes first —
59
- // otherwise `CODEX_HOME` is followed by an escape, not whitespace, and the
60
- // match fails.
55
+ // Pull CODEX_HOME out of a wrapper's `doctor` (or `--version`) output. Two
56
+ // shapes appear, and we must handle BOTH:
57
+ // 1. Diagnostic table: `CODEX_HOME <path> (dir)` — only when `doctor`
58
+ // fully succeeds. Variable whitespace; the `(dir)`/`(file)` suffix marks
59
+ // the path end.
60
+ // 2. Wrapper banner: `CODEX_HOME=<path>` — printed on EVERY invocation.
61
+ // Critical because in ccsm's non-interactive spawn `doctor` often exits
62
+ // non-zero (skipping the table) yet still prints this banner line, so it's
63
+ // the reliable source. Without it the probe returns null and the seed
64
+ // lands in ~/.codex instead of the wrapper's relocated home, breaking
65
+ // `resume <id>` ("No saved session found with ID …").
66
+ // Some wrappers colour the label (`\x1b[7mCODEX_HOME\x1b[0m`); strip ANSI first.
61
67
  function parseCodexHomeFromDoctor(text) {
62
68
  if (!text) return null;
63
69
  const clean = String(text).replace(/\x1b\[[0-9;]*m/g, '');
64
- const m = clean.match(/\bCODEX_HOME\s+(.+?)\s*\((?:dir|file)\)/);
70
+ let m = clean.match(/\bCODEX_HOME\s+(.+?)\s*\((?:dir|file)\)/); // table form
71
+ if (!m) m = clean.match(/\bCODEX_HOME=(.+?)\s*$/m); // banner form
65
72
  if (!m) return null;
66
73
  const p = m[1].trim();
67
74
  return p || null;
@@ -54,7 +54,13 @@ function spawn({ command, args = [], cwd, env, cols = 120, rows = 30, meta = {},
54
54
  name: 'xterm-256color',
55
55
  cols, rows,
56
56
  cwd: cwd ? path.resolve(cwd) : process.cwd(),
57
- env: { ...process.env, ...(env || {}) },
57
+ // Advertise 24-bit colour. xterm.js renders truecolor, and CLIs like
58
+ // claude paint their diffs with 24-bit DARK red/green backgrounds +
59
+ // white text when COLORTERM says so. Without it they downgrade to the
60
+ // bright ANSI-16 red/green backgrounds, which wash the white foreground
61
+ // out (the "white text gets buried" report). VSCode sets the same on its
62
+ // terminal PTYs; our ANSI-16 palette already matches VSCode's verbatim.
63
+ env: { ...process.env, ...(env || {}), COLORTERM: 'truecolor' },
58
64
  };
59
65
  if (process.platform === 'win32') {
60
66
  ptyOpts.useConpty = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bakapiano/ccsm",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
4
4
  "description": "Claude Code Session Manager — Windows web UI to manage many concurrent claude sessions: live list, snapshot/restore, focus existing window, new session in an isolated workspace with repo clones",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
@@ -183,6 +183,30 @@ export function TerminalView({ terminalId, cliType }) {
183
183
 
184
184
  const host = hostRef.current;
185
185
  term.open(host);
186
+
187
+ // Answer OSC 10/11 (default foreground / background colour) queries with
188
+ // the LIVE theme colours. CLIs like claude probe the terminal background
189
+ // (`OSC 11 ; ? ST`) to pick a light- or dark-tuned syntax theme. xterm.js
190
+ // doesn't answer these by default, so claude assumes a dark terminal and
191
+ // paints near-white tokens (comments, f-string interpolations, call
192
+ // names) that vanish on our light background — the "字体颜色和背景重复"
193
+ // bug. VSCode answers them; we match. Reply format is the xterm/X11
194
+ // `rgb:RRRR/GGGG/BBBB` (16-bit-per-channel) the query expects.
195
+ const answerColorOsc = (code, getHex) => (data) => {
196
+ if (data !== '?') return false; // only the query form
197
+ const hex = getHex(); // '#rrggbb'
198
+ const ch = (i) => parseInt(hex.slice(i, i + 2), 16);
199
+ const w = (v) => (v * 257).toString(16).padStart(4, '0'); // 8-bit → 16-bit
200
+ const reply = `\x1b]${code};rgb:${w(ch(1))}/${w(ch(3))}/${w(ch(5))}\x07`;
201
+ const ws = wsRef.current;
202
+ if (ws && ws.readyState === 1) ws.send(JSON.stringify({ type: 'input', data: reply }));
203
+ return true;
204
+ };
205
+ try {
206
+ term.parser.registerOscHandler(11, answerColorOsc(11, () => themeRef.current.background));
207
+ term.parser.registerOscHandler(10, answerColorOsc(10, () => themeRef.current.foreground));
208
+ } catch {}
209
+
186
210
  // Robust fit scheduler. A single requestAnimationFrame works most
187
211
  // of the time but races on tab/session switches: the .tab-panel
188
212
  // just flipped from display:none to display:flex and although the
package/server.js CHANGED
@@ -291,15 +291,32 @@ function spawnCliSession({ cli, cwd, sessionId, meta, extraArgs = [] }) {
291
291
  // extraArgs into the single quoted command string — otherwise extraArgs
292
292
  // would become args to the shell itself, not the wrapped command.
293
293
  // Re-resolve here when extraArgs is present so the quoting is correct.
294
+ // Force a session-scoped theme=auto for claude so its syntax/diff colours
295
+ // follow the ccsm terminal background (which we report via OSC 10/11 in
296
+ // TerminalView). claude's DEFAULT theme is *dark*, whose near-white tokens
297
+ // (comments, f-string interpolations, call names) vanish on our light
298
+ // terminal — the "字体颜色和背景重复" bug. --settings is session-scoped, so
299
+ // the user's global ~/.claude/settings.json is left untouched, and ccsm
300
+ // sessions Just Work on a fresh machine without anyone running /theme auto.
301
+ // (Injected here as an integration arg, like --session-id — not via the
302
+ // user-editable cli.args, so it reaches existing configs too.)
303
+ // Skip the injection entirely if the user already put their own --settings
304
+ // in cli.args — claude deep-merges multiple --settings (verified: later ones
305
+ // win per-key), so ours would silently override a theme they set on purpose.
306
+ // Respect the user's explicit choice instead.
307
+ const userHasSettings = (cli.args || []).some(
308
+ (a) => a === '--settings' || String(a).startsWith('--settings='));
309
+ const baseArgs = [...(cli.args || [])];
310
+ if (cli.type === 'claude' && !userHasSettings) baseArgs.push('--settings', '{"theme":"auto"}');
294
311
  const resolved = resolveCommand(
295
312
  cli.command,
296
- [...(cli.args || []), ...extraArgs],
313
+ [...baseArgs, ...extraArgs],
297
314
  cli.shell || 'direct',
298
315
  );
299
316
  const { exe, prefixArgs, fallbackExe, consumesUserArgs } = resolved;
300
317
  const args = consumesUserArgs
301
318
  ? prefixArgs
302
- : [...prefixArgs, ...(cli.args || []), ...extraArgs];
319
+ : [...prefixArgs, ...baseArgs, ...extraArgs];
303
320
  // Merge user-scope PATH from registry into the env we hand the PTY.
304
321
  // spawnEnv() also strips duplicate path-case keys so our override
305
322
  // doesn't get shadowed by the inherited `Path` from process.env.