@sma1lboy/kobe 0.6.10 → 0.7.0

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +1770 -1149
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -70,7 +70,7 @@ var init_package = __esm(() => {
70
70
  package_default = {
71
71
  $schema: "https://json.schemastore.org/package.json",
72
72
  name: "@sma1lboy/kobe",
73
- version: "0.6.10",
73
+ version: "0.7.0",
74
74
  description: "TUI orchestrator for Claude Code (codename)",
75
75
  type: "module",
76
76
  packageManager: "bun@1.3.13",
@@ -214,6 +214,10 @@ function isNewerSemver(latest, current) {
214
214
  return false;
215
215
  }
216
216
  async function checkLatestVersion(opts = {}) {
217
+ const fake = process.env.KOBE_FAKE_UPDATE;
218
+ if (fake) {
219
+ return { current: CURRENT_VERSION, latest: fake, hasUpdate: isNewerSemver(fake, CURRENT_VERSION) };
220
+ }
217
221
  if (isDev() && !opts.force)
218
222
  return null;
219
223
  const latest = await fetchLatestFromRegistry(PACKAGE_NAME);
@@ -3045,6 +3049,21 @@ class Orchestrator {
3045
3049
  if (task.title === trimmed)
3046
3050
  return;
3047
3051
  await this.store.update(task.id, { title: trimmed });
3052
+ await this.followBranchToTitle(task, trimmed);
3053
+ }
3054
+ async followBranchToTitle(taskBefore, newTitle) {
3055
+ if (taskBefore.kind === "main" || !taskBefore.worktreePath)
3056
+ return;
3057
+ if (taskBefore.branch !== autoBranch(PLACEHOLDER_TASK_TITLE, taskBefore.id))
3058
+ return;
3059
+ const nextBranch = autoBranch(newTitle, taskBefore.id);
3060
+ if (nextBranch === taskBefore.branch)
3061
+ return;
3062
+ try {
3063
+ await this.setBranch(taskBefore.id, nextBranch);
3064
+ } catch (err) {
3065
+ console.error(`[kobe] follow-branch-to-title failed for ${taskBefore.id}:`, err);
3066
+ }
3048
3067
  }
3049
3068
  async setBranch(id, branch) {
3050
3069
  const trimmed = branch.trim();
@@ -4217,6 +4236,13 @@ var init_history3 = __esm(() => {
4217
4236
  });
4218
4237
 
4219
4238
  // src/monitor/auto-title.ts
4239
+ function readerFor(vendor) {
4240
+ if (vendor === "codex")
4241
+ return readHistory2;
4242
+ if (vendor === "copilot")
4243
+ return readHistory3;
4244
+ return readHistory;
4245
+ }
4220
4246
  async function originSessions(worktree, vendor) {
4221
4247
  if (vendor === "codex") {
4222
4248
  return { ids: await listSessionIdsForWorktree(worktree), read: readHistory2 };
@@ -4228,22 +4254,33 @@ async function originSessions(worktree, vendor) {
4228
4254
  const ids = [...files].sort((a, b) => a.mtimeMs - b.mtimeMs).map((f) => f.sessionId);
4229
4255
  return { ids, read: readHistory };
4230
4256
  }
4257
+ function titleFromMessages(messages) {
4258
+ const firstUser = messages.find((m) => m.role === "user");
4259
+ if (!firstUser)
4260
+ return "";
4261
+ const text = firstUser.blocks.filter((b) => b.type === "text").map((b) => b.text).join(" ");
4262
+ return deriveTitleFromPrompt(text);
4263
+ }
4231
4264
  async function deriveTitleFromSession(worktree, vendor = DEFAULT_TASK_VENDOR) {
4232
4265
  if (!worktree)
4233
4266
  return "";
4234
4267
  const { ids, read } = await originSessions(worktree, vendor);
4235
4268
  for (const sessionId of ids.slice(0, MAX_SESSIONS_SCANNED)) {
4236
- const messages = await read(sessionId);
4237
- const firstUser = messages.find((m) => m.role === "user");
4238
- if (!firstUser)
4239
- continue;
4240
- const text = firstUser.blocks.filter((b) => b.type === "text").map((b) => b.text).join(" ");
4241
- const title = deriveTitleFromPrompt(text);
4269
+ const title = titleFromMessages(await read(sessionId));
4242
4270
  if (title)
4243
4271
  return title;
4244
4272
  }
4245
4273
  return "";
4246
4274
  }
4275
+ async function deriveTitleFromSessionId(vendor, sessionId) {
4276
+ if (!sessionId)
4277
+ return "";
4278
+ try {
4279
+ return titleFromMessages(await readerFor(vendor)(sessionId));
4280
+ } catch {
4281
+ return "";
4282
+ }
4283
+ }
4247
4284
  var MAX_SESSIONS_SCANNED = 8;
4248
4285
  var init_auto_title = __esm(() => {
4249
4286
  init_history();
@@ -4251,168 +4288,483 @@ var init_auto_title = __esm(() => {
4251
4288
  init_history3();
4252
4289
  });
4253
4290
 
4254
- // src/daemon/crash-log.ts
4255
- function toError(err) {
4256
- if (err instanceof Error)
4257
- return err;
4258
- let text;
4291
+ // src/tmux/client.ts
4292
+ var exports_client = {};
4293
+ __export(exports_client, {
4294
+ windowCount: () => windowCount,
4295
+ tmuxSessionName: () => tmuxSessionName,
4296
+ tmuxCommandSequence: () => tmuxCommandSequence,
4297
+ tmuxAvailable: () => tmuxAvailable,
4298
+ tmuxArgs: () => tmuxArgs,
4299
+ tagPaneRole: () => tagPaneRole,
4300
+ tagClaudePane: () => tagClaudePane,
4301
+ switchClientBeforeKill: () => switchClientBeforeKill,
4302
+ setWindowOption: () => setWindowOption,
4303
+ setSessionOption: () => setSessionOption,
4304
+ sessionExists: () => sessionExists,
4305
+ sendKeys: () => sendKeys,
4306
+ sendKeyName: () => sendKeyName,
4307
+ runTmuxSequenceCapturing: () => runTmuxSequenceCapturing,
4308
+ runTmuxSequence: () => runTmuxSequence,
4309
+ runTmuxCapturing: () => runTmuxCapturing,
4310
+ runTmux: () => runTmux,
4311
+ paneIdByRole: () => paneIdByRole,
4312
+ newWindow: () => newWindow,
4313
+ killSession: () => killSession,
4314
+ getSessionOptions: () => getSessionOptions,
4315
+ getSessionOption: () => getSessionOption,
4316
+ ensureFallbackSession: () => ensureFallbackSession,
4317
+ currentSessionName: () => currentSessionName,
4318
+ claudePaneIdStrict: () => claudePaneIdStrict,
4319
+ claudePaneId: () => claudePaneId,
4320
+ capturePaneById: () => capturePaneById,
4321
+ attachArgv: () => attachArgv,
4322
+ PANE_ROLE_OPTION: () => PANE_ROLE_OPTION,
4323
+ KOBE_TMUX_SOCKET: () => KOBE_TMUX_SOCKET,
4324
+ KOBE_HOME_SESSION: () => KOBE_HOME_SESSION,
4325
+ CLAUDE_ROLE_OPTION: () => CLAUDE_ROLE_OPTION,
4326
+ CHAT_TAB_SESSION_ID_OPTION: () => CHAT_TAB_SESSION_ID_OPTION
4327
+ });
4328
+ function tmuxArgs(...args) {
4329
+ return ["tmux", "-L", KOBE_TMUX_SOCKET, ...args];
4330
+ }
4331
+ function tmuxSessionName(taskId) {
4332
+ return `kobe-${taskId.replace(/[^A-Za-z0-9_-]/g, "")}`;
4333
+ }
4334
+ function attachArgv(name) {
4335
+ return tmuxArgs("attach-session", "-t", `=${name}`);
4336
+ }
4337
+ async function drainText(stream) {
4338
+ if (!stream)
4339
+ return "";
4259
4340
  try {
4260
- text = typeof err === "string" ? err : JSON.stringify(err);
4341
+ return await new Response(stream).text();
4261
4342
  } catch {
4262
- text = String(err);
4343
+ return "";
4263
4344
  }
4264
- return new Error(text);
4265
- }
4266
- function formatCrashEntry(kind, err, now = new Date) {
4267
- const e = toError(err);
4268
- return `[${now.toISOString()}] daemon ${kind}: ${e.stack ?? `${e.name}: ${e.message}`}
4269
- `;
4270
- }
4271
- function formatDaemonError(subsystem, err, now = new Date) {
4272
- const e = toError(err);
4273
- return `[${now.toISOString()}] daemon error [${subsystem}]: ${e.stack ?? `${e.name}: ${e.message}`}
4274
- `;
4275
4345
  }
4276
- function logDaemonError(subsystem, err) {
4277
- process.stderr.write(formatDaemonError(subsystem, err));
4346
+ async function runTmux(args) {
4347
+ const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "pipe" });
4348
+ const [errText, code] = await Promise.all([drainText(proc.stderr), proc.exited]);
4349
+ if (code !== 0 && errText.trim().length > 0) {
4350
+ console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
4351
+ }
4352
+ return code;
4278
4353
  }
4279
- function installDaemonCrashHandlers(log = (l) => process.stderr.write(l)) {
4280
- if (onRejection || onException)
4281
- return;
4282
- onRejection = (reason) => log(formatCrashEntry("unhandledRejection", reason));
4283
- onException = (err) => log(formatCrashEntry("uncaughtException", err));
4284
- process.on("unhandledRejection", onRejection);
4285
- process.on("uncaughtException", onException);
4354
+ async function runTmuxQuiet(args) {
4355
+ const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
4356
+ return proc.exited;
4286
4357
  }
4287
- var onRejection, onException;
4288
-
4289
- // src/daemon/auto-title-poller.ts
4290
- async function runAutoTitlePass(orch, derive = deriveTitleFromSession) {
4291
- let renamed = 0;
4292
- for (const task of orch.listTasks()) {
4293
- if (task.title !== PLACEHOLDER_TASK_TITLE || !task.worktreePath)
4358
+ function tmuxCommandSequence(commands) {
4359
+ const out = [];
4360
+ for (const cmd of commands) {
4361
+ if (cmd.length === 0)
4294
4362
  continue;
4295
- try {
4296
- const title = await derive(task.worktreePath, task.vendor ?? DEFAULT_TASK_VENDOR);
4297
- if (!title)
4298
- continue;
4299
- const current = orch.getTask(task.id);
4300
- if (!current || current.title !== PLACEHOLDER_TASK_TITLE)
4301
- continue;
4302
- await orch.setTitle(task.id, title);
4303
- renamed++;
4304
- } catch (err) {
4305
- logDaemonError("auto-title-poller", err);
4306
- }
4363
+ if (out.length > 0)
4364
+ out.push(";");
4365
+ out.push(...cmd);
4307
4366
  }
4308
- return renamed;
4367
+ return out;
4309
4368
  }
4310
- function startAutoTitlePoller(orch, intervalMs = DEFAULT_AUTO_TITLE_POLL_MS) {
4311
- if (intervalMs <= 0)
4312
- return () => {};
4313
- let running = false;
4314
- const tick = () => {
4315
- if (running)
4316
- return;
4317
- running = true;
4318
- runAutoTitlePass(orch).catch((err) => logDaemonError("auto-title-poller", err)).finally(() => {
4319
- running = false;
4320
- });
4321
- };
4322
- const timer = setInterval(tick, intervalMs);
4323
- timer.unref?.();
4324
- return () => clearInterval(timer);
4369
+ async function runTmuxSequence(commands) {
4370
+ const args = tmuxCommandSequence(commands);
4371
+ return args.length === 0 ? 0 : runTmux(args);
4325
4372
  }
4326
- var DEFAULT_AUTO_TITLE_POLL_MS = 4000;
4327
- var init_auto_title_poller = __esm(() => {
4328
- init_auto_title();
4329
- init_core();
4330
- });
4331
-
4332
- // src/daemon/event-bus.ts
4333
- class DaemonEventBus {
4334
- last = new Map;
4335
- sinks = new Set;
4336
- publish(channel, payload) {
4337
- this.last.set(channel, payload);
4338
- const event = { channel, payload };
4339
- for (const sink of this.sinks)
4340
- sink(event);
4341
- }
4342
- snapshot() {
4343
- return [...this.last].map(([channel, payload]) => ({ channel, payload }));
4344
- }
4345
- onPublish(sink) {
4346
- this.sinks.add(sink);
4347
- return () => {
4348
- this.sinks.delete(sink);
4349
- };
4373
+ async function runTmuxCapturing(args) {
4374
+ const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "pipe", stderr: "pipe" });
4375
+ const [stdout, errText, code] = await Promise.all([drainText(proc.stdout), drainText(proc.stderr), proc.exited]);
4376
+ if (code !== 0 && errText.trim().length > 0) {
4377
+ console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
4350
4378
  }
4379
+ return { code, stdout };
4351
4380
  }
4352
-
4353
- // src/daemon/paths.ts
4354
- import { createHash as createHash2 } from "crypto";
4355
- import { homedir as homedir6, tmpdir } from "os";
4356
- import { join as join3 } from "path";
4357
- function shortHomeTag(homeDir2) {
4358
- return createHash2("sha1").update(homeDir2).digest("hex").slice(0, 8);
4359
- }
4360
- function fitSocketPath(naturalPath, homeDir2, role, pidTag) {
4361
- if (Buffer.byteLength(naturalPath, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
4362
- return naturalPath;
4363
- const tag = shortHomeTag(homeDir2);
4364
- const suffix = pidTag === undefined ? "" : `-${pidTag}`;
4365
- const fallback = join3(tmpdir(), `kobe-${tag}-${role}${suffix}.sock`);
4366
- if (Buffer.byteLength(fallback, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
4367
- return fallback;
4368
- throw new Error(`kobe socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
4381
+ async function runTmuxSequenceCapturing(commands) {
4382
+ const args = tmuxCommandSequence(commands);
4383
+ return args.length === 0 ? { code: 0, stdout: "" } : runTmuxCapturing(args);
4369
4384
  }
4370
- function defaultDaemonSocketPath(homeDir2) {
4371
- const override = process.env.KOBE_DAEMON_SOCKET_PATH;
4372
- if (override && override.length > 0)
4373
- return override;
4374
- const explicit = homeDir2 ?? process.env.KOBE_HOME_DIR;
4375
- if (explicit && explicit.length > 0) {
4376
- return fitSocketPath(join3(explicit, ".kobe", "daemon.sock"), explicit, "daemon");
4377
- }
4378
- const runtimeDir = process.env.XDG_RUNTIME_DIR;
4379
- if (runtimeDir && runtimeDir.length > 0) {
4380
- return fitSocketPath(join3(runtimeDir, "kobe.sock"), runtimeDir, "daemon");
4385
+ async function tmuxAvailable() {
4386
+ try {
4387
+ const proc = Bun.spawn(["tmux", "-V"], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
4388
+ return await proc.exited === 0;
4389
+ } catch {
4390
+ return false;
4381
4391
  }
4382
- const home = homedir6();
4383
- return fitSocketPath(join3(home, ".kobe", "daemon.sock"), home, "daemon");
4384
4392
  }
4385
- function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4386
- const override = process.env.KOBE_DAEMON_PID_PATH;
4387
- if (override && override.length > 0)
4388
- return override;
4389
- return join3(homeDir2, ".kobe", "daemon.pid");
4393
+ async function sessionExists(name) {
4394
+ return await runTmuxQuiet(["has-session", "-t", `=${name}`]) === 0;
4390
4395
  }
4391
- function defaultDaemonLogPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4392
- return join3(homeDir2, ".kobe", "daemon.log");
4396
+ async function windowCount(sessionName) {
4397
+ const { code, stdout } = await runTmuxCapturing(["list-windows", "-t", `=${sessionName}`, "-F", "#{window_id}"]);
4398
+ if (code !== 0)
4399
+ return 0;
4400
+ return stdout.split(`
4401
+ `).filter((l) => l.trim().length > 0).length;
4393
4402
  }
4394
- var SOCKET_PATH_SAFETY_LIMIT = 100;
4395
- var init_paths2 = () => {};
4396
-
4397
- // src/daemon/server.ts
4398
- import { mkdir as mkdir3, readFile as readFile5, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
4399
- import { createServer } from "net";
4400
- import { dirname as dirname3 } from "path";
4401
- function resolveIdleGraceMs() {
4402
- const raw = process.env.KOBE_DAEMON_IDLE_GRACE_MS;
4403
- if (raw === undefined)
4404
- return DEFAULT_IDLE_GRACE_MS;
4405
- const n = Number(raw);
4406
- return Number.isFinite(n) && n >= 0 ? n : DEFAULT_IDLE_GRACE_MS;
4403
+ async function setSessionOption(session, option, value) {
4404
+ await runTmux(["set-option", "-t", session, option, value]);
4407
4405
  }
4408
- async function startDaemonServer(orch, options = {}) {
4409
- const socketPath = options.socketPath ?? defaultDaemonSocketPath(options.homeDir);
4410
- const pidPath = options.pidPath ?? defaultDaemonPidPath(options.homeDir);
4411
- const startedAt = options.startedAt ?? new Date;
4412
- const clients = new Set;
4413
- let nextClientId = 1;
4414
- const idleGraceMs = resolveIdleGraceMs();
4415
- let idleTimer = null;
4406
+ async function getSessionOption(session, option) {
4407
+ const { code, stdout } = await runTmuxCapturing(["show-options", "-v", "-t", session, option]);
4408
+ return code === 0 ? stdout.trim() : "";
4409
+ }
4410
+ async function getSessionOptions(session, options) {
4411
+ const values = Object.fromEntries(options.map((option) => [option, undefined]));
4412
+ const { code, stdout } = await runTmuxSequenceCapturing(options.map((option) => ["show-options", "-q", "-t", session, option]));
4413
+ if (code !== 0)
4414
+ return values;
4415
+ for (const line of stdout.split(`
4416
+ `)) {
4417
+ const idx = line.indexOf(" ");
4418
+ if (idx <= 0)
4419
+ continue;
4420
+ const option = line.slice(0, idx);
4421
+ if (option in values)
4422
+ values[option] = line.slice(idx + 1).trim();
4423
+ }
4424
+ return values;
4425
+ }
4426
+ async function tagPaneRole(paneId, role) {
4427
+ await runTmux(["set-option", "-p", "-t", paneId, PANE_ROLE_OPTION, role]);
4428
+ }
4429
+ async function setWindowOption(target, option, value) {
4430
+ await runTmux(["set-window-option", "-t", target, option, value]);
4431
+ }
4432
+ async function tagClaudePane(paneId) {
4433
+ await tagPaneRole(paneId, CLAUDE_ROLE_VALUE);
4434
+ }
4435
+ async function paneIdByRole(sessionName, role, fallbackFirst = false) {
4436
+ const { code, stdout } = await runTmuxCapturing([
4437
+ "list-panes",
4438
+ "-t",
4439
+ `=${sessionName}`,
4440
+ "-F",
4441
+ `#{pane_id} #{${PANE_ROLE_OPTION}}`
4442
+ ]);
4443
+ if (code !== 0)
4444
+ return "";
4445
+ let firstId = "";
4446
+ for (const line of stdout.split(`
4447
+ `)) {
4448
+ const [id, paneRole] = line.split("\t");
4449
+ if (!id)
4450
+ continue;
4451
+ if (!firstId)
4452
+ firstId = id.trim();
4453
+ if (paneRole?.trim() === role)
4454
+ return id.trim();
4455
+ }
4456
+ return fallbackFirst ? firstId : "";
4457
+ }
4458
+ async function claudePaneId(sessionName) {
4459
+ return paneIdByRole(sessionName, CLAUDE_ROLE_VALUE, true);
4460
+ }
4461
+ async function claudePaneIdStrict(sessionName) {
4462
+ return paneIdByRole(sessionName, CLAUDE_ROLE_VALUE, false);
4463
+ }
4464
+ async function capturePaneById(paneId, lines) {
4465
+ if (!paneId)
4466
+ return "";
4467
+ const args = ["capture-pane", "-t", paneId, "-p"];
4468
+ if (typeof lines === "number" && lines > 0)
4469
+ args.push("-S", String(-lines));
4470
+ const { code, stdout } = await runTmuxCapturing(args);
4471
+ return code === 0 ? stdout : "";
4472
+ }
4473
+ async function sendKeys(target, text) {
4474
+ await runTmux(["send-keys", "-t", target, "-l", "--", text]);
4475
+ }
4476
+ async function sendKeyName(target, key) {
4477
+ await runTmux(["send-keys", "-t", target, key]);
4478
+ }
4479
+ async function newWindow(session, opts) {
4480
+ const args = ["new-window", "-t", `=${session}`, "-c", opts.cwd];
4481
+ if (opts.name)
4482
+ args.push("-n", opts.name);
4483
+ args.push(opts.command);
4484
+ await runTmux(args);
4485
+ }
4486
+ async function currentSessionName() {
4487
+ const args = ["display-message", "-p"];
4488
+ const target = process.env.TMUX_PANE;
4489
+ if (target && target.length > 0)
4490
+ args.push("-t", target);
4491
+ args.push("#{session_name}");
4492
+ const { code, stdout } = await runTmuxCapturing(args);
4493
+ const name = stdout.trim();
4494
+ return code === 0 && name.length > 0 ? name : null;
4495
+ }
4496
+ async function killSession(name) {
4497
+ if (await sessionExists(name))
4498
+ await runTmux(["kill-session", "-t", `=${name}`]);
4499
+ }
4500
+ async function ensureFallbackSession() {
4501
+ const name = KOBE_HOME_SESSION;
4502
+ if (!await sessionExists(name)) {
4503
+ await runTmux([
4504
+ "new-session",
4505
+ "-d",
4506
+ "-s",
4507
+ name,
4508
+ "-x",
4509
+ "220",
4510
+ "-y",
4511
+ "50",
4512
+ "sh",
4513
+ "-c",
4514
+ 'clear; printf "\\n No active task\\n\\n Select a task from the sidebar to continue.\\n\\n"; exec ${SHELL:-sh}'
4515
+ ]);
4516
+ }
4517
+ return name;
4518
+ }
4519
+ async function switchClientBeforeKill(killedName, nextSessionName) {
4520
+ const current = await currentSessionName();
4521
+ if (current !== killedName)
4522
+ return;
4523
+ if (nextSessionName && nextSessionName !== killedName && await sessionExists(nextSessionName)) {
4524
+ await runTmux(["switch-client", "-t", `=${nextSessionName}`]);
4525
+ return;
4526
+ }
4527
+ const fallback = await ensureFallbackSession();
4528
+ await runTmux(["switch-client", "-t", `=${fallback}`]);
4529
+ }
4530
+ var KOBE_TMUX_SOCKET, PANE_ROLE_OPTION = "@kobe_role", CLAUDE_ROLE_OPTION, CLAUDE_ROLE_VALUE = "claude", CHAT_TAB_SESSION_ID_OPTION = "@kobe_session_id", KOBE_HOME_SESSION = "kobe-home";
4531
+ var init_client2 = __esm(() => {
4532
+ KOBE_TMUX_SOCKET = process.env.KOBE_TMUX_SOCKET?.trim() || "kobe";
4533
+ CLAUDE_ROLE_OPTION = PANE_ROLE_OPTION;
4534
+ });
4535
+
4536
+ // src/tmux/chat-tab-naming.ts
4537
+ async function listChatTabWindows(session, runner = realRunner) {
4538
+ const { code, stdout } = await runner.capture([
4539
+ "list-windows",
4540
+ "-t",
4541
+ `=${session}`,
4542
+ "-F",
4543
+ `#{window_index} #{${CHAT_TAB_SESSION_ID_OPTION}}`
4544
+ ]);
4545
+ if (code !== 0)
4546
+ return [];
4547
+ const out = [];
4548
+ for (const line of stdout.split(`
4549
+ `)) {
4550
+ const tab = line.indexOf("\t");
4551
+ const index = Number.parseInt((tab >= 0 ? line.slice(0, tab) : line).trim(), 10);
4552
+ if (!Number.isInteger(index))
4553
+ continue;
4554
+ out.push({ index, sessionId: tab >= 0 ? line.slice(tab + 1).trim() : "" });
4555
+ }
4556
+ return out;
4557
+ }
4558
+ async function windowNamedManually(session, index, runner) {
4559
+ const { code, stdout } = await runner.capture([
4560
+ "show-window-options",
4561
+ "-t",
4562
+ `=${session}:${index}`,
4563
+ "automatic-rename"
4564
+ ]);
4565
+ return code === 0 && /\boff\b/.test(stdout);
4566
+ }
4567
+ async function renameWindow(session, index, title, runner) {
4568
+ return await runner.run(["rename-window", "-t", `=${session}:${index}`, "--", title]) === 0;
4569
+ }
4570
+ async function runChatTabNamingPass(orch, deps = realDeps) {
4571
+ let renamed = 0;
4572
+ for (const task of orch.listTasks()) {
4573
+ if (task.kind === "main" || !task.worktreePath)
4574
+ continue;
4575
+ const session = tmuxSessionName(task.id);
4576
+ const windows = await listChatTabWindows(session, deps.runner);
4577
+ if (windows.length === 0)
4578
+ continue;
4579
+ const originIndex = windows.reduce((min, w) => Math.min(min, w.index), Number.POSITIVE_INFINITY);
4580
+ const vendor = task.vendor ?? DEFAULT_TASK_VENDOR;
4581
+ for (const w of windows) {
4582
+ try {
4583
+ if (await windowNamedManually(session, w.index, deps.runner))
4584
+ continue;
4585
+ const title = w.sessionId ? await deps.titleFromSessionId(vendor, w.sessionId) : w.index === originIndex ? await deps.titleFromWorktree(task.worktreePath, vendor) : "";
4586
+ if (title && await renameWindow(session, w.index, title, deps.runner))
4587
+ renamed++;
4588
+ } catch {}
4589
+ }
4590
+ }
4591
+ return renamed;
4592
+ }
4593
+ var realRunner, realDeps;
4594
+ var init_chat_tab_naming = __esm(() => {
4595
+ init_auto_title();
4596
+ init_client2();
4597
+ realRunner = { capture: runTmuxCapturing, run: runTmux };
4598
+ realDeps = {
4599
+ runner: realRunner,
4600
+ titleFromSessionId: deriveTitleFromSessionId,
4601
+ titleFromWorktree: deriveTitleFromSession
4602
+ };
4603
+ });
4604
+
4605
+ // src/daemon/crash-log.ts
4606
+ function toError(err) {
4607
+ if (err instanceof Error)
4608
+ return err;
4609
+ let text;
4610
+ try {
4611
+ text = typeof err === "string" ? err : JSON.stringify(err);
4612
+ } catch {
4613
+ text = String(err);
4614
+ }
4615
+ return new Error(text);
4616
+ }
4617
+ function formatCrashEntry(kind, err, now = new Date) {
4618
+ const e = toError(err);
4619
+ return `[${now.toISOString()}] daemon ${kind}: ${e.stack ?? `${e.name}: ${e.message}`}
4620
+ `;
4621
+ }
4622
+ function formatDaemonError(subsystem, err, now = new Date) {
4623
+ const e = toError(err);
4624
+ return `[${now.toISOString()}] daemon error [${subsystem}]: ${e.stack ?? `${e.name}: ${e.message}`}
4625
+ `;
4626
+ }
4627
+ function logDaemonError(subsystem, err) {
4628
+ process.stderr.write(formatDaemonError(subsystem, err));
4629
+ }
4630
+ function installDaemonCrashHandlers(log = (l) => process.stderr.write(l)) {
4631
+ if (onRejection || onException)
4632
+ return;
4633
+ onRejection = (reason) => log(formatCrashEntry("unhandledRejection", reason));
4634
+ onException = (err) => log(formatCrashEntry("uncaughtException", err));
4635
+ process.on("unhandledRejection", onRejection);
4636
+ process.on("uncaughtException", onException);
4637
+ }
4638
+ var onRejection, onException;
4639
+
4640
+ // src/daemon/auto-title-poller.ts
4641
+ async function runAutoTitlePass(orch, derive = deriveTitleFromSession) {
4642
+ const renamed = [];
4643
+ for (const task of orch.listTasks()) {
4644
+ if (task.title !== PLACEHOLDER_TASK_TITLE || !task.worktreePath)
4645
+ continue;
4646
+ try {
4647
+ const title = await derive(task.worktreePath, task.vendor ?? DEFAULT_TASK_VENDOR);
4648
+ if (!title)
4649
+ continue;
4650
+ const current = orch.getTask(task.id);
4651
+ if (!current || current.title !== PLACEHOLDER_TASK_TITLE)
4652
+ continue;
4653
+ await orch.setTitle(task.id, title);
4654
+ renamed.push({ id: task.id, title });
4655
+ } catch (err) {
4656
+ logDaemonError("auto-title-poller", err);
4657
+ }
4658
+ }
4659
+ return renamed;
4660
+ }
4661
+ function startAutoTitlePoller(orch, intervalMs = DEFAULT_AUTO_TITLE_POLL_MS) {
4662
+ if (intervalMs <= 0)
4663
+ return () => {};
4664
+ let running = false;
4665
+ const tick = () => {
4666
+ if (running)
4667
+ return;
4668
+ running = true;
4669
+ runAutoTitlePass(orch).then(() => runChatTabNamingPass(orch)).catch((err) => logDaemonError("auto-title-poller", err)).finally(() => {
4670
+ running = false;
4671
+ });
4672
+ };
4673
+ const timer = setInterval(tick, intervalMs);
4674
+ timer.unref?.();
4675
+ return () => clearInterval(timer);
4676
+ }
4677
+ var DEFAULT_AUTO_TITLE_POLL_MS = 4000;
4678
+ var init_auto_title_poller = __esm(() => {
4679
+ init_auto_title();
4680
+ init_core();
4681
+ init_chat_tab_naming();
4682
+ });
4683
+
4684
+ // src/daemon/event-bus.ts
4685
+ class DaemonEventBus {
4686
+ last = new Map;
4687
+ sinks = new Set;
4688
+ publish(channel, payload) {
4689
+ this.last.set(channel, payload);
4690
+ const event = { channel, payload };
4691
+ for (const sink of this.sinks)
4692
+ sink(event);
4693
+ }
4694
+ snapshot() {
4695
+ return [...this.last].map(([channel, payload]) => ({ channel, payload }));
4696
+ }
4697
+ onPublish(sink) {
4698
+ this.sinks.add(sink);
4699
+ return () => {
4700
+ this.sinks.delete(sink);
4701
+ };
4702
+ }
4703
+ }
4704
+
4705
+ // src/daemon/paths.ts
4706
+ import { createHash as createHash2 } from "crypto";
4707
+ import { homedir as homedir6, tmpdir } from "os";
4708
+ import { join as join3 } from "path";
4709
+ function shortHomeTag(homeDir2) {
4710
+ return createHash2("sha1").update(homeDir2).digest("hex").slice(0, 8);
4711
+ }
4712
+ function fitSocketPath(naturalPath, homeDir2, role, pidTag) {
4713
+ if (Buffer.byteLength(naturalPath, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
4714
+ return naturalPath;
4715
+ const tag = shortHomeTag(homeDir2);
4716
+ const suffix = pidTag === undefined ? "" : `-${pidTag}`;
4717
+ const fallback = join3(tmpdir(), `kobe-${tag}-${role}${suffix}.sock`);
4718
+ if (Buffer.byteLength(fallback, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
4719
+ return fallback;
4720
+ throw new Error(`kobe socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
4721
+ }
4722
+ function defaultDaemonSocketPath(homeDir2) {
4723
+ const override = process.env.KOBE_DAEMON_SOCKET_PATH;
4724
+ if (override && override.length > 0)
4725
+ return override;
4726
+ const explicit = homeDir2 ?? process.env.KOBE_HOME_DIR;
4727
+ if (explicit && explicit.length > 0) {
4728
+ return fitSocketPath(join3(explicit, ".kobe", "daemon.sock"), explicit, "daemon");
4729
+ }
4730
+ const runtimeDir = process.env.XDG_RUNTIME_DIR;
4731
+ if (runtimeDir && runtimeDir.length > 0) {
4732
+ return fitSocketPath(join3(runtimeDir, "kobe.sock"), runtimeDir, "daemon");
4733
+ }
4734
+ const home = homedir6();
4735
+ return fitSocketPath(join3(home, ".kobe", "daemon.sock"), home, "daemon");
4736
+ }
4737
+ function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4738
+ const override = process.env.KOBE_DAEMON_PID_PATH;
4739
+ if (override && override.length > 0)
4740
+ return override;
4741
+ return join3(homeDir2, ".kobe", "daemon.pid");
4742
+ }
4743
+ function defaultDaemonLogPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4744
+ return join3(homeDir2, ".kobe", "daemon.log");
4745
+ }
4746
+ var SOCKET_PATH_SAFETY_LIMIT = 100;
4747
+ var init_paths2 = () => {};
4748
+
4749
+ // src/daemon/server.ts
4750
+ import { mkdir as mkdir3, readFile as readFile5, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
4751
+ import { createServer } from "net";
4752
+ import { dirname as dirname3 } from "path";
4753
+ function resolveIdleGraceMs() {
4754
+ const raw = process.env.KOBE_DAEMON_IDLE_GRACE_MS;
4755
+ if (raw === undefined)
4756
+ return DEFAULT_IDLE_GRACE_MS;
4757
+ const n = Number(raw);
4758
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_IDLE_GRACE_MS;
4759
+ }
4760
+ async function startDaemonServer(orch, options = {}) {
4761
+ const socketPath = options.socketPath ?? defaultDaemonSocketPath(options.homeDir);
4762
+ const pidPath = options.pidPath ?? defaultDaemonPidPath(options.homeDir);
4763
+ const startedAt = options.startedAt ?? new Date;
4764
+ const clients = new Set;
4765
+ let nextClientId = 1;
4766
+ const idleGraceMs = resolveIdleGraceMs();
4767
+ let idleTimer = null;
4416
4768
  let stopping = false;
4417
4769
  function guiCount() {
4418
4770
  let n = 0;
@@ -5075,289 +5427,55 @@ var init_repo_cmd = __esm(() => {
5075
5427
  ].join(`
5076
5428
  `);
5077
5429
  });
5078
-
5079
- // src/engine/interactive-command.ts
5080
- function engineCommandKey(vendor) {
5081
- return `engineCommand.${vendor}`;
5082
- }
5083
- function defaultEngineCommand(vendor) {
5084
- return DEFAULT_COMMANDS[vendor ?? "claude"] ?? DEFAULT_COMMANDS.claude;
5085
- }
5086
- function parseEngineCommand(command) {
5087
- const out = [];
5088
- const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
5089
- for (let m = re.exec(command);m !== null; m = re.exec(command)) {
5090
- out.push(m[1] ?? m[2] ?? m[3] ?? "");
5091
- }
5092
- return out;
5093
- }
5094
- function interactiveEngineCommand(vendor) {
5095
- const v = vendor ?? "claude";
5096
- const override = getPersistedString(engineCommandKey(v))?.trim();
5097
- if (override) {
5098
- const argv = parseEngineCommand(override);
5099
- if (argv.length > 0)
5100
- return argv;
5101
- }
5102
- return defaultEngineCommand(v);
5103
- }
5104
- var DEFAULT_COMMANDS, VENDOR_LABEL;
5105
- var init_interactive_command = __esm(() => {
5106
- init_repos();
5107
- DEFAULT_COMMANDS = {
5108
- claude: ["claude"],
5109
- codex: ["codex"],
5110
- copilot: ["copilot"]
5111
- };
5112
- VENDOR_LABEL = {
5113
- claude: "Claude",
5114
- codex: "Codex",
5115
- copilot: "Copilot"
5116
- };
5117
- });
5118
-
5119
- // src/tmux/client.ts
5120
- var exports_client = {};
5121
- __export(exports_client, {
5122
- windowCount: () => windowCount,
5123
- tmuxSessionName: () => tmuxSessionName,
5124
- tmuxCommandSequence: () => tmuxCommandSequence,
5125
- tmuxAvailable: () => tmuxAvailable,
5126
- tmuxArgs: () => tmuxArgs,
5127
- tagPaneRole: () => tagPaneRole,
5128
- tagClaudePane: () => tagClaudePane,
5129
- switchClientBeforeKill: () => switchClientBeforeKill,
5130
- setWindowOption: () => setWindowOption,
5131
- setSessionOption: () => setSessionOption,
5132
- sessionExists: () => sessionExists,
5133
- sendKeys: () => sendKeys,
5134
- sendKeyName: () => sendKeyName,
5135
- runTmuxSequenceCapturing: () => runTmuxSequenceCapturing,
5136
- runTmuxSequence: () => runTmuxSequence,
5137
- runTmuxCapturing: () => runTmuxCapturing,
5138
- runTmux: () => runTmux,
5139
- paneIdByRole: () => paneIdByRole,
5140
- newWindow: () => newWindow,
5141
- killSession: () => killSession,
5142
- getSessionOptions: () => getSessionOptions,
5143
- getSessionOption: () => getSessionOption,
5144
- ensureFallbackSession: () => ensureFallbackSession,
5145
- currentSessionName: () => currentSessionName,
5146
- claudePaneIdStrict: () => claudePaneIdStrict,
5147
- claudePaneId: () => claudePaneId,
5148
- capturePaneById: () => capturePaneById,
5149
- attachArgv: () => attachArgv,
5150
- PANE_ROLE_OPTION: () => PANE_ROLE_OPTION,
5151
- KOBE_TMUX_SOCKET: () => KOBE_TMUX_SOCKET,
5152
- KOBE_HOME_SESSION: () => KOBE_HOME_SESSION,
5153
- CLAUDE_ROLE_OPTION: () => CLAUDE_ROLE_OPTION
5154
- });
5155
- function tmuxArgs(...args) {
5156
- return ["tmux", "-L", KOBE_TMUX_SOCKET, ...args];
5157
- }
5158
- function tmuxSessionName(taskId) {
5159
- return `kobe-${taskId.replace(/[^A-Za-z0-9_-]/g, "")}`;
5160
- }
5161
- function attachArgv(name) {
5162
- return tmuxArgs("attach-session", "-t", `=${name}`);
5163
- }
5164
- async function drainText(stream) {
5165
- if (!stream)
5166
- return "";
5167
- try {
5168
- return await new Response(stream).text();
5169
- } catch {
5170
- return "";
5171
- }
5172
- }
5173
- async function runTmux(args) {
5174
- const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "pipe" });
5175
- const [errText, code] = await Promise.all([drainText(proc.stderr), proc.exited]);
5176
- if (code !== 0 && errText.trim().length > 0) {
5177
- console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
5178
- }
5179
- return code;
5180
- }
5181
- async function runTmuxQuiet(args) {
5182
- const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
5183
- return proc.exited;
5184
- }
5185
- function tmuxCommandSequence(commands) {
5186
- const out = [];
5187
- for (const cmd of commands) {
5188
- if (cmd.length === 0)
5189
- continue;
5190
- if (out.length > 0)
5191
- out.push(";");
5192
- out.push(...cmd);
5193
- }
5194
- return out;
5195
- }
5196
- async function runTmuxSequence(commands) {
5197
- const args = tmuxCommandSequence(commands);
5198
- return args.length === 0 ? 0 : runTmux(args);
5199
- }
5200
- async function runTmuxCapturing(args) {
5201
- const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "pipe", stderr: "pipe" });
5202
- const [stdout, errText, code] = await Promise.all([drainText(proc.stdout), drainText(proc.stderr), proc.exited]);
5203
- if (code !== 0 && errText.trim().length > 0) {
5204
- console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
5205
- }
5206
- return { code, stdout };
5207
- }
5208
- async function runTmuxSequenceCapturing(commands) {
5209
- const args = tmuxCommandSequence(commands);
5210
- return args.length === 0 ? { code: 0, stdout: "" } : runTmuxCapturing(args);
5211
- }
5212
- async function tmuxAvailable() {
5213
- try {
5214
- const proc = Bun.spawn(["tmux", "-V"], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
5215
- return await proc.exited === 0;
5216
- } catch {
5217
- return false;
5218
- }
5219
- }
5220
- async function sessionExists(name) {
5221
- return await runTmuxQuiet(["has-session", "-t", `=${name}`]) === 0;
5222
- }
5223
- async function windowCount(sessionName) {
5224
- const { code, stdout } = await runTmuxCapturing(["list-windows", "-t", `=${sessionName}`, "-F", "#{window_id}"]);
5225
- if (code !== 0)
5226
- return 0;
5227
- return stdout.split(`
5228
- `).filter((l) => l.trim().length > 0).length;
5229
- }
5230
- async function setSessionOption(session, option, value) {
5231
- await runTmux(["set-option", "-t", session, option, value]);
5232
- }
5233
- async function getSessionOption(session, option) {
5234
- const { code, stdout } = await runTmuxCapturing(["show-options", "-v", "-t", session, option]);
5235
- return code === 0 ? stdout.trim() : "";
5236
- }
5237
- async function getSessionOptions(session, options) {
5238
- const values = Object.fromEntries(options.map((option) => [option, undefined]));
5239
- const { code, stdout } = await runTmuxSequenceCapturing(options.map((option) => ["show-options", "-q", "-t", session, option]));
5240
- if (code !== 0)
5241
- return values;
5242
- for (const line of stdout.split(`
5243
- `)) {
5244
- const idx = line.indexOf(" ");
5245
- if (idx <= 0)
5246
- continue;
5247
- const option = line.slice(0, idx);
5248
- if (option in values)
5249
- values[option] = line.slice(idx + 1).trim();
5250
- }
5251
- return values;
5252
- }
5253
- async function tagPaneRole(paneId, role) {
5254
- await runTmux(["set-option", "-p", "-t", paneId, PANE_ROLE_OPTION, role]);
5255
- }
5256
- async function setWindowOption(target, option, value) {
5257
- await runTmux(["set-window-option", "-t", target, option, value]);
5258
- }
5259
- async function tagClaudePane(paneId) {
5260
- await tagPaneRole(paneId, CLAUDE_ROLE_VALUE);
5261
- }
5262
- async function paneIdByRole(sessionName, role, fallbackFirst = false) {
5263
- const { code, stdout } = await runTmuxCapturing([
5264
- "list-panes",
5265
- "-t",
5266
- `=${sessionName}`,
5267
- "-F",
5268
- `#{pane_id} #{${PANE_ROLE_OPTION}}`
5269
- ]);
5270
- if (code !== 0)
5271
- return "";
5272
- let firstId = "";
5273
- for (const line of stdout.split(`
5274
- `)) {
5275
- const [id, paneRole] = line.split("\t");
5276
- if (!id)
5277
- continue;
5278
- if (!firstId)
5279
- firstId = id.trim();
5280
- if (paneRole?.trim() === role)
5281
- return id.trim();
5282
- }
5283
- return fallbackFirst ? firstId : "";
5284
- }
5285
- async function claudePaneId(sessionName) {
5286
- return paneIdByRole(sessionName, CLAUDE_ROLE_VALUE, true);
5287
- }
5288
- async function claudePaneIdStrict(sessionName) {
5289
- return paneIdByRole(sessionName, CLAUDE_ROLE_VALUE, false);
5290
- }
5291
- async function capturePaneById(paneId, lines) {
5292
- if (!paneId)
5293
- return "";
5294
- const args = ["capture-pane", "-t", paneId, "-p"];
5295
- if (typeof lines === "number" && lines > 0)
5296
- args.push("-S", String(-lines));
5297
- const { code, stdout } = await runTmuxCapturing(args);
5298
- return code === 0 ? stdout : "";
5299
- }
5300
- async function sendKeys(target, text) {
5301
- await runTmux(["send-keys", "-t", target, "-l", "--", text]);
5302
- }
5303
- async function sendKeyName(target, key) {
5304
- await runTmux(["send-keys", "-t", target, key]);
5305
- }
5306
- async function newWindow(session, opts) {
5307
- const args = ["new-window", "-t", `=${session}`, "-c", opts.cwd];
5308
- if (opts.name)
5309
- args.push("-n", opts.name);
5310
- args.push(opts.command);
5311
- await runTmux(args);
5312
- }
5313
- async function currentSessionName() {
5314
- const args = ["display-message", "-p"];
5315
- const target = process.env.TMUX_PANE;
5316
- if (target && target.length > 0)
5317
- args.push("-t", target);
5318
- args.push("#{session_name}");
5319
- const { code, stdout } = await runTmuxCapturing(args);
5320
- const name = stdout.trim();
5321
- return code === 0 && name.length > 0 ? name : null;
5430
+
5431
+ // src/engine/interactive-command.ts
5432
+ import { randomUUID } from "crypto";
5433
+ function engineCommandKey(vendor) {
5434
+ return `engineCommand.${vendor}`;
5322
5435
  }
5323
- async function killSession(name) {
5324
- if (await sessionExists(name))
5325
- await runTmux(["kill-session", "-t", `=${name}`]);
5436
+ function defaultEngineCommand(vendor) {
5437
+ return DEFAULT_COMMANDS[vendor ?? "claude"] ?? DEFAULT_COMMANDS.claude;
5326
5438
  }
5327
- async function ensureFallbackSession() {
5328
- const name = KOBE_HOME_SESSION;
5329
- if (!await sessionExists(name)) {
5330
- await runTmux([
5331
- "new-session",
5332
- "-d",
5333
- "-s",
5334
- name,
5335
- "-x",
5336
- "220",
5337
- "-y",
5338
- "50",
5339
- "sh",
5340
- "-c",
5341
- 'clear; printf "\\n No active task\\n\\n Select a task from the sidebar to continue.\\n\\n"; exec ${SHELL:-sh}'
5342
- ]);
5439
+ function parseEngineCommand(command) {
5440
+ const out = [];
5441
+ const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
5442
+ for (let m = re.exec(command);m !== null; m = re.exec(command)) {
5443
+ out.push(m[1] ?? m[2] ?? m[3] ?? "");
5343
5444
  }
5344
- return name;
5445
+ return out;
5345
5446
  }
5346
- async function switchClientBeforeKill(killedName, nextSessionName) {
5347
- const current = await currentSessionName();
5348
- if (current !== killedName)
5349
- return;
5350
- if (nextSessionName && nextSessionName !== killedName && await sessionExists(nextSessionName)) {
5351
- await runTmux(["switch-client", "-t", `=${nextSessionName}`]);
5352
- return;
5447
+ function interactiveEngineCommand(vendor) {
5448
+ const v = vendor ?? "claude";
5449
+ const override = getPersistedString(engineCommandKey(v))?.trim();
5450
+ if (override) {
5451
+ const argv = parseEngineCommand(override);
5452
+ if (argv.length > 0)
5453
+ return argv;
5353
5454
  }
5354
- const fallback = await ensureFallbackSession();
5355
- await runTmux(["switch-client", "-t", `=${fallback}`]);
5455
+ return defaultEngineCommand(v);
5356
5456
  }
5357
- var KOBE_TMUX_SOCKET, PANE_ROLE_OPTION = "@kobe_role", CLAUDE_ROLE_OPTION, CLAUDE_ROLE_VALUE = "claude", KOBE_HOME_SESSION = "kobe-home";
5358
- var init_client2 = __esm(() => {
5359
- KOBE_TMUX_SOCKET = process.env.KOBE_TMUX_SOCKET?.trim() || "kobe";
5360
- CLAUDE_ROLE_OPTION = PANE_ROLE_OPTION;
5457
+ function withClaudeSessionId(argv, vendor) {
5458
+ if ((vendor ?? "claude") !== "claude")
5459
+ return { argv, sessionId: null };
5460
+ if (argv.some((a) => CLAUDE_SESSION_CONTROL_FLAGS.has(a)))
5461
+ return { argv, sessionId: null };
5462
+ const sessionId = randomUUID();
5463
+ return { argv: [...argv, "--session-id", sessionId], sessionId };
5464
+ }
5465
+ var DEFAULT_COMMANDS, VENDOR_LABEL, CLAUDE_SESSION_CONTROL_FLAGS;
5466
+ var init_interactive_command = __esm(() => {
5467
+ init_repos();
5468
+ DEFAULT_COMMANDS = {
5469
+ claude: ["claude"],
5470
+ codex: ["codex"],
5471
+ copilot: ["copilot"]
5472
+ };
5473
+ VENDOR_LABEL = {
5474
+ claude: "Claude",
5475
+ codex: "Codex",
5476
+ copilot: "Copilot"
5477
+ };
5478
+ CLAUDE_SESSION_CONTROL_FLAGS = new Set(["--session-id", "--resume", "-r", "--continue", "-c", "--from-pr"]);
5361
5479
  });
5362
5480
 
5363
5481
  // src/tmux/prompt-delivery.ts
@@ -5480,6 +5598,7 @@ __export(exports_tmux, {
5480
5598
  tmuxAvailable: () => tmuxAvailable,
5481
5599
  switchClientBeforeKill: () => switchClientBeforeKill,
5482
5600
  sessionExists: () => sessionExists,
5601
+ refreshKobeWorkspacePanes: () => refreshKobeWorkspacePanes,
5483
5602
  quickCreate: () => quickCreate,
5484
5603
  openUpdateTab: () => openUpdateTab,
5485
5604
  openSettingsTab: () => openSettingsTab,
@@ -5487,6 +5606,7 @@ __export(exports_tmux, {
5487
5606
  newChatTab: () => newChatTab,
5488
5607
  killSession: () => killSession,
5489
5608
  ensureSession: () => ensureSession,
5609
+ currentSessionName: () => currentSessionName,
5490
5610
  attachArgv: () => attachArgv,
5491
5611
  PANE_VERSION_OPTION: () => PANE_VERSION_OPTION,
5492
5612
  CHAT_TAB_SWITCH_BINDINGS: () => CHAT_TAB_SWITCH_BINDINGS,
@@ -5549,6 +5669,7 @@ async function ensureSessionImpl(opts) {
5549
5669
  await runTmux(["kill-session", "-t", `=${opts.name}`]);
5550
5670
  }
5551
5671
  const inv = kobeCliInvocation();
5672
+ const launch = withClaudeSessionId(opts.command, opts.vendor);
5552
5673
  const r0 = await runTmuxCapturing([
5553
5674
  "new-session",
5554
5675
  "-d",
@@ -5560,7 +5681,7 @@ async function ensureSessionImpl(opts) {
5560
5681
  "-P",
5561
5682
  "-F",
5562
5683
  "#{pane_id}",
5563
- engineLaunchLine(shellQuoteArgv(opts.command), {
5684
+ engineLaunchLine(shellQuoteArgv(launch.argv), {
5564
5685
  initScript: opts.initScript,
5565
5686
  markerPath: opts.initScript ? worktreeInitMarkerPath(opts.cwd) : undefined
5566
5687
  })
@@ -5570,6 +5691,8 @@ async function ensureSessionImpl(opts) {
5570
5691
  console.error("[kobe tmux] new-session returned no pane id; session creation failed");
5571
5692
  return false;
5572
5693
  }
5694
+ if (launch.sessionId)
5695
+ await setWindowOption(pane0, CHAT_TAB_SESSION_ID_OPTION, launch.sessionId);
5573
5696
  await runTmuxSequence([
5574
5697
  ...opts.taskId ? [["set-option", "-t", opts.name, "@kobe_task", opts.taskId]] : [],
5575
5698
  ["set-option", "-t", opts.name, "@kobe_worktree", opts.cwd],
@@ -5711,6 +5834,58 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
5711
5834
  if (commands.length > 0)
5712
5835
  await runTmuxSequence(commands);
5713
5836
  }
5837
+ async function refreshKobeWorkspacePanes(session) {
5838
+ const sessionOptions = await getSessionOptions(session, ["@kobe_worktree", "@kobe_task", "@kobe_vendor"]);
5839
+ const cwd = sessionOptions["@kobe_worktree"] || process.cwd();
5840
+ const taskId = sessionOptions["@kobe_task"] || undefined;
5841
+ const vendor = sessionOptions["@kobe_vendor"] || undefined;
5842
+ const inv = kobeCliInvocation();
5843
+ const envPrefix = inheritedEnvPrefix();
5844
+ const { code, stdout } = await runTmuxCapturing([
5845
+ "list-panes",
5846
+ "-s",
5847
+ "-t",
5848
+ `=${session}`,
5849
+ "-F",
5850
+ `#{window_id} #{pane_id} #{@kobe_role} #{${PANE_VERSION_OPTION}}`
5851
+ ]);
5852
+ if (code !== 0)
5853
+ return;
5854
+ const byWindow = new Map;
5855
+ for (const row of parseKobePaneRows(stdout)) {
5856
+ const panes = byWindow.get(row.windowId) ?? [];
5857
+ panes.push(row);
5858
+ byWindow.set(row.windowId, panes);
5859
+ }
5860
+ const commands = [];
5861
+ for (const panes of byWindow.values()) {
5862
+ const claudePane = panes.find((pane) => pane.role === "claude")?.paneId;
5863
+ const tasksPane = panes.find((pane) => pane.role === "tasks");
5864
+ const opsPane = panes.find((pane) => pane.role === "ops");
5865
+ if (tasksPane) {
5866
+ commands.push(["respawn-pane", "-k", "-t", tasksPane.paneId, "-c", cwd, keepAlive(envPrefix + tasksPaneCommand(inv))], ["set-option", "-p", "-t", tasksPane.paneId, "@kobe_role", "tasks"], ["set-option", "-p", "-t", tasksPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
5867
+ }
5868
+ if (opsPane && claudePane) {
5869
+ commands.push([
5870
+ "respawn-pane",
5871
+ "-k",
5872
+ "-t",
5873
+ opsPane.paneId,
5874
+ "-c",
5875
+ cwd,
5876
+ keepAlive(envPrefix + opsPaneCommand({
5877
+ cwd,
5878
+ taskId,
5879
+ claudePaneId: claudePane,
5880
+ cliInvocation: inv,
5881
+ vendor
5882
+ }))
5883
+ ], ["set-option", "-p", "-t", opsPane.paneId, "@kobe_role", "ops"], ["set-option", "-p", "-t", opsPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
5884
+ }
5885
+ }
5886
+ if (commands.length > 0)
5887
+ await runTmuxSequence(commands);
5888
+ }
5714
5889
  function inheritedEnvPrefix() {
5715
5890
  const parts = [];
5716
5891
  for (const key of ["KOBE_HOME_DIR", "KOBE_DAEMON_SOCKET_PATH", "KOBE_TMUX_SOCKET"]) {
@@ -5783,6 +5958,7 @@ async function newChatTab(session, vendorOverride) {
5783
5958
  if (vendorOverride)
5784
5959
  await rememberSessionVendor(session, taskId, vendorOverride);
5785
5960
  const command = interactiveEngineCommand(vendor);
5961
+ const launch = withClaudeSessionId(command, vendor);
5786
5962
  const inv = kobeCliInvocation();
5787
5963
  const r = await runTmuxCapturing([
5788
5964
  "new-window",
@@ -5793,11 +5969,13 @@ async function newChatTab(session, vendorOverride) {
5793
5969
  "-P",
5794
5970
  "-F",
5795
5971
  "#{pane_id}",
5796
- keepAlive(shellQuoteArgv(command))
5972
+ keepAlive(shellQuoteArgv(launch.argv))
5797
5973
  ]);
5798
5974
  const claudePane = r.stdout.trim();
5799
5975
  if (!claudePane)
5800
5976
  return;
5977
+ if (launch.sessionId)
5978
+ await setWindowOption(claudePane, CHAT_TAB_SESSION_ID_OPTION, launch.sessionId);
5801
5979
  await buildPanesAround(claudePane, { cwd, taskId, inv, vendor });
5802
5980
  }
5803
5981
  async function openSettingsTab(session) {
@@ -5949,7 +6127,8 @@ function readWorktreeChanges(worktreePath) {
5949
6127
  const out = spawnSync3("git", ["status", "--porcelain=v1"], {
5950
6128
  cwd: worktreePath,
5951
6129
  encoding: "utf8",
5952
- stdio: ["ignore", "pipe", "pipe"]
6130
+ stdio: ["ignore", "pipe", "pipe"],
6131
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
5953
6132
  });
5954
6133
  if (out.status !== 0 || !out.stdout)
5955
6134
  return ZERO;
@@ -12827,6 +13006,15 @@ var init_account_detect = __esm(() => {
12827
13006
  };
12828
13007
  });
12829
13008
 
13009
+ // src/tui/lib/editor-prefs.ts
13010
+ function normalizeEditorKind(value) {
13011
+ return value === "nano" || value === "custom" || value === "vim" ? value : DEFAULT_EDITOR_KIND;
13012
+ }
13013
+ var EDITOR_KINDS, EDITOR_KIND_KEY = "editor.kind", EDITOR_CUSTOM_KEY = "editor.customCommand", DEFAULT_EDITOR_KIND = "vim";
13014
+ var init_editor_prefs = __esm(() => {
13015
+ EDITOR_KINDS = ["vim", "nano", "custom"];
13016
+ });
13017
+
12830
13018
  // src/tui/lib/settings-surface.ts
12831
13019
  function normalizeSettingsSurface(value) {
12832
13020
  return value === "taskpanel" ? "taskpanel" : "chattab";
@@ -13015,7 +13203,7 @@ function devRowCount(hasDaemon) {
13015
13203
  return hasDaemon ? 2 : 1;
13016
13204
  }
13017
13205
  function generalRowCount(themeCount, focusAccentCount) {
13018
- return themeCount + 1 + focusAccentCount + 4;
13206
+ return themeCount + 1 + focusAccentCount + 6;
13019
13207
  }
13020
13208
  function bodyRowCount(section, themeCount, focusAccentCount, hasDaemon) {
13021
13209
  if (section === "general")
@@ -13050,6 +13238,12 @@ function surfaceChattabRowIndex(themeCount, focusAccentCount) {
13050
13238
  function surfaceTaskpanelRowIndex(themeCount, focusAccentCount) {
13051
13239
  return surfaceChattabRowIndex(themeCount, focusAccentCount) + 1;
13052
13240
  }
13241
+ function editorKindRowIndex(themeCount, focusAccentCount) {
13242
+ return surfaceTaskpanelRowIndex(themeCount, focusAccentCount) + 1;
13243
+ }
13244
+ function editorCustomRowIndex(themeCount, focusAccentCount) {
13245
+ return editorKindRowIndex(themeCount, focusAccentCount) + 1;
13246
+ }
13053
13247
  var SECTIONS, FOCUS_ACCENT_LABEL;
13054
13248
  var init_model = __esm(() => {
13055
13249
  init_vendor();
@@ -13118,13 +13312,17 @@ function GeneralSettingsSection(props) {
13118
13312
  const soundRow = () => soundRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13119
13313
  const surfaceChattabRow = () => surfaceChattabRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13120
13314
  const surfaceTaskpanelRow = () => surfaceTaskpanelRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13315
+ const editorKindRow = () => editorKindRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13316
+ const editorCustomRow = () => editorCustomRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13121
13317
  const isTransparentRow = () => props.bodyRow() === transparentRowIndex(props.themeNames().length);
13122
13318
  const isToastRow = () => props.bodyRow() === toastRow();
13123
13319
  const isSoundRow = () => props.bodyRow() === soundRow();
13124
13320
  const isSurfaceChattabRow = () => props.bodyRow() === surfaceChattabRow();
13125
13321
  const isSurfaceTaskpanelRow = () => props.bodyRow() === surfaceTaskpanelRow();
13322
+ const isEditorKindRow = () => props.bodyRow() === editorKindRow();
13323
+ const isEditorCustomRow = () => props.bodyRow() === editorCustomRow();
13126
13324
  return (() => {
13127
- var _el$4 = createElement("box"), _el$5 = createElement("text"), _el$7 = createElement("text"), _el$9 = createElement("box"), _el$0 = createElement("box"), _el$1 = createElement("text"), _el$11 = createElement("text"), _el$13 = createElement("box"), _el$14 = createElement("text"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$18 = createElement("text"), _el$20 = createElement("box"), _el$21 = createElement("text"), _el$23 = createElement("text"), _el$25 = createElement("box"), _el$26 = createElement("text"), _el$27 = createTextNode(` Toast`), _el$28 = createElement("box"), _el$29 = createElement("text"), _el$30 = createTextNode(` Sound`), _el$31 = createElement("box"), _el$32 = createElement("text"), _el$34 = createElement("text"), _el$36 = createElement("box"), _el$37 = createElement("text"), _el$38 = createTextNode(` ChatTab (separate page)`), _el$39 = createElement("box"), _el$40 = createElement("text"), _el$41 = createTextNode(` Task panel (in-pane overlay)`);
13325
+ var _el$4 = createElement("box"), _el$5 = createElement("text"), _el$7 = createElement("text"), _el$9 = createElement("box"), _el$0 = createElement("box"), _el$1 = createElement("text"), _el$11 = createElement("text"), _el$13 = createElement("box"), _el$14 = createElement("text"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$18 = createElement("text"), _el$20 = createElement("box"), _el$21 = createElement("text"), _el$23 = createElement("text"), _el$25 = createElement("box"), _el$26 = createElement("text"), _el$27 = createTextNode(` Toast`), _el$28 = createElement("box"), _el$29 = createElement("text"), _el$30 = createTextNode(` Sound`), _el$31 = createElement("box"), _el$32 = createElement("text"), _el$34 = createElement("text"), _el$36 = createElement("box"), _el$37 = createElement("text"), _el$38 = createTextNode(` ChatTab (separate page)`), _el$39 = createElement("box"), _el$40 = createElement("text"), _el$41 = createTextNode(` Task panel (in-pane overlay)`), _el$42 = createElement("box"), _el$43 = createElement("text"), _el$45 = createElement("text"), _el$47 = createElement("box"), _el$48 = createElement("text"), _el$49 = createElement("box"), _el$50 = createElement("text");
13128
13326
  insertNode(_el$4, _el$5);
13129
13327
  insertNode(_el$4, _el$7);
13130
13328
  insertNode(_el$4, _el$9);
@@ -13132,6 +13330,7 @@ function GeneralSettingsSection(props) {
13132
13330
  insertNode(_el$4, _el$15);
13133
13331
  insertNode(_el$4, _el$20);
13134
13332
  insertNode(_el$4, _el$31);
13333
+ insertNode(_el$4, _el$42);
13135
13334
  setProp(_el$4, "flexDirection", "column");
13136
13335
  setProp(_el$4, "gap", 1);
13137
13336
  insertNode(_el$5, createTextNode(`Theme`));
@@ -13146,33 +13345,33 @@ function GeneralSettingsSection(props) {
13146
13345
  const isCursor = () => props.level() === "body" && props.bodyRow() === i();
13147
13346
  const isSelected = () => name === themeCtx.selected;
13148
13347
  return (() => {
13149
- var _el$42 = createElement("box"), _el$43 = createElement("text");
13150
- insertNode(_el$42, _el$43);
13151
- setProp(_el$42, "flexDirection", "row");
13152
- setProp(_el$42, "gap", 1);
13153
- setProp(_el$42, "paddingLeft", 1);
13154
- setProp(_el$42, "paddingRight", 1);
13155
- setProp(_el$42, "onMouseUp", () => {
13348
+ var _el$51 = createElement("box"), _el$52 = createElement("text");
13349
+ insertNode(_el$51, _el$52);
13350
+ setProp(_el$51, "flexDirection", "row");
13351
+ setProp(_el$51, "gap", 1);
13352
+ setProp(_el$51, "paddingLeft", 1);
13353
+ setProp(_el$51, "paddingRight", 1);
13354
+ setProp(_el$51, "onMouseUp", () => {
13156
13355
  props.setLevel("body");
13157
13356
  props.setBodyRow(i());
13158
13357
  props.setThemeCursor(i());
13159
- themeCtx.set(name);
13358
+ props.selectTheme(name);
13160
13359
  });
13161
- setProp(_el$43, "wrapMode", "none");
13162
- insert(_el$43, () => isSelected() ? "\u25CF " : " ", null);
13163
- insert(_el$43, name, null);
13360
+ setProp(_el$52, "wrapMode", "none");
13361
+ insert(_el$52, () => isSelected() ? "\u25CF " : " ", null);
13362
+ insert(_el$52, name, null);
13164
13363
  effect((_p$) => {
13165
- var _v$32 = isCursor() ? theme.primary : undefined, _v$33 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.accent : theme.text, _v$34 = isCursor() || isSelected() ? TextAttributes5.BOLD : undefined;
13166
- _v$32 !== _p$.e && (_p$.e = setProp(_el$42, "backgroundColor", _v$32, _p$.e));
13167
- _v$33 !== _p$.t && (_p$.t = setProp(_el$43, "fg", _v$33, _p$.t));
13168
- _v$34 !== _p$.a && (_p$.a = setProp(_el$43, "attributes", _v$34, _p$.a));
13364
+ var _v$40 = isCursor() ? theme.primary : undefined, _v$41 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.accent : theme.text, _v$42 = isCursor() || isSelected() ? TextAttributes5.BOLD : undefined;
13365
+ _v$40 !== _p$.e && (_p$.e = setProp(_el$51, "backgroundColor", _v$40, _p$.e));
13366
+ _v$41 !== _p$.t && (_p$.t = setProp(_el$52, "fg", _v$41, _p$.t));
13367
+ _v$42 !== _p$.a && (_p$.a = setProp(_el$52, "attributes", _v$42, _p$.a));
13169
13368
  return _p$;
13170
13369
  }, {
13171
13370
  e: undefined,
13172
13371
  t: undefined,
13173
13372
  a: undefined
13174
13373
  });
13175
- return _el$42;
13374
+ return _el$51;
13176
13375
  })();
13177
13376
  }
13178
13377
  }));
@@ -13192,7 +13391,7 @@ function GeneralSettingsSection(props) {
13192
13391
  setProp(_el$13, "onMouseUp", () => {
13193
13392
  props.setLevel("body");
13194
13393
  props.setBodyRow(transparentRowIndex(props.themeNames().length));
13195
- themeCtx.setTransparentBackground(!themeCtx.transparentBackground);
13394
+ props.toggleTransparent();
13196
13395
  });
13197
13396
  setProp(_el$14, "wrapMode", "none");
13198
13397
  insert(_el$14, () => themeCtx.transparentBackground ? "[x] on" : "[ ] off");
@@ -13211,32 +13410,32 @@ function GeneralSettingsSection(props) {
13211
13410
  const isCursor = () => props.level() === "body" && props.bodyRow() === rowIndex();
13212
13411
  const isSelected = () => themeCtx.focusAccent === slot;
13213
13412
  return (() => {
13214
- var _el$44 = createElement("box"), _el$45 = createElement("text");
13215
- insertNode(_el$44, _el$45);
13216
- setProp(_el$44, "flexDirection", "row");
13217
- setProp(_el$44, "gap", 1);
13218
- setProp(_el$44, "paddingLeft", 1);
13219
- setProp(_el$44, "paddingRight", 1);
13220
- setProp(_el$44, "onMouseUp", () => {
13413
+ var _el$53 = createElement("box"), _el$54 = createElement("text");
13414
+ insertNode(_el$53, _el$54);
13415
+ setProp(_el$53, "flexDirection", "row");
13416
+ setProp(_el$53, "gap", 1);
13417
+ setProp(_el$53, "paddingLeft", 1);
13418
+ setProp(_el$53, "paddingRight", 1);
13419
+ setProp(_el$53, "onMouseUp", () => {
13221
13420
  props.setLevel("body");
13222
13421
  props.setBodyRow(rowIndex());
13223
- themeCtx.setFocusAccent(slot);
13422
+ props.selectFocusAccent(slot);
13224
13423
  });
13225
- setProp(_el$45, "wrapMode", "none");
13226
- insert(_el$45, () => isSelected() ? "\u25CF " : " ", null);
13227
- insert(_el$45, () => FOCUS_ACCENT_LABEL[slot], null);
13424
+ setProp(_el$54, "wrapMode", "none");
13425
+ insert(_el$54, () => isSelected() ? "\u25CF " : " ", null);
13426
+ insert(_el$54, () => FOCUS_ACCENT_LABEL[slot], null);
13228
13427
  effect((_p$) => {
13229
- var _v$35 = isCursor() ? theme.primary : undefined, _v$36 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.focusAccent : theme.text, _v$37 = isCursor() || isSelected() ? TextAttributes5.BOLD : undefined;
13230
- _v$35 !== _p$.e && (_p$.e = setProp(_el$44, "backgroundColor", _v$35, _p$.e));
13231
- _v$36 !== _p$.t && (_p$.t = setProp(_el$45, "fg", _v$36, _p$.t));
13232
- _v$37 !== _p$.a && (_p$.a = setProp(_el$45, "attributes", _v$37, _p$.a));
13428
+ var _v$43 = isCursor() ? theme.primary : undefined, _v$44 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.focusAccent : theme.text, _v$45 = isCursor() || isSelected() ? TextAttributes5.BOLD : undefined;
13429
+ _v$43 !== _p$.e && (_p$.e = setProp(_el$53, "backgroundColor", _v$43, _p$.e));
13430
+ _v$44 !== _p$.t && (_p$.t = setProp(_el$54, "fg", _v$44, _p$.t));
13431
+ _v$45 !== _p$.a && (_p$.a = setProp(_el$54, "attributes", _v$45, _p$.a));
13233
13432
  return _p$;
13234
13433
  }, {
13235
13434
  e: undefined,
13236
13435
  t: undefined,
13237
13436
  a: undefined
13238
13437
  });
13239
- return _el$44;
13438
+ return _el$53;
13240
13439
  })();
13241
13440
  }
13242
13441
  }), null);
@@ -13312,8 +13511,42 @@ function GeneralSettingsSection(props) {
13312
13511
  insertNode(_el$40, _el$41);
13313
13512
  setProp(_el$40, "wrapMode", "none");
13314
13513
  insert(_el$40, () => props.settingsSurface() === "taskpanel" ? "[x]" : "[ ]", _el$41);
13514
+ insertNode(_el$42, _el$43);
13515
+ insertNode(_el$42, _el$45);
13516
+ insertNode(_el$42, _el$47);
13517
+ insertNode(_el$42, _el$49);
13518
+ setProp(_el$42, "flexDirection", "column");
13519
+ setProp(_el$42, "gap", 0);
13520
+ setProp(_el$42, "paddingTop", 1);
13521
+ insertNode(_el$43, createTextNode(`Editor`));
13522
+ insertNode(_el$45, createTextNode(`What \`e\` opens a file with in the file tree (enter stays the read-only preview). enter on the row below cycles vim / nano / custom; if the editor isn't installed it falls back to the preview.`));
13523
+ setProp(_el$45, "wrapMode", "word");
13524
+ insertNode(_el$47, _el$48);
13525
+ setProp(_el$47, "flexDirection", "row");
13526
+ setProp(_el$47, "gap", 1);
13527
+ setProp(_el$47, "paddingLeft", 1);
13528
+ setProp(_el$47, "paddingRight", 1);
13529
+ setProp(_el$47, "onMouseUp", () => {
13530
+ props.setLevel("body");
13531
+ props.setBodyRow(editorKindRow());
13532
+ props.cycleEditorKind();
13533
+ });
13534
+ setProp(_el$48, "wrapMode", "none");
13535
+ insert(_el$48, () => `editor: < ${props.editorKind()} > (enter to change)`);
13536
+ insertNode(_el$49, _el$50);
13537
+ setProp(_el$49, "flexDirection", "row");
13538
+ setProp(_el$49, "gap", 1);
13539
+ setProp(_el$49, "paddingLeft", 1);
13540
+ setProp(_el$49, "paddingRight", 1);
13541
+ setProp(_el$49, "onMouseUp", () => {
13542
+ props.setLevel("body");
13543
+ props.setBodyRow(editorCustomRow());
13544
+ props.editEditorCustom();
13545
+ });
13546
+ setProp(_el$50, "wrapMode", "none");
13547
+ insert(_el$50, () => `custom: ${props.editorCustomCommand().trim() || "(unset \u2014 enter to edit)"}`);
13315
13548
  effect((_p$) => {
13316
- var _v$4 = theme.text, _v$5 = TextAttributes5.BOLD, _v$6 = theme.textMuted, _v$7 = theme.text, _v$8 = TextAttributes5.BOLD, _v$9 = theme.textMuted, _v$0 = isTransparentRow() ? theme.primary : undefined, _v$1 = isTransparentRow() ? theme.selectedListItemText : themeCtx.transparentBackground ? theme.accent : theme.textMuted, _v$10 = TextAttributes5.BOLD, _v$11 = theme.text, _v$12 = TextAttributes5.BOLD, _v$13 = theme.textMuted, _v$14 = theme.text, _v$15 = TextAttributes5.BOLD, _v$16 = theme.textMuted, _v$17 = isToastRow() ? theme.primary : undefined, _v$18 = isToastRow() ? theme.selectedListItemText : props.toastEnabled() ? theme.accent : theme.textMuted, _v$19 = TextAttributes5.BOLD, _v$20 = isSoundRow() ? theme.primary : undefined, _v$21 = isSoundRow() ? theme.selectedListItemText : props.soundEnabled() ? theme.accent : theme.textMuted, _v$22 = TextAttributes5.BOLD, _v$23 = theme.text, _v$24 = TextAttributes5.BOLD, _v$25 = theme.textMuted, _v$26 = isSurfaceChattabRow() ? theme.primary : undefined, _v$27 = isSurfaceChattabRow() ? theme.selectedListItemText : props.settingsSurface() === "chattab" ? theme.accent : theme.textMuted, _v$28 = TextAttributes5.BOLD, _v$29 = isSurfaceTaskpanelRow() ? theme.primary : undefined, _v$30 = isSurfaceTaskpanelRow() ? theme.selectedListItemText : props.settingsSurface() === "taskpanel" ? theme.accent : theme.textMuted, _v$31 = TextAttributes5.BOLD;
13549
+ var _v$4 = theme.text, _v$5 = TextAttributes5.BOLD, _v$6 = theme.textMuted, _v$7 = theme.text, _v$8 = TextAttributes5.BOLD, _v$9 = theme.textMuted, _v$0 = isTransparentRow() ? theme.primary : undefined, _v$1 = isTransparentRow() ? theme.selectedListItemText : themeCtx.transparentBackground ? theme.accent : theme.textMuted, _v$10 = TextAttributes5.BOLD, _v$11 = theme.text, _v$12 = TextAttributes5.BOLD, _v$13 = theme.textMuted, _v$14 = theme.text, _v$15 = TextAttributes5.BOLD, _v$16 = theme.textMuted, _v$17 = isToastRow() ? theme.primary : undefined, _v$18 = isToastRow() ? theme.selectedListItemText : props.toastEnabled() ? theme.accent : theme.textMuted, _v$19 = TextAttributes5.BOLD, _v$20 = isSoundRow() ? theme.primary : undefined, _v$21 = isSoundRow() ? theme.selectedListItemText : props.soundEnabled() ? theme.accent : theme.textMuted, _v$22 = TextAttributes5.BOLD, _v$23 = theme.text, _v$24 = TextAttributes5.BOLD, _v$25 = theme.textMuted, _v$26 = isSurfaceChattabRow() ? theme.primary : undefined, _v$27 = isSurfaceChattabRow() ? theme.selectedListItemText : props.settingsSurface() === "chattab" ? theme.accent : theme.textMuted, _v$28 = TextAttributes5.BOLD, _v$29 = isSurfaceTaskpanelRow() ? theme.primary : undefined, _v$30 = isSurfaceTaskpanelRow() ? theme.selectedListItemText : props.settingsSurface() === "taskpanel" ? theme.accent : theme.textMuted, _v$31 = TextAttributes5.BOLD, _v$32 = theme.text, _v$33 = TextAttributes5.BOLD, _v$34 = theme.textMuted, _v$35 = isEditorKindRow() ? theme.primary : undefined, _v$36 = isEditorKindRow() ? theme.selectedListItemText : theme.accent, _v$37 = TextAttributes5.BOLD, _v$38 = isEditorCustomRow() ? theme.primary : undefined, _v$39 = isEditorCustomRow() ? theme.selectedListItemText : props.editorKind() === "custom" ? theme.text : theme.textMuted;
13317
13550
  _v$4 !== _p$.e && (_p$.e = setProp(_el$5, "fg", _v$4, _p$.e));
13318
13551
  _v$5 !== _p$.t && (_p$.t = setProp(_el$5, "attributes", _v$5, _p$.t));
13319
13552
  _v$6 !== _p$.a && (_p$.a = setProp(_el$7, "fg", _v$6, _p$.a));
@@ -13344,6 +13577,14 @@ function GeneralSettingsSection(props) {
13344
13577
  _v$29 !== _p$.B && (_p$.B = setProp(_el$39, "backgroundColor", _v$29, _p$.B));
13345
13578
  _v$30 !== _p$.v && (_p$.v = setProp(_el$40, "fg", _v$30, _p$.v));
13346
13579
  _v$31 !== _p$.k && (_p$.k = setProp(_el$40, "attributes", _v$31, _p$.k));
13580
+ _v$32 !== _p$.x && (_p$.x = setProp(_el$43, "fg", _v$32, _p$.x));
13581
+ _v$33 !== _p$.j && (_p$.j = setProp(_el$43, "attributes", _v$33, _p$.j));
13582
+ _v$34 !== _p$.q && (_p$.q = setProp(_el$45, "fg", _v$34, _p$.q));
13583
+ _v$35 !== _p$.z && (_p$.z = setProp(_el$47, "backgroundColor", _v$35, _p$.z));
13584
+ _v$36 !== _p$.P && (_p$.P = setProp(_el$48, "fg", _v$36, _p$.P));
13585
+ _v$37 !== _p$.H && (_p$.H = setProp(_el$48, "attributes", _v$37, _p$.H));
13586
+ _v$38 !== _p$.F && (_p$.F = setProp(_el$49, "backgroundColor", _v$38, _p$.F));
13587
+ _v$39 !== _p$.M && (_p$.M = setProp(_el$50, "fg", _v$39, _p$.M));
13347
13588
  return _p$;
13348
13589
  }, {
13349
13590
  e: undefined,
@@ -13375,7 +13616,15 @@ function GeneralSettingsSection(props) {
13375
13616
  C: undefined,
13376
13617
  B: undefined,
13377
13618
  v: undefined,
13378
- k: undefined
13619
+ k: undefined,
13620
+ x: undefined,
13621
+ j: undefined,
13622
+ q: undefined,
13623
+ z: undefined,
13624
+ P: undefined,
13625
+ H: undefined,
13626
+ F: undefined,
13627
+ M: undefined
13379
13628
  });
13380
13629
  return _el$4;
13381
13630
  })();
@@ -13385,47 +13634,47 @@ function EngineSettingsSection(props) {
13385
13634
  theme
13386
13635
  } = useTheme();
13387
13636
  return (() => {
13388
- var _el$46 = createElement("box"), _el$47 = createElement("text"), _el$49 = createElement("text"), _el$51 = createElement("box");
13389
- insertNode(_el$46, _el$47);
13390
- insertNode(_el$46, _el$49);
13391
- insertNode(_el$46, _el$51);
13392
- setProp(_el$46, "flexDirection", "column");
13393
- setProp(_el$46, "gap", 1);
13394
- insertNode(_el$47, createTextNode(`Launch command`));
13395
- insertNode(_el$49, createTextNode(`The command each engine's task pane runs. Override it when your binary isn't on PATH as \`claude\` / \`codex\` (e.g. it's \`cl\`) or to pass default flags. enter to edit \xB7 takes effect on the next task enter.`));
13396
- setProp(_el$49, "wrapMode", "word");
13397
- setProp(_el$51, "flexDirection", "column");
13398
- setProp(_el$51, "gap", 0);
13399
- insert(_el$51, createComponent2(For, {
13637
+ var _el$55 = createElement("box"), _el$56 = createElement("text"), _el$58 = createElement("text"), _el$60 = createElement("box");
13638
+ insertNode(_el$55, _el$56);
13639
+ insertNode(_el$55, _el$58);
13640
+ insertNode(_el$55, _el$60);
13641
+ setProp(_el$55, "flexDirection", "column");
13642
+ setProp(_el$55, "gap", 1);
13643
+ insertNode(_el$56, createTextNode(`Launch command`));
13644
+ insertNode(_el$58, createTextNode(`The command each engine's task pane runs. Override it when your binary isn't on PATH as \`claude\` / \`codex\` (e.g. it's \`cl\`) or to pass default flags. enter to edit \xB7 takes effect on the next task enter.`));
13645
+ setProp(_el$58, "wrapMode", "word");
13646
+ setProp(_el$60, "flexDirection", "column");
13647
+ setProp(_el$60, "gap", 0);
13648
+ insert(_el$60, createComponent2(For, {
13400
13649
  get each() {
13401
13650
  return props.vendors;
13402
13651
  },
13403
13652
  children: (vendor, i) => {
13404
13653
  const isCursor = () => props.level() === "body" && props.bodyRow() === i();
13405
13654
  return (() => {
13406
- var _el$52 = createElement("box"), _el$53 = createElement("text"), _el$54 = createElement("text");
13407
- insertNode(_el$52, _el$53);
13408
- insertNode(_el$52, _el$54);
13409
- setProp(_el$52, "flexDirection", "row");
13410
- setProp(_el$52, "gap", 1);
13411
- setProp(_el$52, "paddingLeft", 1);
13412
- setProp(_el$52, "paddingRight", 1);
13413
- setProp(_el$52, "onMouseUp", () => {
13655
+ var _el$61 = createElement("box"), _el$62 = createElement("text"), _el$63 = createElement("text");
13656
+ insertNode(_el$61, _el$62);
13657
+ insertNode(_el$61, _el$63);
13658
+ setProp(_el$61, "flexDirection", "row");
13659
+ setProp(_el$61, "gap", 1);
13660
+ setProp(_el$61, "paddingLeft", 1);
13661
+ setProp(_el$61, "paddingRight", 1);
13662
+ setProp(_el$61, "onMouseUp", () => {
13414
13663
  props.setLevel("body");
13415
13664
  props.setBodyRow(i());
13416
13665
  props.editEngine(vendor);
13417
13666
  });
13418
- setProp(_el$53, "wrapMode", "none");
13419
- insert(_el$53, () => VENDOR_LABEL[vendor]);
13420
- setProp(_el$54, "wrapMode", "none");
13421
- insert(_el$54, () => props.commandText(vendor), null);
13422
- insert(_el$54, () => props.isDefault(vendor) ? " (default)" : "", null);
13667
+ setProp(_el$62, "wrapMode", "none");
13668
+ insert(_el$62, () => VENDOR_LABEL[vendor]);
13669
+ setProp(_el$63, "wrapMode", "none");
13670
+ insert(_el$63, () => props.commandText(vendor), null);
13671
+ insert(_el$63, () => props.isDefault(vendor) ? " (default)" : "", null);
13423
13672
  effect((_p$) => {
13424
- var _v$41 = isCursor() ? theme.primary : undefined, _v$42 = isCursor() ? theme.selectedListItemText : theme.text, _v$43 = TextAttributes5.BOLD, _v$44 = isCursor() ? theme.selectedListItemText : props.isDefault(vendor) ? theme.textMuted : theme.accent;
13425
- _v$41 !== _p$.e && (_p$.e = setProp(_el$52, "backgroundColor", _v$41, _p$.e));
13426
- _v$42 !== _p$.t && (_p$.t = setProp(_el$53, "fg", _v$42, _p$.t));
13427
- _v$43 !== _p$.a && (_p$.a = setProp(_el$53, "attributes", _v$43, _p$.a));
13428
- _v$44 !== _p$.o && (_p$.o = setProp(_el$54, "fg", _v$44, _p$.o));
13673
+ var _v$49 = isCursor() ? theme.primary : undefined, _v$50 = isCursor() ? theme.selectedListItemText : theme.text, _v$51 = TextAttributes5.BOLD, _v$52 = isCursor() ? theme.selectedListItemText : props.isDefault(vendor) ? theme.textMuted : theme.accent;
13674
+ _v$49 !== _p$.e && (_p$.e = setProp(_el$61, "backgroundColor", _v$49, _p$.e));
13675
+ _v$50 !== _p$.t && (_p$.t = setProp(_el$62, "fg", _v$50, _p$.t));
13676
+ _v$51 !== _p$.a && (_p$.a = setProp(_el$62, "attributes", _v$51, _p$.a));
13677
+ _v$52 !== _p$.o && (_p$.o = setProp(_el$63, "fg", _v$52, _p$.o));
13429
13678
  return _p$;
13430
13679
  }, {
13431
13680
  e: undefined,
@@ -13433,22 +13682,22 @@ function EngineSettingsSection(props) {
13433
13682
  a: undefined,
13434
13683
  o: undefined
13435
13684
  });
13436
- return _el$52;
13685
+ return _el$61;
13437
13686
  })();
13438
13687
  }
13439
13688
  }));
13440
13689
  effect((_p$) => {
13441
- var _v$38 = theme.text, _v$39 = TextAttributes5.BOLD, _v$40 = theme.textMuted;
13442
- _v$38 !== _p$.e && (_p$.e = setProp(_el$47, "fg", _v$38, _p$.e));
13443
- _v$39 !== _p$.t && (_p$.t = setProp(_el$47, "attributes", _v$39, _p$.t));
13444
- _v$40 !== _p$.a && (_p$.a = setProp(_el$49, "fg", _v$40, _p$.a));
13690
+ var _v$46 = theme.text, _v$47 = TextAttributes5.BOLD, _v$48 = theme.textMuted;
13691
+ _v$46 !== _p$.e && (_p$.e = setProp(_el$56, "fg", _v$46, _p$.e));
13692
+ _v$47 !== _p$.t && (_p$.t = setProp(_el$56, "attributes", _v$47, _p$.t));
13693
+ _v$48 !== _p$.a && (_p$.a = setProp(_el$58, "fg", _v$48, _p$.a));
13445
13694
  return _p$;
13446
13695
  }, {
13447
13696
  e: undefined,
13448
13697
  t: undefined,
13449
13698
  a: undefined
13450
13699
  });
13451
- return _el$46;
13700
+ return _el$55;
13452
13701
  })();
13453
13702
  }
13454
13703
  function AccountsSettingsSection(props) {
@@ -13457,221 +13706,221 @@ function AccountsSettingsSection(props) {
13457
13706
  } = useTheme();
13458
13707
  const binaryLine = (s) => s.binary.found ? `Binary: ${s.binary.path}` : `Binary: ${s.binary.error}`;
13459
13708
  return (() => {
13460
- var _el$55 = createElement("box"), _el$56 = createElement("text"), _el$58 = createElement("text"), _el$60 = createElement("box"), _el$61 = createElement("text"), _el$65 = createElement("box"), _el$66 = createElement("text"), _el$70 = createElement("box"), _el$71 = createElement("text");
13461
- insertNode(_el$55, _el$56);
13462
- insertNode(_el$55, _el$58);
13463
- insertNode(_el$55, _el$60);
13464
- insertNode(_el$55, _el$65);
13465
- insertNode(_el$55, _el$70);
13466
- setProp(_el$55, "flexDirection", "column");
13467
- setProp(_el$55, "gap", 1);
13468
- insertNode(_el$56, createTextNode(`Accounts`));
13469
- insertNode(_el$58, createTextNode(`Read-only view of locally-detected engine accounts. Login flows land here later.`));
13470
- setProp(_el$58, "wrapMode", "word");
13471
- insertNode(_el$60, _el$61);
13472
- setProp(_el$60, "flexDirection", "column");
13473
- setProp(_el$60, "gap", 0);
13474
- insertNode(_el$61, createTextNode(`claude-code`));
13475
- insert(_el$60, createComponent2(Show, {
13709
+ var _el$64 = createElement("box"), _el$65 = createElement("text"), _el$67 = createElement("text"), _el$69 = createElement("box"), _el$70 = createElement("text"), _el$74 = createElement("box"), _el$75 = createElement("text"), _el$79 = createElement("box"), _el$80 = createElement("text");
13710
+ insertNode(_el$64, _el$65);
13711
+ insertNode(_el$64, _el$67);
13712
+ insertNode(_el$64, _el$69);
13713
+ insertNode(_el$64, _el$74);
13714
+ insertNode(_el$64, _el$79);
13715
+ setProp(_el$64, "flexDirection", "column");
13716
+ setProp(_el$64, "gap", 1);
13717
+ insertNode(_el$65, createTextNode(`Accounts`));
13718
+ insertNode(_el$67, createTextNode(`Read-only view of locally-detected engine accounts. Login flows land here later.`));
13719
+ setProp(_el$67, "wrapMode", "word");
13720
+ insertNode(_el$69, _el$70);
13721
+ setProp(_el$69, "flexDirection", "column");
13722
+ setProp(_el$69, "gap", 0);
13723
+ insertNode(_el$70, createTextNode(`claude-code`));
13724
+ insert(_el$69, createComponent2(Show, {
13476
13725
  get when() {
13477
13726
  return props.claudeStatus() === null;
13478
13727
  },
13479
13728
  get children() {
13480
- var _el$63 = createElement("text");
13481
- insertNode(_el$63, createTextNode(`Checking\u2026`));
13482
- effect((_$p) => setProp(_el$63, "fg", theme.textMuted, _$p));
13483
- return _el$63;
13729
+ var _el$72 = createElement("text");
13730
+ insertNode(_el$72, createTextNode(`Checking\u2026`));
13731
+ effect((_$p) => setProp(_el$72, "fg", theme.textMuted, _$p));
13732
+ return _el$72;
13484
13733
  }
13485
13734
  }), null);
13486
- insert(_el$60, createComponent2(Show, {
13735
+ insert(_el$69, createComponent2(Show, {
13487
13736
  get when() {
13488
13737
  return props.claudeStatus();
13489
13738
  },
13490
13739
  children: (s) => (() => {
13491
- var _el$75 = createElement("box"), _el$76 = createElement("text");
13492
- insertNode(_el$75, _el$76);
13493
- setProp(_el$75, "flexDirection", "column");
13494
- setProp(_el$75, "gap", 0);
13495
- setProp(_el$76, "wrapMode", "word");
13496
- insert(_el$76, () => binaryLine(s()));
13497
- insert(_el$75, () => {
13740
+ var _el$84 = createElement("box"), _el$85 = createElement("text");
13741
+ insertNode(_el$84, _el$85);
13742
+ setProp(_el$84, "flexDirection", "column");
13743
+ setProp(_el$84, "gap", 0);
13744
+ setProp(_el$85, "wrapMode", "word");
13745
+ insert(_el$85, () => binaryLine(s()));
13746
+ insert(_el$84, () => {
13498
13747
  const a = s().account;
13499
13748
  if (a.kind === "oauth") {
13500
13749
  const tail = [a.organization, a.billingType].filter((x) => !!x).join(" \xB7 ");
13501
13750
  return (() => {
13502
- var _el$77 = createElement("text");
13503
- setProp(_el$77, "wrapMode", "word");
13504
- insert(_el$77, () => `\u25CF Logged in: ${a.email}${tail ? ` (${tail})` : ""}`);
13505
- effect((_$p) => setProp(_el$77, "fg", theme.success, _$p));
13506
- return _el$77;
13751
+ var _el$86 = createElement("text");
13752
+ setProp(_el$86, "wrapMode", "word");
13753
+ insert(_el$86, () => `\u25CF Logged in: ${a.email}${tail ? ` (${tail})` : ""}`);
13754
+ effect((_$p) => setProp(_el$86, "fg", theme.success, _$p));
13755
+ return _el$86;
13507
13756
  })();
13508
13757
  }
13509
13758
  return (() => {
13510
- var _el$78 = createElement("text");
13511
- insertNode(_el$78, createTextNode(`\u25CB Not logged in`));
13512
- effect((_$p) => setProp(_el$78, "fg", theme.textMuted, _$p));
13513
- return _el$78;
13759
+ var _el$87 = createElement("text");
13760
+ insertNode(_el$87, createTextNode(`\u25CB Not logged in`));
13761
+ effect((_$p) => setProp(_el$87, "fg", theme.textMuted, _$p));
13762
+ return _el$87;
13514
13763
  })();
13515
13764
  }, null);
13516
- insert(_el$75, createComponent2(Show, {
13765
+ insert(_el$84, createComponent2(Show, {
13517
13766
  get when() {
13518
13767
  return s().accountError;
13519
13768
  },
13520
13769
  children: (err) => (() => {
13521
- var _el$80 = createElement("text");
13522
- setProp(_el$80, "wrapMode", "word");
13523
- insert(_el$80, () => `! ${err()}`);
13524
- effect((_$p) => setProp(_el$80, "fg", theme.warning, _$p));
13525
- return _el$80;
13770
+ var _el$89 = createElement("text");
13771
+ setProp(_el$89, "wrapMode", "word");
13772
+ insert(_el$89, () => `! ${err()}`);
13773
+ effect((_$p) => setProp(_el$89, "fg", theme.warning, _$p));
13774
+ return _el$89;
13526
13775
  })()
13527
13776
  }), null);
13528
- effect((_$p) => setProp(_el$76, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13529
- return _el$75;
13777
+ effect((_$p) => setProp(_el$85, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13778
+ return _el$84;
13530
13779
  })()
13531
13780
  }), null);
13532
- insertNode(_el$65, _el$66);
13533
- setProp(_el$65, "flexDirection", "column");
13534
- setProp(_el$65, "gap", 0);
13535
- insertNode(_el$66, createTextNode(`codex`));
13536
- insert(_el$65, createComponent2(Show, {
13781
+ insertNode(_el$74, _el$75);
13782
+ setProp(_el$74, "flexDirection", "column");
13783
+ setProp(_el$74, "gap", 0);
13784
+ insertNode(_el$75, createTextNode(`codex`));
13785
+ insert(_el$74, createComponent2(Show, {
13537
13786
  get when() {
13538
13787
  return props.codexStatus() === null;
13539
13788
  },
13540
13789
  get children() {
13541
- var _el$68 = createElement("text");
13542
- insertNode(_el$68, createTextNode(`Checking\u2026`));
13543
- effect((_$p) => setProp(_el$68, "fg", theme.textMuted, _$p));
13544
- return _el$68;
13790
+ var _el$77 = createElement("text");
13791
+ insertNode(_el$77, createTextNode(`Checking\u2026`));
13792
+ effect((_$p) => setProp(_el$77, "fg", theme.textMuted, _$p));
13793
+ return _el$77;
13545
13794
  }
13546
13795
  }), null);
13547
- insert(_el$65, createComponent2(Show, {
13796
+ insert(_el$74, createComponent2(Show, {
13548
13797
  get when() {
13549
13798
  return props.codexStatus();
13550
13799
  },
13551
13800
  children: (s) => (() => {
13552
- var _el$81 = createElement("box"), _el$82 = createElement("text");
13553
- insertNode(_el$81, _el$82);
13554
- setProp(_el$81, "flexDirection", "column");
13555
- setProp(_el$81, "gap", 0);
13556
- setProp(_el$82, "wrapMode", "word");
13557
- insert(_el$82, () => binaryLine(s()));
13558
- insert(_el$81, () => {
13801
+ var _el$90 = createElement("box"), _el$91 = createElement("text");
13802
+ insertNode(_el$90, _el$91);
13803
+ setProp(_el$90, "flexDirection", "column");
13804
+ setProp(_el$90, "gap", 0);
13805
+ setProp(_el$91, "wrapMode", "word");
13806
+ insert(_el$91, () => binaryLine(s()));
13807
+ insert(_el$90, () => {
13559
13808
  const a = s().account;
13560
13809
  if (a.kind === "chatgpt") {
13561
13810
  return (() => {
13562
- var _el$83 = createElement("text");
13563
- setProp(_el$83, "wrapMode", "word");
13564
- insert(_el$83, () => `\u25CF ChatGPT login: ${a.email}${a.plan ? ` (${a.plan})` : ""}`);
13565
- effect((_$p) => setProp(_el$83, "fg", theme.success, _$p));
13566
- return _el$83;
13811
+ var _el$92 = createElement("text");
13812
+ setProp(_el$92, "wrapMode", "word");
13813
+ insert(_el$92, () => `\u25CF ChatGPT login: ${a.email}${a.plan ? ` (${a.plan})` : ""}`);
13814
+ effect((_$p) => setProp(_el$92, "fg", theme.success, _$p));
13815
+ return _el$92;
13567
13816
  })();
13568
13817
  }
13569
13818
  if (a.kind === "apikey")
13570
13819
  return (() => {
13571
- var _el$84 = createElement("text");
13572
- insertNode(_el$84, createTextNode(`\u25CF API key configured`));
13573
- effect((_$p) => setProp(_el$84, "fg", theme.success, _$p));
13574
- return _el$84;
13820
+ var _el$93 = createElement("text");
13821
+ insertNode(_el$93, createTextNode(`\u25CF API key configured`));
13822
+ effect((_$p) => setProp(_el$93, "fg", theme.success, _$p));
13823
+ return _el$93;
13575
13824
  })();
13576
13825
  return (() => {
13577
- var _el$86 = createElement("text");
13578
- insertNode(_el$86, createTextNode(`\u25CB Not logged in`));
13579
- effect((_$p) => setProp(_el$86, "fg", theme.textMuted, _$p));
13580
- return _el$86;
13826
+ var _el$95 = createElement("text");
13827
+ insertNode(_el$95, createTextNode(`\u25CB Not logged in`));
13828
+ effect((_$p) => setProp(_el$95, "fg", theme.textMuted, _$p));
13829
+ return _el$95;
13581
13830
  })();
13582
13831
  }, null);
13583
- insert(_el$81, createComponent2(Show, {
13832
+ insert(_el$90, createComponent2(Show, {
13584
13833
  get when() {
13585
13834
  return s().accountError;
13586
13835
  },
13587
13836
  children: (err) => (() => {
13588
- var _el$88 = createElement("text");
13589
- setProp(_el$88, "wrapMode", "word");
13590
- insert(_el$88, () => `! ${err()}`);
13591
- effect((_$p) => setProp(_el$88, "fg", theme.warning, _$p));
13592
- return _el$88;
13837
+ var _el$97 = createElement("text");
13838
+ setProp(_el$97, "wrapMode", "word");
13839
+ insert(_el$97, () => `! ${err()}`);
13840
+ effect((_$p) => setProp(_el$97, "fg", theme.warning, _$p));
13841
+ return _el$97;
13593
13842
  })()
13594
13843
  }), null);
13595
- effect((_$p) => setProp(_el$82, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13596
- return _el$81;
13844
+ effect((_$p) => setProp(_el$91, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13845
+ return _el$90;
13597
13846
  })()
13598
13847
  }), null);
13599
- insertNode(_el$70, _el$71);
13600
- setProp(_el$70, "flexDirection", "column");
13601
- setProp(_el$70, "gap", 0);
13602
- insertNode(_el$71, createTextNode(`copilot`));
13603
- insert(_el$70, createComponent2(Show, {
13848
+ insertNode(_el$79, _el$80);
13849
+ setProp(_el$79, "flexDirection", "column");
13850
+ setProp(_el$79, "gap", 0);
13851
+ insertNode(_el$80, createTextNode(`copilot`));
13852
+ insert(_el$79, createComponent2(Show, {
13604
13853
  get when() {
13605
13854
  return props.copilotStatus() === null;
13606
13855
  },
13607
13856
  get children() {
13608
- var _el$73 = createElement("text");
13609
- insertNode(_el$73, createTextNode(`Checking\u2026`));
13610
- effect((_$p) => setProp(_el$73, "fg", theme.textMuted, _$p));
13611
- return _el$73;
13857
+ var _el$82 = createElement("text");
13858
+ insertNode(_el$82, createTextNode(`Checking\u2026`));
13859
+ effect((_$p) => setProp(_el$82, "fg", theme.textMuted, _$p));
13860
+ return _el$82;
13612
13861
  }
13613
13862
  }), null);
13614
- insert(_el$70, createComponent2(Show, {
13863
+ insert(_el$79, createComponent2(Show, {
13615
13864
  get when() {
13616
13865
  return props.copilotStatus();
13617
13866
  },
13618
13867
  children: (s) => (() => {
13619
- var _el$89 = createElement("box"), _el$90 = createElement("text");
13620
- insertNode(_el$89, _el$90);
13621
- setProp(_el$89, "flexDirection", "column");
13622
- setProp(_el$89, "gap", 0);
13623
- setProp(_el$90, "wrapMode", "word");
13624
- insert(_el$90, () => binaryLine(s()));
13625
- insert(_el$89, () => {
13868
+ var _el$98 = createElement("box"), _el$99 = createElement("text");
13869
+ insertNode(_el$98, _el$99);
13870
+ setProp(_el$98, "flexDirection", "column");
13871
+ setProp(_el$98, "gap", 0);
13872
+ setProp(_el$99, "wrapMode", "word");
13873
+ insert(_el$99, () => binaryLine(s()));
13874
+ insert(_el$98, () => {
13626
13875
  const a = s().account;
13627
13876
  if (a.kind === "token")
13628
13877
  return (() => {
13629
- var _el$91 = createElement("text");
13630
- insert(_el$91, () => `\u25CF Token configured (${a.source})`);
13631
- effect((_$p) => setProp(_el$91, "fg", theme.success, _$p));
13632
- return _el$91;
13878
+ var _el$100 = createElement("text");
13879
+ insert(_el$100, () => `\u25CF Token configured (${a.source})`);
13880
+ effect((_$p) => setProp(_el$100, "fg", theme.success, _$p));
13881
+ return _el$100;
13633
13882
  })();
13634
13883
  if (a.kind === "oauth")
13635
13884
  return (() => {
13636
- var _el$92 = createElement("text");
13637
- insertNode(_el$92, createTextNode(`\u25CF Copilot login detected`));
13638
- effect((_$p) => setProp(_el$92, "fg", theme.success, _$p));
13639
- return _el$92;
13885
+ var _el$101 = createElement("text");
13886
+ insertNode(_el$101, createTextNode(`\u25CF Copilot login detected`));
13887
+ effect((_$p) => setProp(_el$101, "fg", theme.success, _$p));
13888
+ return _el$101;
13640
13889
  })();
13641
13890
  return (() => {
13642
- var _el$94 = createElement("text");
13643
- insertNode(_el$94, createTextNode(`\u25CB Not logged in`));
13644
- effect((_$p) => setProp(_el$94, "fg", theme.textMuted, _$p));
13645
- return _el$94;
13891
+ var _el$103 = createElement("text");
13892
+ insertNode(_el$103, createTextNode(`\u25CB Not logged in`));
13893
+ effect((_$p) => setProp(_el$103, "fg", theme.textMuted, _$p));
13894
+ return _el$103;
13646
13895
  })();
13647
13896
  }, null);
13648
- insert(_el$89, createComponent2(Show, {
13897
+ insert(_el$98, createComponent2(Show, {
13649
13898
  get when() {
13650
13899
  return s().accountError;
13651
13900
  },
13652
13901
  children: (err) => (() => {
13653
- var _el$96 = createElement("text");
13654
- setProp(_el$96, "wrapMode", "word");
13655
- insert(_el$96, () => `! ${err()}`);
13656
- effect((_$p) => setProp(_el$96, "fg", theme.warning, _$p));
13657
- return _el$96;
13902
+ var _el$105 = createElement("text");
13903
+ setProp(_el$105, "wrapMode", "word");
13904
+ insert(_el$105, () => `! ${err()}`);
13905
+ effect((_$p) => setProp(_el$105, "fg", theme.warning, _$p));
13906
+ return _el$105;
13658
13907
  })()
13659
13908
  }), null);
13660
- effect((_$p) => setProp(_el$90, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13661
- return _el$89;
13909
+ effect((_$p) => setProp(_el$99, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13910
+ return _el$98;
13662
13911
  })()
13663
13912
  }), null);
13664
13913
  effect((_p$) => {
13665
- var _v$45 = theme.text, _v$46 = TextAttributes5.BOLD, _v$47 = theme.textMuted, _v$48 = theme.text, _v$49 = TextAttributes5.BOLD, _v$50 = theme.text, _v$51 = TextAttributes5.BOLD, _v$52 = theme.text, _v$53 = TextAttributes5.BOLD;
13666
- _v$45 !== _p$.e && (_p$.e = setProp(_el$56, "fg", _v$45, _p$.e));
13667
- _v$46 !== _p$.t && (_p$.t = setProp(_el$56, "attributes", _v$46, _p$.t));
13668
- _v$47 !== _p$.a && (_p$.a = setProp(_el$58, "fg", _v$47, _p$.a));
13669
- _v$48 !== _p$.o && (_p$.o = setProp(_el$61, "fg", _v$48, _p$.o));
13670
- _v$49 !== _p$.i && (_p$.i = setProp(_el$61, "attributes", _v$49, _p$.i));
13671
- _v$50 !== _p$.n && (_p$.n = setProp(_el$66, "fg", _v$50, _p$.n));
13672
- _v$51 !== _p$.s && (_p$.s = setProp(_el$66, "attributes", _v$51, _p$.s));
13673
- _v$52 !== _p$.h && (_p$.h = setProp(_el$71, "fg", _v$52, _p$.h));
13674
- _v$53 !== _p$.r && (_p$.r = setProp(_el$71, "attributes", _v$53, _p$.r));
13914
+ var _v$53 = theme.text, _v$54 = TextAttributes5.BOLD, _v$55 = theme.textMuted, _v$56 = theme.text, _v$57 = TextAttributes5.BOLD, _v$58 = theme.text, _v$59 = TextAttributes5.BOLD, _v$60 = theme.text, _v$61 = TextAttributes5.BOLD;
13915
+ _v$53 !== _p$.e && (_p$.e = setProp(_el$65, "fg", _v$53, _p$.e));
13916
+ _v$54 !== _p$.t && (_p$.t = setProp(_el$65, "attributes", _v$54, _p$.t));
13917
+ _v$55 !== _p$.a && (_p$.a = setProp(_el$67, "fg", _v$55, _p$.a));
13918
+ _v$56 !== _p$.o && (_p$.o = setProp(_el$70, "fg", _v$56, _p$.o));
13919
+ _v$57 !== _p$.i && (_p$.i = setProp(_el$70, "attributes", _v$57, _p$.i));
13920
+ _v$58 !== _p$.n && (_p$.n = setProp(_el$75, "fg", _v$58, _p$.n));
13921
+ _v$59 !== _p$.s && (_p$.s = setProp(_el$75, "attributes", _v$59, _p$.s));
13922
+ _v$60 !== _p$.h && (_p$.h = setProp(_el$80, "fg", _v$60, _p$.h));
13923
+ _v$61 !== _p$.r && (_p$.r = setProp(_el$80, "attributes", _v$61, _p$.r));
13675
13924
  return _p$;
13676
13925
  }, {
13677
13926
  e: undefined,
@@ -13684,7 +13933,7 @@ function AccountsSettingsSection(props) {
13684
13933
  h: undefined,
13685
13934
  r: undefined
13686
13935
  });
13687
- return _el$55;
13936
+ return _el$64;
13688
13937
  })();
13689
13938
  }
13690
13939
  function DevSettingsSection(props) {
@@ -13694,58 +13943,58 @@ function DevSettingsSection(props) {
13694
13943
  const resetIsCursor = () => props.level() === "body" && props.bodyRow() === 0;
13695
13944
  const restartIsCursor = () => props.level() === "body" && props.bodyRow() === 1;
13696
13945
  return (() => {
13697
- var _el$97 = createElement("box"), _el$98 = createElement("text"), _el$100 = createElement("text"), _el$102 = createElement("box"), _el$103 = createElement("text");
13698
- insertNode(_el$97, _el$98);
13699
- insertNode(_el$97, _el$100);
13700
- insertNode(_el$97, _el$102);
13701
- setProp(_el$97, "flexDirection", "column");
13702
- setProp(_el$97, "gap", 1);
13703
- insertNode(_el$98, createTextNode(`Reset UI state`));
13704
- insertNode(_el$100, createTextNode(`Clears ~/.config/kobe/state.json and ~/.kobe/tasks.json, then quits kobe \u2014 relaunch to start fresh. Working session / Archive lists, pane sizes, theme, model picks all reset. Worktrees on disk and Claude Code session history are not touched.`));
13705
- setProp(_el$100, "wrapMode", "word");
13706
- insertNode(_el$102, _el$103);
13707
- setProp(_el$102, "flexDirection", "row");
13708
- setProp(_el$102, "paddingLeft", 1);
13709
- setProp(_el$102, "paddingRight", 1);
13710
- setProp(_el$102, "onMouseUp", () => {
13946
+ var _el$106 = createElement("box"), _el$107 = createElement("text"), _el$109 = createElement("text"), _el$111 = createElement("box"), _el$112 = createElement("text");
13947
+ insertNode(_el$106, _el$107);
13948
+ insertNode(_el$106, _el$109);
13949
+ insertNode(_el$106, _el$111);
13950
+ setProp(_el$106, "flexDirection", "column");
13951
+ setProp(_el$106, "gap", 1);
13952
+ insertNode(_el$107, createTextNode(`Reset UI state`));
13953
+ insertNode(_el$109, createTextNode(`Clears ~/.config/kobe/state.json and ~/.kobe/tasks.json, then quits kobe \u2014 relaunch to start fresh. Working session / Archive lists, pane sizes, theme, model picks all reset. Worktrees on disk and Claude Code session history are not touched.`));
13954
+ setProp(_el$109, "wrapMode", "word");
13955
+ insertNode(_el$111, _el$112);
13956
+ setProp(_el$111, "flexDirection", "row");
13957
+ setProp(_el$111, "paddingLeft", 1);
13958
+ setProp(_el$111, "paddingRight", 1);
13959
+ setProp(_el$111, "onMouseUp", () => {
13711
13960
  props.setLevel("body");
13712
13961
  props.setBodyRow(0);
13713
13962
  props.confirmReset();
13714
13963
  });
13715
- insertNode(_el$103, createTextNode(`[enter] Reset`));
13716
- insert(_el$97, createComponent2(Show, {
13964
+ insertNode(_el$112, createTextNode(`[enter] Reset`));
13965
+ insert(_el$106, createComponent2(Show, {
13717
13966
  get when() {
13718
13967
  return props.hasDaemon;
13719
13968
  },
13720
13969
  get children() {
13721
- var _el$105 = createElement("box"), _el$106 = createElement("text"), _el$108 = createElement("text"), _el$110 = createElement("box"), _el$111 = createElement("text");
13722
- insertNode(_el$105, _el$106);
13723
- insertNode(_el$105, _el$108);
13724
- insertNode(_el$105, _el$110);
13725
- setProp(_el$105, "flexDirection", "column");
13726
- setProp(_el$105, "gap", 0);
13727
- setProp(_el$105, "paddingTop", 1);
13728
- insertNode(_el$106, createTextNode(`Restart backend`));
13729
- insertNode(_el$108, createTextNode(`Stops the kobe daemon and quits this kobe window so the next launch spawns a fresh daemon \u2014 picks up daemon / orchestrator / engine edits without a process kill. Other attached kobe windows will lose their connection too.`));
13730
- setProp(_el$108, "wrapMode", "word");
13731
- insertNode(_el$110, _el$111);
13732
- setProp(_el$110, "flexDirection", "row");
13733
- setProp(_el$110, "paddingLeft", 1);
13734
- setProp(_el$110, "paddingRight", 1);
13735
- setProp(_el$110, "onMouseUp", () => {
13970
+ var _el$114 = createElement("box"), _el$115 = createElement("text"), _el$117 = createElement("text"), _el$119 = createElement("box"), _el$120 = createElement("text");
13971
+ insertNode(_el$114, _el$115);
13972
+ insertNode(_el$114, _el$117);
13973
+ insertNode(_el$114, _el$119);
13974
+ setProp(_el$114, "flexDirection", "column");
13975
+ setProp(_el$114, "gap", 0);
13976
+ setProp(_el$114, "paddingTop", 1);
13977
+ insertNode(_el$115, createTextNode(`Restart backend`));
13978
+ insertNode(_el$117, createTextNode(`Stops the kobe daemon and quits this kobe window so the next launch spawns a fresh daemon \u2014 picks up daemon / orchestrator / engine edits without a process kill. Other attached kobe windows will lose their connection too.`));
13979
+ setProp(_el$117, "wrapMode", "word");
13980
+ insertNode(_el$119, _el$120);
13981
+ setProp(_el$119, "flexDirection", "row");
13982
+ setProp(_el$119, "paddingLeft", 1);
13983
+ setProp(_el$119, "paddingRight", 1);
13984
+ setProp(_el$119, "onMouseUp", () => {
13736
13985
  props.setLevel("body");
13737
13986
  props.setBodyRow(1);
13738
13987
  props.confirmRestartDaemon();
13739
13988
  });
13740
- insertNode(_el$111, createTextNode(`[enter] Restart`));
13989
+ insertNode(_el$120, createTextNode(`[enter] Restart`));
13741
13990
  effect((_p$) => {
13742
- var _v$54 = theme.text, _v$55 = TextAttributes5.BOLD, _v$56 = theme.textMuted, _v$57 = restartIsCursor() ? theme.primary : theme.backgroundElement, _v$58 = restartIsCursor() ? theme.selectedListItemText : theme.accent, _v$59 = TextAttributes5.BOLD;
13743
- _v$54 !== _p$.e && (_p$.e = setProp(_el$106, "fg", _v$54, _p$.e));
13744
- _v$55 !== _p$.t && (_p$.t = setProp(_el$106, "attributes", _v$55, _p$.t));
13745
- _v$56 !== _p$.a && (_p$.a = setProp(_el$108, "fg", _v$56, _p$.a));
13746
- _v$57 !== _p$.o && (_p$.o = setProp(_el$110, "backgroundColor", _v$57, _p$.o));
13747
- _v$58 !== _p$.i && (_p$.i = setProp(_el$111, "fg", _v$58, _p$.i));
13748
- _v$59 !== _p$.n && (_p$.n = setProp(_el$111, "attributes", _v$59, _p$.n));
13991
+ var _v$62 = theme.text, _v$63 = TextAttributes5.BOLD, _v$64 = theme.textMuted, _v$65 = restartIsCursor() ? theme.primary : theme.backgroundElement, _v$66 = restartIsCursor() ? theme.selectedListItemText : theme.accent, _v$67 = TextAttributes5.BOLD;
13992
+ _v$62 !== _p$.e && (_p$.e = setProp(_el$115, "fg", _v$62, _p$.e));
13993
+ _v$63 !== _p$.t && (_p$.t = setProp(_el$115, "attributes", _v$63, _p$.t));
13994
+ _v$64 !== _p$.a && (_p$.a = setProp(_el$117, "fg", _v$64, _p$.a));
13995
+ _v$65 !== _p$.o && (_p$.o = setProp(_el$119, "backgroundColor", _v$65, _p$.o));
13996
+ _v$66 !== _p$.i && (_p$.i = setProp(_el$120, "fg", _v$66, _p$.i));
13997
+ _v$67 !== _p$.n && (_p$.n = setProp(_el$120, "attributes", _v$67, _p$.n));
13749
13998
  return _p$;
13750
13999
  }, {
13751
14000
  e: undefined,
@@ -13755,17 +14004,17 @@ function DevSettingsSection(props) {
13755
14004
  i: undefined,
13756
14005
  n: undefined
13757
14006
  });
13758
- return _el$105;
14007
+ return _el$114;
13759
14008
  }
13760
14009
  }), null);
13761
14010
  effect((_p$) => {
13762
- var _v$60 = theme.text, _v$61 = TextAttributes5.BOLD, _v$62 = theme.textMuted, _v$63 = resetIsCursor() ? theme.primary : theme.backgroundElement, _v$64 = resetIsCursor() ? theme.selectedListItemText : theme.warning, _v$65 = TextAttributes5.BOLD;
13763
- _v$60 !== _p$.e && (_p$.e = setProp(_el$98, "fg", _v$60, _p$.e));
13764
- _v$61 !== _p$.t && (_p$.t = setProp(_el$98, "attributes", _v$61, _p$.t));
13765
- _v$62 !== _p$.a && (_p$.a = setProp(_el$100, "fg", _v$62, _p$.a));
13766
- _v$63 !== _p$.o && (_p$.o = setProp(_el$102, "backgroundColor", _v$63, _p$.o));
13767
- _v$64 !== _p$.i && (_p$.i = setProp(_el$103, "fg", _v$64, _p$.i));
13768
- _v$65 !== _p$.n && (_p$.n = setProp(_el$103, "attributes", _v$65, _p$.n));
14011
+ var _v$68 = theme.text, _v$69 = TextAttributes5.BOLD, _v$70 = theme.textMuted, _v$71 = resetIsCursor() ? theme.primary : theme.backgroundElement, _v$72 = resetIsCursor() ? theme.selectedListItemText : theme.warning, _v$73 = TextAttributes5.BOLD;
14012
+ _v$68 !== _p$.e && (_p$.e = setProp(_el$107, "fg", _v$68, _p$.e));
14013
+ _v$69 !== _p$.t && (_p$.t = setProp(_el$107, "attributes", _v$69, _p$.t));
14014
+ _v$70 !== _p$.a && (_p$.a = setProp(_el$109, "fg", _v$70, _p$.a));
14015
+ _v$71 !== _p$.o && (_p$.o = setProp(_el$111, "backgroundColor", _v$71, _p$.o));
14016
+ _v$72 !== _p$.i && (_p$.i = setProp(_el$112, "fg", _v$72, _p$.i));
14017
+ _v$73 !== _p$.n && (_p$.n = setProp(_el$112, "attributes", _v$73, _p$.n));
13769
14018
  return _p$;
13770
14019
  }, {
13771
14020
  e: undefined,
@@ -13775,7 +14024,7 @@ function DevSettingsSection(props) {
13775
14024
  i: undefined,
13776
14025
  n: undefined
13777
14026
  });
13778
- return _el$97;
14027
+ return _el$106;
13779
14028
  })();
13780
14029
  }
13781
14030
  var init_sections = __esm(() => {
@@ -13844,9 +14093,40 @@ function SettingsDialog(props) {
13844
14093
  function isSurfaceTaskpanelRow() {
13845
14094
  return section() === "general" && bodyRow() === surfaceTaskpanelRowIndex(themeNames().length, FOCUS_ACCENT_SLOTS.length);
13846
14095
  }
14096
+ function isEditorKindRow() {
14097
+ return section() === "general" && bodyRow() === editorKindRowIndex(themeNames().length, FOCUS_ACCENT_SLOTS.length);
14098
+ }
14099
+ function isEditorCustomRow() {
14100
+ return section() === "general" && bodyRow() === editorCustomRowIndex(themeNames().length, FOCUS_ACCENT_SLOTS.length);
14101
+ }
13847
14102
  function settingsSurface() {
13848
14103
  return normalizeSettingsSurface(props.kv.get(SETTINGS_SURFACE_KEY, DEFAULT_SETTINGS_SURFACE));
13849
14104
  }
14105
+ function selectTheme(name) {
14106
+ if (themeCtx.selected === name)
14107
+ return;
14108
+ if (!themeCtx.set(name))
14109
+ return;
14110
+ props.kv.set("activeTheme", name);
14111
+ props.onVisualPrefsChange?.();
14112
+ }
14113
+ function setTransparentBackground(next) {
14114
+ if (themeCtx.transparentBackground === next)
14115
+ return;
14116
+ themeCtx.setTransparentBackground(next);
14117
+ props.kv.set("transparentBackground", next);
14118
+ props.onVisualPrefsChange?.();
14119
+ }
14120
+ function toggleTransparent() {
14121
+ setTransparentBackground(!themeCtx.transparentBackground);
14122
+ }
14123
+ function selectFocusAccent(slot) {
14124
+ if (themeCtx.focusAccent === slot)
14125
+ return;
14126
+ themeCtx.setFocusAccent(slot);
14127
+ props.kv.set("focusAccent", slot);
14128
+ props.onVisualPrefsChange?.();
14129
+ }
13850
14130
  function selectSurface(surface) {
13851
14131
  props.kv.set(SETTINGS_SURFACE_KEY, surface);
13852
14132
  }
@@ -13880,6 +14160,30 @@ function SettingsDialog(props) {
13880
14160
  return;
13881
14161
  props.kv.set(engineCommandKey(vendor), next.trim());
13882
14162
  }
14163
+ function editorKind() {
14164
+ return normalizeEditorKind(props.kv.get(EDITOR_KIND_KEY, DEFAULT_EDITOR_KIND));
14165
+ }
14166
+ function cycleEditorKind() {
14167
+ const i = EDITOR_KINDS.indexOf(editorKind());
14168
+ const next = EDITOR_KINDS[(i + 1) % EDITOR_KINDS.length];
14169
+ if (next)
14170
+ props.kv.set(EDITOR_KIND_KEY, next);
14171
+ }
14172
+ function editorCustomCommand() {
14173
+ const v = props.kv.get(EDITOR_CUSTOM_KEY, "");
14174
+ return typeof v === "string" ? v : "";
14175
+ }
14176
+ async function editEditorCustom() {
14177
+ const next = await RenameTaskDialog.show(dialog, editorCustomCommand(), {
14178
+ dialogTitle: "Custom editor command (use {file} for the path)"
14179
+ });
14180
+ if (next === undefined)
14181
+ return;
14182
+ const cmd = next.trim();
14183
+ props.kv.set(EDITOR_CUSTOM_KEY, cmd);
14184
+ if (cmd)
14185
+ props.kv.set(EDITOR_KIND_KEY, "custom");
14186
+ }
13883
14187
  function enterBody() {
13884
14188
  if (level() !== "sidebar" || bodyRowCount2() === 0)
13885
14189
  return;
@@ -13916,14 +14220,14 @@ function SettingsDialog(props) {
13916
14220
  function activateBodyRow() {
13917
14221
  if (section() === "general") {
13918
14222
  if (isTransparentRow()) {
13919
- themeCtx.setTransparentBackground(!themeCtx.transparentBackground);
14223
+ toggleTransparent();
13920
14224
  return;
13921
14225
  }
13922
14226
  const focusIdx = currentFocusAccentRow();
13923
14227
  if (focusIdx !== null) {
13924
14228
  const slot = FOCUS_ACCENT_SLOTS[focusIdx];
13925
14229
  if (slot)
13926
- themeCtx.setFocusAccent(slot);
14230
+ selectFocusAccent(slot);
13927
14231
  return;
13928
14232
  }
13929
14233
  if (isToastRow()) {
@@ -13942,9 +14246,17 @@ function SettingsDialog(props) {
13942
14246
  selectSurface("taskpanel");
13943
14247
  return;
13944
14248
  }
14249
+ if (isEditorKindRow()) {
14250
+ cycleEditorKind();
14251
+ return;
14252
+ }
14253
+ if (isEditorCustomRow()) {
14254
+ editEditorCustom();
14255
+ return;
14256
+ }
13945
14257
  const name = themeNames()[bodyRow()];
13946
14258
  if (name)
13947
- themeCtx.set(name);
14259
+ selectTheme(name);
13948
14260
  return;
13949
14261
  }
13950
14262
  if (section() === "engines") {
@@ -13961,6 +14273,7 @@ function SettingsDialog(props) {
13961
14273
  }
13962
14274
  }
13963
14275
  useBindings(() => ({
14276
+ enabled: !props.standalone || dialog.stack.length === 0,
13964
14277
  bindings: [{
13965
14278
  key: "down",
13966
14279
  cmd: () => moveCursor(1)
@@ -13999,7 +14312,7 @@ function SettingsDialog(props) {
13999
14312
  }
14000
14313
  }, {
14001
14314
  key: "t",
14002
- cmd: () => themeCtx.setTransparentBackground(!themeCtx.transparentBackground)
14315
+ cmd: toggleTransparent
14003
14316
  }]
14004
14317
  }));
14005
14318
  return (() => {
@@ -14042,12 +14355,19 @@ function SettingsDialog(props) {
14042
14355
  setBodyRow,
14043
14356
  themeNames,
14044
14357
  setThemeCursor,
14358
+ selectTheme,
14359
+ toggleTransparent,
14360
+ selectFocusAccent,
14045
14361
  toastEnabled,
14046
14362
  soundEnabled,
14047
14363
  toggleToast,
14048
14364
  toggleSound,
14049
14365
  settingsSurface,
14050
- selectSurface
14366
+ selectSurface,
14367
+ editorKind,
14368
+ cycleEditorKind,
14369
+ editorCustomCommand,
14370
+ editEditorCustom: () => void editEditorCustom()
14051
14371
  });
14052
14372
  }
14053
14373
  }), null);
@@ -14129,6 +14449,7 @@ var init_settings_dialog = __esm(() => {
14129
14449
  init_interactive_command();
14130
14450
  init_vendor();
14131
14451
  init_theme2();
14452
+ init_editor_prefs();
14132
14453
  init_keymap();
14133
14454
  init_dialog();
14134
14455
  init_rename_task_dialog();
@@ -14136,12 +14457,20 @@ var init_settings_dialog = __esm(() => {
14136
14457
  init_model();
14137
14458
  init_sections();
14138
14459
  SettingsDialog.show = (dialog, kv, orchestrator) => {
14460
+ let visualPrefsChanged = false;
14139
14461
  return new Promise((resolve6) => {
14140
14462
  dialog.replace(() => createComponent2(SettingsDialog, {
14141
14463
  kv,
14142
14464
  orchestrator,
14143
- onClose: () => resolve6()
14144
- }), () => resolve6());
14465
+ onVisualPrefsChange: () => {
14466
+ visualPrefsChanged = true;
14467
+ },
14468
+ onClose: () => resolve6({
14469
+ visualPrefsChanged
14470
+ })
14471
+ }), () => resolve6({
14472
+ visualPrefsChanged
14473
+ }));
14145
14474
  });
14146
14475
  };
14147
14476
  });
@@ -14234,22 +14563,27 @@ var init_kv = __esm(() => {
14234
14563
  init: () => {
14235
14564
  const [store2, setStore2] = createStore(loadInitial());
14236
14565
  let writeTimer = null;
14566
+ function writeNow(label) {
14567
+ const statePath2 = kvStatePath();
14568
+ try {
14569
+ mkdirSync4(dirname5(statePath2), {
14570
+ recursive: true
14571
+ });
14572
+ const tmp = `${statePath2}.tmp`;
14573
+ writeFileSync3(tmp, JSON.stringify(store2, null, 2), "utf8");
14574
+ renameSync2(tmp, statePath2);
14575
+ return true;
14576
+ } catch (err) {
14577
+ console.error(`[kobe] kv ${label} failed:`, err);
14578
+ return false;
14579
+ }
14580
+ }
14237
14581
  function scheduleWrite() {
14238
14582
  if (writeTimer)
14239
14583
  clearTimeout(writeTimer);
14240
14584
  writeTimer = setTimeout(() => {
14241
14585
  writeTimer = null;
14242
- const statePath2 = kvStatePath();
14243
- try {
14244
- mkdirSync4(dirname5(statePath2), {
14245
- recursive: true
14246
- });
14247
- const tmp = `${statePath2}.tmp`;
14248
- writeFileSync3(tmp, JSON.stringify(store2, null, 2), "utf8");
14249
- renameSync2(tmp, statePath2);
14250
- } catch (err) {
14251
- console.error("[kobe] kv write failed:", err);
14252
- }
14586
+ writeNow("write");
14253
14587
  }, WRITE_DEBOUNCE_MS);
14254
14588
  }
14255
14589
  const result = {
@@ -14271,6 +14605,13 @@ var init_kv = __esm(() => {
14271
14605
  setStore2(key, value);
14272
14606
  scheduleWrite();
14273
14607
  },
14608
+ flush() {
14609
+ if (writeTimer) {
14610
+ clearTimeout(writeTimer);
14611
+ writeTimer = null;
14612
+ }
14613
+ return writeNow("flush");
14614
+ },
14274
14615
  clear() {
14275
14616
  for (const k of Object.keys(store2))
14276
14617
  setStore2(k, undefined);
@@ -14422,36 +14763,6 @@ var init_worktree_opener = __esm(() => {
14422
14763
  ];
14423
14764
  });
14424
14765
 
14425
- // src/tui/panes/sidebar/git-head.ts
14426
- import { spawnSync as spawnSync8 } from "child_process";
14427
- function readCurrentBranch(repo) {
14428
- if (!repo)
14429
- return "";
14430
- try {
14431
- const out = spawnSync8("git", ["symbolic-ref", "--short", "HEAD"], {
14432
- cwd: repo,
14433
- encoding: "utf8",
14434
- stdio: ["ignore", "pipe", "pipe"]
14435
- });
14436
- if (out.status === 0 && out.stdout) {
14437
- const name = out.stdout.trim();
14438
- if (name && name !== "HEAD")
14439
- return name;
14440
- }
14441
- const head = spawnSync8("git", ["rev-parse", "--verify", "HEAD"], {
14442
- cwd: repo,
14443
- encoding: "utf8",
14444
- stdio: ["ignore", "pipe", "pipe"]
14445
- });
14446
- if (head.status === 0)
14447
- return "(detached)";
14448
- return "";
14449
- } catch {
14450
- return "";
14451
- }
14452
- }
14453
- var init_git_head = () => {};
14454
-
14455
14766
  // src/tui/panes/sidebar/fuzzy.ts
14456
14767
  function fuzzyMatch(query, haystack) {
14457
14768
  if (!query)
@@ -15079,8 +15390,16 @@ var init_keybindings = __esm(() => {
15079
15390
  scope: "files",
15080
15391
  keys: ["return"],
15081
15392
  category: "Files",
15082
- description: "Open file",
15083
- hint: { keys: "enter", label: "open" }
15393
+ description: "Open file read-only preview",
15394
+ hint: { keys: "enter", label: "preview" }
15395
+ },
15396
+ {
15397
+ id: "files.edit",
15398
+ scope: "files",
15399
+ keys: ["e"],
15400
+ category: "Files",
15401
+ description: "Open file in the configured editor (vim / nano / custom)",
15402
+ hint: { keys: "e", label: "edit" }
15084
15403
  },
15085
15404
  {
15086
15405
  id: "files.tab",
@@ -15362,6 +15681,13 @@ function truncatePathTail(path11, max) {
15362
15681
  return path11;
15363
15682
  return `\u2026${path11.slice(path11.length - Math.max(0, max - 1))}`;
15364
15683
  }
15684
+ function abbrevHome(path11) {
15685
+ const home = process.env.HOME;
15686
+ if (home && (path11 === home || path11.startsWith(`${home}/`))) {
15687
+ return `~${path11.slice(home.length)}`;
15688
+ }
15689
+ return path11;
15690
+ }
15365
15691
  function Sidebar(props) {
15366
15692
  const {
15367
15693
  theme
@@ -15415,14 +15741,15 @@ function Sidebar(props) {
15415
15741
  onCleanup(() => clearInterval(spinnerInterval));
15416
15742
  const rows = createMemo(() => buildRows(props.tasks(), view(), searchMode() ? searchQuery() : ""));
15417
15743
  const flatIds = createMemo(() => flattenIds(rows()));
15744
+ const firstTaskFlatIndex = createMemo(() => {
15745
+ const r = rows();
15746
+ const idx = r.findIndex((row) => row.task.kind !== "main");
15747
+ return idx < 0 ? -1 : r[idx].flatIndex;
15748
+ });
15418
15749
  const totalRows = createMemo(() => flattenIds(buildRows(props.tasks(), view(), "")).length);
15419
15750
  const effectiveWidth = () => props.width ? props.width() : SIDEBAR_WIDTH;
15420
- const showChangesCol = createMemo(() => effectiveWidth() >= SHOW_CHANGES_MIN_WIDTH);
15421
- const showBranchCol = createMemo(() => effectiveWidth() >= SHOW_BRANCH_MIN_WIDTH);
15422
- const titleBudget = createMemo(() => {
15423
- const reserved = 9 + (showChangesCol() ? CHANGES_COLUMN_WIDTH + 1 : 0) + (showBranchCol() ? BRANCH_LABEL_MAX + 2 : 0);
15424
- return Math.max(4, effectiveWidth() - reserved);
15425
- });
15751
+ const titleBudget = createMemo(() => Math.max(6, effectiveWidth() - 9));
15752
+ const subtitleBudget = createMemo(() => Math.max(6, effectiveWidth() - 16));
15426
15753
  const dims = useTerminalDimensions();
15427
15754
  const [hover, setHover] = createSignal(null);
15428
15755
  const [cursorIndex, setCursorIndex] = createSignal(-1);
@@ -15480,80 +15807,127 @@ function Sidebar(props) {
15480
15807
  onSearchEnter: () => enterSearch(),
15481
15808
  onSearchExit: (select) => exitSearch(select)
15482
15809
  });
15483
- return (() => {
15484
- var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$17 = createElement("box"), _el$18 = createElement("scrollbox"), _el$19 = createElement("box"), _el$22 = createElement("box"), _el$23 = createElement("text");
15810
+ const SectionHeader = (p) => (() => {
15811
+ var _el$ = createElement("box"), _el$2 = createElement("text"), _el$3 = createElement("text");
15485
15812
  insertNode(_el$, _el$2);
15486
- insertNode(_el$, _el$17);
15487
- insertNode(_el$, _el$18);
15488
- insertNode(_el$, _el$22);
15489
- setProp(_el$, "flexShrink", 0);
15490
- setProp(_el$, "flexDirection", "column");
15491
- setProp(_el$, "paddingTop", 1);
15492
- setProp(_el$, "paddingBottom", 1);
15493
- setProp(_el$, "paddingLeft", 2);
15494
- setProp(_el$, "paddingRight", 2);
15495
- insertNode(_el$2, _el$3);
15496
- insertNode(_el$2, _el$5);
15497
- setProp(_el$2, "flexDirection", "row");
15498
- setProp(_el$2, "gap", 1);
15499
- setProp(_el$2, "paddingBottom", 1);
15500
- setProp(_el$2, "paddingLeft", 1);
15501
- insertNode(_el$3, createTextNode(`h`));
15813
+ insertNode(_el$, _el$3);
15814
+ setProp(_el$, "flexDirection", "row");
15815
+ setProp(_el$, "gap", 1);
15816
+ setProp(_el$, "paddingLeft", 1);
15817
+ setProp(_el$, "paddingRight", 1);
15818
+ setProp(_el$2, "wrapMode", "none");
15819
+ insert(_el$2, () => p.label);
15502
15820
  setProp(_el$3, "wrapMode", "none");
15503
- insertNode(_el$5, createTextNode(`TASKS`));
15504
- setProp(_el$5, "wrapMode", "none");
15505
- insert(_el$, createComponent2(Show, {
15821
+ insert(_el$3, () => "\u2500".repeat(Math.max(2, effectiveWidth() - 9 - p.label.length)));
15822
+ effect((_p$) => {
15823
+ var _v$ = p.topPad ? 1 : 0, _v$2 = theme.textMuted, _v$3 = TextAttributes8.BOLD, _v$4 = theme.border;
15824
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "paddingTop", _v$, _p$.e));
15825
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "fg", _v$2, _p$.t));
15826
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$2, "attributes", _v$3, _p$.a));
15827
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$3, "fg", _v$4, _p$.o));
15828
+ return _p$;
15829
+ }, {
15830
+ e: undefined,
15831
+ t: undefined,
15832
+ a: undefined,
15833
+ o: undefined
15834
+ });
15835
+ return _el$;
15836
+ })();
15837
+ return (() => {
15838
+ var _el$4 = createElement("box"), _el$5 = createElement("box"), _el$6 = createElement("text"), _el$18 = createElement("box"), _el$19 = createElement("scrollbox"), _el$20 = createElement("box"), _el$23 = createElement("box"), _el$24 = createElement("text");
15839
+ insertNode(_el$4, _el$5);
15840
+ insertNode(_el$4, _el$18);
15841
+ insertNode(_el$4, _el$19);
15842
+ insertNode(_el$4, _el$23);
15843
+ setProp(_el$4, "flexShrink", 0);
15844
+ setProp(_el$4, "flexDirection", "column");
15845
+ setProp(_el$4, "paddingTop", 1);
15846
+ setProp(_el$4, "paddingBottom", 1);
15847
+ setProp(_el$4, "paddingLeft", 0);
15848
+ setProp(_el$4, "paddingRight", 0);
15849
+ insertNode(_el$5, _el$6);
15850
+ setProp(_el$5, "flexDirection", "row");
15851
+ setProp(_el$5, "justifyContent", "space-between");
15852
+ setProp(_el$5, "gap", 1);
15853
+ setProp(_el$5, "paddingBottom", 1);
15854
+ setProp(_el$5, "paddingLeft", 1);
15855
+ setProp(_el$5, "paddingRight", 1);
15856
+ insertNode(_el$6, createTextNode(`KOBE`));
15857
+ setProp(_el$6, "wrapMode", "none");
15858
+ insert(_el$5, createComponent2(Show, {
15859
+ get when() {
15860
+ return props.headerStatus?.();
15861
+ },
15862
+ children: (status) => (() => {
15863
+ var _el$26 = createElement("text");
15864
+ setProp(_el$26, "wrapMode", "none");
15865
+ setProp(_el$26, "onMouseUp", () => props.onHeaderStatusClick?.());
15866
+ insert(_el$26, () => status().label);
15867
+ effect((_p$) => {
15868
+ var _v$11 = status().emphasize ? theme.warning : theme.textMuted, _v$12 = status().emphasize ? TextAttributes8.BOLD : TextAttributes8.DIM;
15869
+ _v$11 !== _p$.e && (_p$.e = setProp(_el$26, "fg", _v$11, _p$.e));
15870
+ _v$12 !== _p$.t && (_p$.t = setProp(_el$26, "attributes", _v$12, _p$.t));
15871
+ return _p$;
15872
+ }, {
15873
+ e: undefined,
15874
+ t: undefined
15875
+ });
15876
+ return _el$26;
15877
+ })()
15878
+ }), null);
15879
+ insert(_el$4, createComponent2(Show, {
15506
15880
  get when() {
15507
15881
  return searchMode();
15508
15882
  },
15509
15883
  get children() {
15510
- var _el$7 = createElement("box"), _el$8 = createElement("text"), _el$0 = createElement("text"), _el$1 = createElement("text");
15511
- insertNode(_el$7, _el$8);
15512
- insertNode(_el$7, _el$0);
15513
- insertNode(_el$7, _el$1);
15514
- setProp(_el$7, "flexDirection", "row");
15515
- setProp(_el$7, "gap", 0);
15516
- setProp(_el$7, "paddingBottom", 1);
15517
- setProp(_el$7, "paddingLeft", 1);
15518
- insertNode(_el$8, createTextNode(`/`));
15519
- setProp(_el$8, "wrapMode", "none");
15520
- setProp(_el$0, "wrapMode", "none");
15521
- insert(_el$0, searchQuery);
15522
- insertNode(_el$1, createTextNode(`\u2588`));
15884
+ var _el$8 = createElement("box"), _el$9 = createElement("text"), _el$1 = createElement("text"), _el$10 = createElement("text");
15885
+ insertNode(_el$8, _el$9);
15886
+ insertNode(_el$8, _el$1);
15887
+ insertNode(_el$8, _el$10);
15888
+ setProp(_el$8, "flexDirection", "row");
15889
+ setProp(_el$8, "gap", 0);
15890
+ setProp(_el$8, "paddingBottom", 1);
15891
+ setProp(_el$8, "paddingLeft", 1);
15892
+ insertNode(_el$9, createTextNode(`/`));
15893
+ setProp(_el$9, "wrapMode", "none");
15523
15894
  setProp(_el$1, "wrapMode", "none");
15524
- insert(_el$7, createComponent2(Show, {
15895
+ insert(_el$1, searchQuery);
15896
+ insertNode(_el$10, createTextNode(`\u2588`));
15897
+ setProp(_el$10, "wrapMode", "none");
15898
+ insert(_el$8, createComponent2(Show, {
15525
15899
  get when() {
15526
15900
  return searchQuery().length === 0;
15527
15901
  },
15528
15902
  get children() {
15529
- var _el$11 = createElement("text"), _el$12 = createTextNode(` fuzzy filter`);
15530
- insertNode(_el$11, _el$12);
15531
- setProp(_el$11, "wrapMode", "none");
15532
- effect((_$p) => setProp(_el$11, "fg", theme.textMuted, _$p));
15533
- return _el$11;
15903
+ var _el$12 = createElement("text"), _el$13 = createTextNode(` fuzzy filter`);
15904
+ insertNode(_el$12, _el$13);
15905
+ setProp(_el$12, "wrapMode", "none");
15906
+ effect((_$p) => setProp(_el$12, "fg", theme.textMuted, _$p));
15907
+ return _el$12;
15534
15908
  }
15535
15909
  }), null);
15536
- insert(_el$7, createComponent2(Show, {
15910
+ insert(_el$8, createComponent2(Show, {
15537
15911
  get when() {
15538
15912
  return searchQuery().length > 0;
15539
15913
  },
15540
15914
  get children() {
15541
- var _el$14 = createElement("text"), _el$15 = createTextNode(` `), _el$16 = createTextNode(`/`);
15542
- insertNode(_el$14, _el$15);
15543
- insertNode(_el$14, _el$16);
15544
- setProp(_el$14, "wrapMode", "none");
15545
- insert(_el$14, () => flatIds().length, _el$16);
15546
- insert(_el$14, totalRows, null);
15547
- effect((_$p) => setProp(_el$14, "fg", theme.textMuted, _$p));
15548
- return _el$14;
15915
+ var _el$15 = createElement("text"), _el$16 = createTextNode(` `), _el$17 = createTextNode(`/`);
15916
+ insertNode(_el$15, _el$16);
15917
+ insertNode(_el$15, _el$17);
15918
+ setProp(_el$15, "wrapMode", "none");
15919
+ insert(_el$15, () => flatIds().length, _el$17);
15920
+ insert(_el$15, totalRows, null);
15921
+ effect((_$p) => setProp(_el$15, "fg", theme.textMuted, _$p));
15922
+ return _el$15;
15549
15923
  }
15550
15924
  }), null);
15551
15925
  effect((_p$) => {
15552
- var { info: _v$, text: _v$2, info: _v$3 } = theme, _v$4 = TextAttributes8.BLINK;
15553
- _v$ !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$, _p$.e));
15554
- _v$2 !== _p$.t && (_p$.t = setProp(_el$0, "fg", _v$2, _p$.t));
15555
- _v$3 !== _p$.a && (_p$.a = setProp(_el$1, "fg", _v$3, _p$.a));
15556
- _v$4 !== _p$.o && (_p$.o = setProp(_el$1, "attributes", _v$4, _p$.o));
15926
+ var { info: _v$5, text: _v$6, info: _v$7 } = theme, _v$8 = TextAttributes8.BLINK;
15927
+ _v$5 !== _p$.e && (_p$.e = setProp(_el$9, "fg", _v$5, _p$.e));
15928
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$1, "fg", _v$6, _p$.t));
15929
+ _v$7 !== _p$.a && (_p$.a = setProp(_el$10, "fg", _v$7, _p$.a));
15930
+ _v$8 !== _p$.o && (_p$.o = setProp(_el$10, "attributes", _v$8, _p$.o));
15557
15931
  return _p$;
15558
15932
  }, {
15559
15933
  e: undefined,
@@ -15561,46 +15935,46 @@ function Sidebar(props) {
15561
15935
  a: undefined,
15562
15936
  o: undefined
15563
15937
  });
15564
- return _el$7;
15938
+ return _el$8;
15565
15939
  }
15566
- }), _el$17);
15567
- setProp(_el$17, "flexDirection", "row");
15568
- setProp(_el$17, "gap", 2);
15569
- setProp(_el$17, "paddingBottom", 1);
15570
- setProp(_el$17, "paddingLeft", 1);
15571
- insert(_el$17, createComponent2(For, {
15940
+ }), _el$18);
15941
+ setProp(_el$18, "flexDirection", "row");
15942
+ setProp(_el$18, "gap", 2);
15943
+ setProp(_el$18, "paddingBottom", 1);
15944
+ setProp(_el$18, "paddingLeft", 1);
15945
+ insert(_el$18, createComponent2(For, {
15572
15946
  each: VIEW_TABS,
15573
15947
  children: (tab) => {
15574
15948
  const active = () => view() === tab.view;
15575
15949
  return (() => {
15576
- var _el$25 = createElement("text");
15577
- setProp(_el$25, "wrapMode", "none");
15578
- setProp(_el$25, "onMouseUp", () => setView(tab.view));
15579
- insert(_el$25, () => tab.label);
15950
+ var _el$27 = createElement("text");
15951
+ setProp(_el$27, "wrapMode", "none");
15952
+ setProp(_el$27, "onMouseUp", () => setView(tab.view));
15953
+ insert(_el$27, () => tab.label);
15580
15954
  effect((_p$) => {
15581
- var _v$1 = active() ? theme.primary : theme.textMuted, _v$10 = active() ? TextAttributes8.BOLD : undefined;
15582
- _v$1 !== _p$.e && (_p$.e = setProp(_el$25, "fg", _v$1, _p$.e));
15583
- _v$10 !== _p$.t && (_p$.t = setProp(_el$25, "attributes", _v$10, _p$.t));
15955
+ var _v$13 = active() ? theme.primary : theme.textMuted, _v$14 = active() ? TextAttributes8.BOLD : undefined;
15956
+ _v$13 !== _p$.e && (_p$.e = setProp(_el$27, "fg", _v$13, _p$.e));
15957
+ _v$14 !== _p$.t && (_p$.t = setProp(_el$27, "attributes", _v$14, _p$.t));
15584
15958
  return _p$;
15585
15959
  }, {
15586
15960
  e: undefined,
15587
15961
  t: undefined
15588
15962
  });
15589
- return _el$25;
15963
+ return _el$27;
15590
15964
  })();
15591
15965
  }
15592
15966
  }));
15593
- insertNode(_el$18, _el$19);
15594
- setProp(_el$18, "flexGrow", 1);
15595
- setProp(_el$18, "verticalScrollbarOptions", {
15967
+ insertNode(_el$19, _el$20);
15968
+ setProp(_el$19, "flexGrow", 1);
15969
+ setProp(_el$19, "verticalScrollbarOptions", {
15596
15970
  trackOptions: {
15597
15971
  foregroundColor: "transparent"
15598
15972
  }
15599
15973
  });
15600
- setProp(_el$19, "flexShrink", 0);
15601
- setProp(_el$19, "gap", 0);
15602
- setProp(_el$19, "paddingRight", 1);
15603
- insert(_el$19, createComponent2(For, {
15974
+ setProp(_el$20, "flexShrink", 0);
15975
+ setProp(_el$20, "gap", 0);
15976
+ setProp(_el$20, "paddingRight", 1);
15977
+ insert(_el$20, createComponent2(For, {
15604
15978
  get each() {
15605
15979
  return rows();
15606
15980
  },
@@ -15640,175 +16014,290 @@ function Sidebar(props) {
15640
16014
  return theme.textMuted;
15641
16015
  }
15642
16016
  };
15643
- const branchLabel = createMemo(() => {
15644
- if (!isMain)
15645
- return "";
15646
- branchTick();
15647
- return readCurrentBranch(task.repo);
15648
- });
15649
16017
  const changes = createMemo(() => {
15650
16018
  branchTick();
15651
16019
  return readWorktreeChanges(task.worktreePath);
15652
16020
  });
15653
16021
  const titleText = isMain ? repoBasename(task.repo) : task.title;
16022
+ const loading = () => isLive() || task.status === "in_progress";
16023
+ const subtitleText = createMemo(() => {
16024
+ if (task.branch.length > 0)
16025
+ return truncateBranchLabel(task.branch, subtitleBudget());
16026
+ return STATUS_LABEL[task.status];
16027
+ });
16028
+ const barColor = () => isCursor() ? theme.focusAccent : isSelected() ? theme.primary : undefined;
16029
+ const barGlyph = () => isCursor() || isSelected() ? "\u258C" : " ";
16030
+ const showProjectsHeader = () => isMain && flatIndex === 0;
16031
+ const showTasksHeader = () => !isMain && flatIndex === firstTaskFlatIndex();
15654
16032
  return (() => {
15655
- var _el$26 = createElement("box"), _el$27 = createElement("text"), _el$28 = createElement("text");
15656
- insertNode(_el$26, _el$27);
15657
- insertNode(_el$26, _el$28);
15658
- setProp(_el$26, "flexDirection", "row");
15659
- setProp(_el$26, "paddingLeft", 1);
15660
- setProp(_el$26, "paddingRight", 1);
15661
- setProp(_el$26, "gap", 1);
15662
- setProp(_el$26, "onMouseUp", () => {
16033
+ var _el$28 = createElement("box"), _el$29 = createElement("box");
16034
+ insertNode(_el$28, _el$29);
16035
+ setProp(_el$28, "flexDirection", "column");
16036
+ setProp(_el$28, "gap", 0);
16037
+ setProp(_el$28, "paddingBottom", isMain ? 0 : 1);
16038
+ insert(_el$28, createComponent2(Show, {
16039
+ get when() {
16040
+ return showProjectsHeader();
16041
+ },
16042
+ get children() {
16043
+ return createComponent2(SectionHeader, {
16044
+ label: "PROJECTS"
16045
+ });
16046
+ }
16047
+ }), _el$29);
16048
+ insert(_el$28, createComponent2(Show, {
16049
+ get when() {
16050
+ return showTasksHeader();
16051
+ },
16052
+ get children() {
16053
+ return createComponent2(SectionHeader, {
16054
+ label: "TASKS",
16055
+ get topPad() {
16056
+ return firstTaskFlatIndex() > 0;
16057
+ }
16058
+ });
16059
+ }
16060
+ }), _el$29);
16061
+ setProp(_el$29, "flexDirection", "column");
16062
+ setProp(_el$29, "gap", 0);
16063
+ setProp(_el$29, "onMouseUp", () => {
15663
16064
  props.onSelect(task.id);
15664
16065
  if (props.activateOnClick)
15665
16066
  props.onActivate?.(task.id);
15666
16067
  });
15667
- setProp(_el$26, "onMouseOver", (e) => setHover({
16068
+ setProp(_el$29, "onMouseOver", (e) => setHover({
15668
16069
  task,
15669
16070
  x: e.x,
15670
16071
  y: e.y
15671
16072
  }));
15672
- setProp(_el$26, "onMouseOut", () => setHover((h) => h?.task.id === task.id ? null : h));
15673
- setProp(_el$27, "wrapMode", "none");
15674
- insert(_el$27, () => isMain ? "\u2605" : isLive() ? IN_PROGRESS_SPINNER[spinnerFrame()] ?? IN_PROGRESS_SPINNER[0] : task.status === "in_progress" ? "\u25CF" : badge.glyph);
15675
- setProp(_el$28, "wrapMode", "none");
15676
- setProp(_el$28, "flexGrow", 1);
15677
- insert(_el$28, () => truncateTitle(titleText, titleBudget()));
15678
- insert(_el$26, createComponent2(Show, {
15679
- get when() {
15680
- return showChangesCol();
15681
- },
15682
- get children() {
15683
- var _el$29 = createElement("box");
15684
- setProp(_el$29, "width", 5);
15685
- setProp(_el$29, "flexShrink", 0);
15686
- setProp(_el$29, "flexDirection", "row");
15687
- setProp(_el$29, "justifyContent", "flex-end");
15688
- setProp(_el$29, "gap", 1);
15689
- insert(_el$29, createComponent2(Show, {
15690
- get when() {
15691
- return changes().added > 0;
15692
- },
15693
- get children() {
15694
- var _el$30 = createElement("text"), _el$31 = createTextNode(`+`);
15695
- insertNode(_el$30, _el$31);
15696
- setProp(_el$30, "wrapMode", "none");
15697
- insert(_el$30, () => changes().added, null);
15698
- effect((_$p) => setProp(_el$30, "fg", isCursor() ? theme.selectedListItemText : theme.success, _$p));
15699
- return _el$30;
15700
- }
15701
- }), null);
15702
- insert(_el$29, createComponent2(Show, {
15703
- get when() {
15704
- return changes().deleted > 0;
15705
- },
15706
- get children() {
15707
- var _el$32 = createElement("text"), _el$33 = createTextNode(`\u2212`);
15708
- insertNode(_el$32, _el$33);
15709
- setProp(_el$32, "wrapMode", "none");
15710
- insert(_el$32, () => changes().deleted, null);
15711
- effect((_$p) => setProp(_el$32, "fg", isCursor() ? theme.selectedListItemText : theme.error, _$p));
15712
- return _el$32;
15713
- }
15714
- }), null);
15715
- return _el$29;
15716
- }
15717
- }), null);
15718
- insert(_el$26, createComponent2(Show, {
15719
- get when() {
15720
- return memo2(() => !!showBranchCol())() && (isMain && branchLabel().length > 0 || !isMain && (task.branch.length > 0 || task.pinned === true));
15721
- },
16073
+ setProp(_el$29, "onMouseOut", () => setHover((h) => h?.task.id === task.id ? null : h));
16074
+ insert(_el$29, createComponent2(Show, {
16075
+ when: isMain,
15722
16076
  get children() {
15723
- var _el$34 = createElement("box");
15724
- setProp(_el$34, "flexDirection", "row");
15725
- setProp(_el$34, "justifyContent", "flex-end");
15726
- setProp(_el$34, "gap", 1);
15727
- setProp(_el$34, "paddingLeft", 1);
15728
- setProp(_el$34, "flexShrink", 0);
15729
- insert(_el$34, createComponent2(Show, {
16077
+ var _el$30 = createElement("box"), _el$31 = createElement("text"), _el$32 = createElement("box"), _el$33 = createElement("text"), _el$34 = createElement("text");
16078
+ insertNode(_el$30, _el$31);
16079
+ insertNode(_el$30, _el$32);
16080
+ setProp(_el$30, "flexDirection", "row");
16081
+ setProp(_el$30, "gap", 0);
16082
+ setProp(_el$31, "wrapMode", "none");
16083
+ insert(_el$31, barGlyph);
16084
+ insertNode(_el$32, _el$33);
16085
+ insertNode(_el$32, _el$34);
16086
+ setProp(_el$32, "flexDirection", "row");
16087
+ setProp(_el$32, "flexGrow", 1);
16088
+ setProp(_el$32, "paddingRight", 1);
16089
+ setProp(_el$32, "gap", 1);
16090
+ setProp(_el$33, "wrapMode", "none");
16091
+ insert(_el$33, (() => {
16092
+ var _c$2 = memo2(() => !!loading());
16093
+ return () => _c$2() ? IN_PROGRESS_SPINNER[spinnerFrame()] ?? IN_PROGRESS_SPINNER[0] : "\u2605";
16094
+ })());
16095
+ setProp(_el$34, "wrapMode", "none");
16096
+ setProp(_el$34, "flexGrow", 1);
16097
+ insert(_el$34, () => truncateTitle(titleText, titleBudget()));
16098
+ insert(_el$32, createComponent2(Show, {
15730
16099
  get when() {
15731
- return isMain && branchLabel().length > 0;
16100
+ return loading();
15732
16101
  },
15733
16102
  get children() {
15734
16103
  var _el$35 = createElement("text");
16104
+ insertNode(_el$35, createTextNode(`working`));
15735
16105
  setProp(_el$35, "wrapMode", "none");
15736
- insert(_el$35, branchLabel);
15737
- effect((_$p) => setProp(_el$35, "fg", isCursor() ? theme.selectedListItemText : theme.textMuted, _$p));
16106
+ effect((_$p) => setProp(_el$35, "fg", theme.primary, _$p));
15738
16107
  return _el$35;
15739
16108
  }
15740
16109
  }), null);
15741
- insert(_el$34, createComponent2(Show, {
15742
- get when() {
15743
- return !isMain && task.branch.length > 0;
15744
- },
15745
- get children() {
15746
- var _el$36 = createElement("text");
15747
- setProp(_el$36, "wrapMode", "none");
15748
- insert(_el$36, () => truncateBranchLabel(task.branch));
15749
- effect((_$p) => setProp(_el$36, "fg", isCursor() ? theme.selectedListItemText : theme.textMuted, _$p));
15750
- return _el$36;
15751
- }
15752
- }), null);
15753
- insert(_el$34, createComponent2(Show, {
16110
+ insert(_el$32, createComponent2(Show, {
15754
16111
  get when() {
15755
- return !isMain && task.pinned === true;
16112
+ return !loading();
15756
16113
  },
15757
16114
  get children() {
15758
16115
  var _el$37 = createElement("text");
15759
- insertNode(_el$37, createTextNode(`\u25B4`));
15760
16116
  setProp(_el$37, "wrapMode", "none");
15761
- effect((_$p) => setProp(_el$37, "fg", isCursor() ? theme.selectedListItemText : theme.warning, _$p));
16117
+ insert(_el$37, () => truncatePathTail(abbrevHome(task.repo), subtitleBudget()));
16118
+ effect((_p$) => {
16119
+ var _v$15 = theme.textMuted, _v$16 = TextAttributes8.DIM;
16120
+ _v$15 !== _p$.e && (_p$.e = setProp(_el$37, "fg", _v$15, _p$.e));
16121
+ _v$16 !== _p$.t && (_p$.t = setProp(_el$37, "attributes", _v$16, _p$.t));
16122
+ return _p$;
16123
+ }, {
16124
+ e: undefined,
16125
+ t: undefined
16126
+ });
15762
16127
  return _el$37;
15763
16128
  }
15764
16129
  }), null);
15765
- return _el$34;
16130
+ effect((_p$) => {
16131
+ var _v$17 = barColor(), _v$18 = theme.primary, _v$19 = TextAttributes8.BOLD, _v$20 = theme.text, _v$21 = TextAttributes8.BOLD;
16132
+ _v$17 !== _p$.e && (_p$.e = setProp(_el$31, "fg", _v$17, _p$.e));
16133
+ _v$18 !== _p$.t && (_p$.t = setProp(_el$33, "fg", _v$18, _p$.t));
16134
+ _v$19 !== _p$.a && (_p$.a = setProp(_el$33, "attributes", _v$19, _p$.a));
16135
+ _v$20 !== _p$.o && (_p$.o = setProp(_el$34, "fg", _v$20, _p$.o));
16136
+ _v$21 !== _p$.i && (_p$.i = setProp(_el$34, "attributes", _v$21, _p$.i));
16137
+ return _p$;
16138
+ }, {
16139
+ e: undefined,
16140
+ t: undefined,
16141
+ a: undefined,
16142
+ o: undefined,
16143
+ i: undefined
16144
+ });
16145
+ return _el$30;
15766
16146
  }
15767
16147
  }), null);
15768
- effect((_p$) => {
15769
- var _v$11 = isCursor() ? theme.primary : isSelected() ? theme.backgroundElement : undefined, _v$12 = isCursor() ? theme.selectedListItemText : badgeColor(), _v$13 = TextAttributes8.BOLD, _v$14 = isCursor() ? theme.selectedListItemText : theme.text, _v$15 = (isMain || isSelected() && !isCursor()) && !isCursor() ? TextAttributes8.BOLD : undefined;
15770
- _v$11 !== _p$.e && (_p$.e = setProp(_el$26, "backgroundColor", _v$11, _p$.e));
15771
- _v$12 !== _p$.t && (_p$.t = setProp(_el$27, "fg", _v$12, _p$.t));
15772
- _v$13 !== _p$.a && (_p$.a = setProp(_el$27, "attributes", _v$13, _p$.a));
15773
- _v$14 !== _p$.o && (_p$.o = setProp(_el$28, "fg", _v$14, _p$.o));
15774
- _v$15 !== _p$.i && (_p$.i = setProp(_el$28, "attributes", _v$15, _p$.i));
15775
- return _p$;
15776
- }, {
15777
- e: undefined,
15778
- t: undefined,
15779
- a: undefined,
15780
- o: undefined,
15781
- i: undefined
15782
- });
15783
- return _el$26;
16148
+ insert(_el$29, createComponent2(Show, {
16149
+ when: !isMain,
16150
+ get children() {
16151
+ return [(() => {
16152
+ var _el$38 = createElement("box"), _el$39 = createElement("text"), _el$40 = createElement("box"), _el$41 = createElement("text"), _el$42 = createElement("text");
16153
+ insertNode(_el$38, _el$39);
16154
+ insertNode(_el$38, _el$40);
16155
+ setProp(_el$38, "flexDirection", "row");
16156
+ setProp(_el$38, "gap", 0);
16157
+ setProp(_el$39, "wrapMode", "none");
16158
+ insert(_el$39, barGlyph);
16159
+ insertNode(_el$40, _el$41);
16160
+ insertNode(_el$40, _el$42);
16161
+ setProp(_el$40, "flexDirection", "row");
16162
+ setProp(_el$40, "flexGrow", 1);
16163
+ setProp(_el$40, "paddingRight", 1);
16164
+ setProp(_el$40, "gap", 1);
16165
+ setProp(_el$41, "wrapMode", "none");
16166
+ insert(_el$41, (() => {
16167
+ var _c$3 = memo2(() => !!loading());
16168
+ return () => _c$3() ? IN_PROGRESS_SPINNER[spinnerFrame()] ?? IN_PROGRESS_SPINNER[0] : badge.glyph;
16169
+ })());
16170
+ setProp(_el$42, "wrapMode", "none");
16171
+ setProp(_el$42, "flexGrow", 1);
16172
+ insert(_el$42, () => truncateTitle(titleText, titleBudget()));
16173
+ insert(_el$40, createComponent2(Show, {
16174
+ get when() {
16175
+ return loading();
16176
+ },
16177
+ get children() {
16178
+ var _el$43 = createElement("text");
16179
+ insertNode(_el$43, createTextNode(`working`));
16180
+ setProp(_el$43, "wrapMode", "none");
16181
+ effect((_$p) => setProp(_el$43, "fg", theme.primary, _$p));
16182
+ return _el$43;
16183
+ }
16184
+ }), null);
16185
+ effect((_p$) => {
16186
+ var _v$22 = barColor(), _v$23 = badgeColor(), _v$24 = TextAttributes8.BOLD, _v$25 = theme.text, _v$26 = isSelected() || isCursor() ? TextAttributes8.BOLD : undefined;
16187
+ _v$22 !== _p$.e && (_p$.e = setProp(_el$39, "fg", _v$22, _p$.e));
16188
+ _v$23 !== _p$.t && (_p$.t = setProp(_el$41, "fg", _v$23, _p$.t));
16189
+ _v$24 !== _p$.a && (_p$.a = setProp(_el$41, "attributes", _v$24, _p$.a));
16190
+ _v$25 !== _p$.o && (_p$.o = setProp(_el$42, "fg", _v$25, _p$.o));
16191
+ _v$26 !== _p$.i && (_p$.i = setProp(_el$42, "attributes", _v$26, _p$.i));
16192
+ return _p$;
16193
+ }, {
16194
+ e: undefined,
16195
+ t: undefined,
16196
+ a: undefined,
16197
+ o: undefined,
16198
+ i: undefined
16199
+ });
16200
+ return _el$38;
16201
+ })(), (() => {
16202
+ var _el$45 = createElement("box"), _el$46 = createElement("text"), _el$47 = createElement("box"), _el$48 = createElement("text");
16203
+ insertNode(_el$45, _el$46);
16204
+ insertNode(_el$45, _el$47);
16205
+ setProp(_el$45, "flexDirection", "row");
16206
+ setProp(_el$45, "gap", 0);
16207
+ setProp(_el$46, "wrapMode", "none");
16208
+ insert(_el$46, barGlyph);
16209
+ insertNode(_el$47, _el$48);
16210
+ setProp(_el$47, "flexDirection", "row");
16211
+ setProp(_el$47, "flexGrow", 1);
16212
+ setProp(_el$47, "paddingLeft", 2);
16213
+ setProp(_el$47, "paddingRight", 1);
16214
+ setProp(_el$47, "gap", 1);
16215
+ setProp(_el$48, "wrapMode", "none");
16216
+ setProp(_el$48, "flexGrow", 1);
16217
+ insert(_el$48, subtitleText);
16218
+ insert(_el$47, createComponent2(Show, {
16219
+ get when() {
16220
+ return task.pinned === true;
16221
+ },
16222
+ get children() {
16223
+ var _el$49 = createElement("text");
16224
+ insertNode(_el$49, createTextNode(`\u25B4`));
16225
+ setProp(_el$49, "wrapMode", "none");
16226
+ effect((_$p) => setProp(_el$49, "fg", theme.warning, _$p));
16227
+ return _el$49;
16228
+ }
16229
+ }), null);
16230
+ insert(_el$47, createComponent2(Show, {
16231
+ get when() {
16232
+ return changes().added > 0;
16233
+ },
16234
+ get children() {
16235
+ var _el$51 = createElement("text"), _el$52 = createTextNode(`+`);
16236
+ insertNode(_el$51, _el$52);
16237
+ setProp(_el$51, "wrapMode", "none");
16238
+ insert(_el$51, () => changes().added, null);
16239
+ effect((_$p) => setProp(_el$51, "fg", theme.success, _$p));
16240
+ return _el$51;
16241
+ }
16242
+ }), null);
16243
+ insert(_el$47, createComponent2(Show, {
16244
+ get when() {
16245
+ return changes().deleted > 0;
16246
+ },
16247
+ get children() {
16248
+ var _el$53 = createElement("text"), _el$54 = createTextNode(`\u2212`);
16249
+ insertNode(_el$53, _el$54);
16250
+ setProp(_el$53, "wrapMode", "none");
16251
+ insert(_el$53, () => changes().deleted, null);
16252
+ effect((_$p) => setProp(_el$53, "fg", theme.error, _$p));
16253
+ return _el$53;
16254
+ }
16255
+ }), null);
16256
+ effect((_p$) => {
16257
+ var _v$27 = barColor(), _v$28 = theme.textMuted, _v$29 = TextAttributes8.DIM;
16258
+ _v$27 !== _p$.e && (_p$.e = setProp(_el$46, "fg", _v$27, _p$.e));
16259
+ _v$28 !== _p$.t && (_p$.t = setProp(_el$48, "fg", _v$28, _p$.t));
16260
+ _v$29 !== _p$.a && (_p$.a = setProp(_el$48, "attributes", _v$29, _p$.a));
16261
+ return _p$;
16262
+ }, {
16263
+ e: undefined,
16264
+ t: undefined,
16265
+ a: undefined
16266
+ });
16267
+ return _el$45;
16268
+ })()];
16269
+ }
16270
+ }), null);
16271
+ effect((_$p) => setProp(_el$29, "backgroundColor", isCursor() ? theme.backgroundElement : undefined, _$p));
16272
+ return _el$28;
15784
16273
  })();
15785
16274
  }
15786
16275
  }), null);
15787
- insert(_el$19, createComponent2(Show, {
16276
+ insert(_el$20, createComponent2(Show, {
15788
16277
  get when() {
15789
16278
  return flatIds().length === 0;
15790
16279
  },
15791
16280
  get children() {
15792
- var _el$20 = createElement("box"), _el$21 = createElement("text");
15793
- insertNode(_el$20, _el$21);
15794
- setProp(_el$20, "paddingTop", 1);
15795
- setProp(_el$20, "paddingLeft", 1);
15796
- insert(_el$21, (() => {
16281
+ var _el$21 = createElement("box"), _el$22 = createElement("text");
16282
+ insertNode(_el$21, _el$22);
16283
+ setProp(_el$21, "paddingTop", 1);
16284
+ setProp(_el$21, "paddingLeft", 1);
16285
+ insert(_el$22, (() => {
15797
16286
  var _c$ = memo2(() => !!(searchMode() && searchQuery().trim().length > 0));
15798
16287
  return () => _c$() ? "No matching tasks \u2014 esc to clear." : view() === "active" ? "No active tasks." : "No archived tasks.";
15799
16288
  })());
15800
- effect((_$p) => setProp(_el$21, "fg", theme.textMuted, _$p));
15801
- return _el$20;
16289
+ effect((_$p) => setProp(_el$22, "fg", theme.textMuted, _$p));
16290
+ return _el$21;
15802
16291
  }
15803
16292
  }), null);
15804
- insertNode(_el$22, _el$23);
15805
- setProp(_el$22, "flexShrink", 0);
15806
- setProp(_el$22, "paddingTop", 1);
15807
- setProp(_el$22, "paddingLeft", 1);
15808
- insertNode(_el$23, createTextNode(`+ New task`));
15809
- setProp(_el$23, "wrapMode", "none");
15810
- setProp(_el$23, "onMouseUp", () => props.onAddTask?.());
15811
- insert(_el$, createComponent2(Show, {
16293
+ insertNode(_el$23, _el$24);
16294
+ setProp(_el$23, "flexShrink", 0);
16295
+ setProp(_el$23, "paddingTop", 1);
16296
+ setProp(_el$23, "paddingLeft", 1);
16297
+ insertNode(_el$24, createTextNode(`+ New task`));
16298
+ setProp(_el$24, "wrapMode", "none");
16299
+ setProp(_el$24, "onMouseUp", () => props.onAddTask?.());
16300
+ insert(_el$4, createComponent2(Show, {
15812
16301
  get when() {
15813
16302
  return hover();
15814
16303
  },
@@ -15838,43 +16327,43 @@ function Sidebar(props) {
15838
16327
  const left = createMemo(() => Math.max(0, Math.min(h().x + 2, dims().width - boxW() - 1)));
15839
16328
  const top = createMemo(() => Math.max(0, Math.min(h().y + 1, dims().height - boxH() - 1)));
15840
16329
  return (() => {
15841
- var _el$39 = createElement("box");
15842
- setProp(_el$39, "position", "absolute");
15843
- setProp(_el$39, "zIndex", 2600);
15844
- setProp(_el$39, "flexDirection", "column");
15845
- setProp(_el$39, "border", true);
15846
- setProp(_el$39, "paddingLeft", 1);
15847
- setProp(_el$39, "paddingRight", 1);
15848
- insert(_el$39, createComponent2(For, {
16330
+ var _el$55 = createElement("box");
16331
+ setProp(_el$55, "position", "absolute");
16332
+ setProp(_el$55, "zIndex", 2600);
16333
+ setProp(_el$55, "flexDirection", "column");
16334
+ setProp(_el$55, "border", true);
16335
+ setProp(_el$55, "paddingLeft", 1);
16336
+ setProp(_el$55, "paddingRight", 1);
16337
+ insert(_el$55, createComponent2(For, {
15849
16338
  get each() {
15850
16339
  return lines();
15851
16340
  },
15852
16341
  children: (l) => (() => {
15853
- var _el$40 = createElement("text");
15854
- setProp(_el$40, "wrapMode", "none");
15855
- insert(_el$40, (() => {
15856
- var _c$2 = memo2(() => !!l.dim);
15857
- return () => _c$2() ? truncatePathTail(l.text, innerW()) : truncateTitle(l.text, innerW());
16342
+ var _el$56 = createElement("text");
16343
+ setProp(_el$56, "wrapMode", "none");
16344
+ insert(_el$56, (() => {
16345
+ var _c$4 = memo2(() => !!l.dim);
16346
+ return () => _c$4() ? truncatePathTail(l.text, innerW()) : truncateTitle(l.text, innerW());
15858
16347
  })());
15859
16348
  effect((_p$) => {
15860
- var _v$21 = l.dim ? theme.textMuted : theme.text, _v$22 = l.bold ? TextAttributes8.BOLD : l.dim ? TextAttributes8.DIM : undefined;
15861
- _v$21 !== _p$.e && (_p$.e = setProp(_el$40, "fg", _v$21, _p$.e));
15862
- _v$22 !== _p$.t && (_p$.t = setProp(_el$40, "attributes", _v$22, _p$.t));
16349
+ var _v$35 = l.dim ? theme.textMuted : theme.text, _v$36 = l.bold ? TextAttributes8.BOLD : l.dim ? TextAttributes8.DIM : undefined;
16350
+ _v$35 !== _p$.e && (_p$.e = setProp(_el$56, "fg", _v$35, _p$.e));
16351
+ _v$36 !== _p$.t && (_p$.t = setProp(_el$56, "attributes", _v$36, _p$.t));
15863
16352
  return _p$;
15864
16353
  }, {
15865
16354
  e: undefined,
15866
16355
  t: undefined
15867
16356
  });
15868
- return _el$40;
16357
+ return _el$56;
15869
16358
  })()
15870
16359
  }));
15871
16360
  effect((_p$) => {
15872
- var _v$16 = left(), _v$17 = top(), _v$18 = boxW(), _v$19 = theme.focusAccent, _v$20 = theme.backgroundElement;
15873
- _v$16 !== _p$.e && (_p$.e = setProp(_el$39, "left", _v$16, _p$.e));
15874
- _v$17 !== _p$.t && (_p$.t = setProp(_el$39, "top", _v$17, _p$.t));
15875
- _v$18 !== _p$.a && (_p$.a = setProp(_el$39, "width", _v$18, _p$.a));
15876
- _v$19 !== _p$.o && (_p$.o = setProp(_el$39, "borderColor", _v$19, _p$.o));
15877
- _v$20 !== _p$.i && (_p$.i = setProp(_el$39, "backgroundColor", _v$20, _p$.i));
16361
+ var _v$30 = left(), _v$31 = top(), _v$32 = boxW(), _v$33 = theme.focusAccent, _v$34 = theme.backgroundElement;
16362
+ _v$30 !== _p$.e && (_p$.e = setProp(_el$55, "left", _v$30, _p$.e));
16363
+ _v$31 !== _p$.t && (_p$.t = setProp(_el$55, "top", _v$31, _p$.t));
16364
+ _v$32 !== _p$.a && (_p$.a = setProp(_el$55, "width", _v$32, _p$.a));
16365
+ _v$33 !== _p$.o && (_p$.o = setProp(_el$55, "borderColor", _v$33, _p$.o));
16366
+ _v$34 !== _p$.i && (_p$.i = setProp(_el$55, "backgroundColor", _v$34, _p$.i));
15878
16367
  return _p$;
15879
16368
  }, {
15880
16369
  e: undefined,
@@ -15883,31 +16372,27 @@ function Sidebar(props) {
15883
16372
  o: undefined,
15884
16373
  i: undefined
15885
16374
  });
15886
- return _el$39;
16375
+ return _el$55;
15887
16376
  })();
15888
16377
  }
15889
16378
  }), null);
15890
16379
  effect((_p$) => {
15891
- var _v$5 = props.width ? props.width() : SIDEBAR_WIDTH, _v$6 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$7 = TextAttributes8.BOLD, _v$8 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$9 = TextAttributes8.BOLD, _v$0 = theme.textMuted;
15892
- _v$5 !== _p$.e && (_p$.e = setProp(_el$, "width", _v$5, _p$.e));
15893
- _v$6 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$6, _p$.t));
15894
- _v$7 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$7, _p$.a));
15895
- _v$8 !== _p$.o && (_p$.o = setProp(_el$5, "fg", _v$8, _p$.o));
15896
- _v$9 !== _p$.i && (_p$.i = setProp(_el$5, "attributes", _v$9, _p$.i));
15897
- _v$0 !== _p$.n && (_p$.n = setProp(_el$23, "fg", _v$0, _p$.n));
16380
+ var _v$9 = props.width ? props.width() : SIDEBAR_WIDTH, _v$0 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$1 = TextAttributes8.BOLD, _v$10 = theme.textMuted;
16381
+ _v$9 !== _p$.e && (_p$.e = setProp(_el$4, "width", _v$9, _p$.e));
16382
+ _v$0 !== _p$.t && (_p$.t = setProp(_el$6, "fg", _v$0, _p$.t));
16383
+ _v$1 !== _p$.a && (_p$.a = setProp(_el$6, "attributes", _v$1, _p$.a));
16384
+ _v$10 !== _p$.o && (_p$.o = setProp(_el$24, "fg", _v$10, _p$.o));
15898
16385
  return _p$;
15899
16386
  }, {
15900
16387
  e: undefined,
15901
16388
  t: undefined,
15902
16389
  a: undefined,
15903
- o: undefined,
15904
- i: undefined,
15905
- n: undefined
16390
+ o: undefined
15906
16391
  });
15907
- return _el$;
16392
+ return _el$4;
15908
16393
  })();
15909
16394
  }
15910
- var SIDEBAR_WIDTH = 32, STATUS_BADGE, IN_PROGRESS_SPINNER, SPINNER_FRAME_MS = 100, VIEW_TABS, MAIN_BRANCH_POLL_MS = 2000, CHANGES_COLUMN_WIDTH = 5, BRANCH_LABEL_MAX = 16, SHOW_CHANGES_MIN_WIDTH = 30, SHOW_BRANCH_MIN_WIDTH = 44;
16395
+ var SIDEBAR_WIDTH = 32, STATUS_BADGE, STATUS_LABEL, IN_PROGRESS_SPINNER, SPINNER_FRAME_MS = 100, VIEW_TABS, MAIN_BRANCH_POLL_MS = 2000, BRANCH_LABEL_MAX = 16;
15911
16396
  var init_Sidebar = __esm(() => {
15912
16397
  init_solid();
15913
16398
  init_solid();
@@ -15921,7 +16406,6 @@ var init_Sidebar = __esm(() => {
15921
16406
  init_dev();
15922
16407
  init_theme2();
15923
16408
  init_theme2();
15924
- init_git_head();
15925
16409
  init_groups();
15926
16410
  init_keys();
15927
16411
  init_worktree_changes();
@@ -15951,6 +16435,14 @@ var init_Sidebar = __esm(() => {
15951
16435
  tone: "error"
15952
16436
  }
15953
16437
  };
16438
+ STATUS_LABEL = {
16439
+ done: "done",
16440
+ in_review: "in review",
16441
+ in_progress: "working",
16442
+ backlog: "backlog",
16443
+ canceled: "canceled",
16444
+ error: "error"
16445
+ };
15954
16446
  IN_PROGRESS_SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
15955
16447
  VIEW_TABS = [{
15956
16448
  view: "active",
@@ -16056,7 +16548,18 @@ function TasksShell(props) {
16056
16548
  return;
16057
16549
  }
16058
16550
  }
16059
- SettingsDialog.show(dialog, kv, props.orch ?? undefined);
16551
+ const result = await SettingsDialog.show(dialog, kv, props.orch ?? undefined);
16552
+ if (!result.visualPrefsChanged)
16553
+ return;
16554
+ if (!kv.flush())
16555
+ return;
16556
+ try {
16557
+ const session = await currentSessionName();
16558
+ if (session)
16559
+ await refreshKobeWorkspacePanes(session);
16560
+ } catch (err) {
16561
+ console.error("[kobe tasks] failed to refresh workspace panes:", err);
16562
+ }
16060
16563
  }
16061
16564
  async function openUpdate() {
16062
16565
  const info = updateInfo();
@@ -16291,6 +16794,18 @@ function TasksShell(props) {
16291
16794
  await runTmux(["switch-client", "-t", `=${name}`]);
16292
16795
  props.orch?.setActiveTask(id).catch(() => {});
16293
16796
  }
16797
+ const headerStatus = createMemo(() => {
16798
+ const info = updateInfo();
16799
+ if (info?.hasUpdate)
16800
+ return {
16801
+ label: `v${info.latest} \u2191`,
16802
+ emphasize: true
16803
+ };
16804
+ return {
16805
+ label: `v${CURRENT_VERSION}`,
16806
+ emphasize: false
16807
+ };
16808
+ });
16294
16809
  return (() => {
16295
16810
  var _el$ = createElement("box"), _el$2 = createElement("box");
16296
16811
  insertNode(_el$, _el$2);
@@ -16306,6 +16821,8 @@ function TasksShell(props) {
16306
16821
  onSelect: setSelectedId,
16307
16822
  onActivate: (id) => void switchTo(id),
16308
16823
  activateOnClick: true,
16824
+ headerStatus,
16825
+ onHeaderStatusClick: () => void openUpdate(),
16309
16826
  width: () => dimensions().width,
16310
16827
  onAddTask: () => void createTask(),
16311
16828
  onRenameRequest: (id) => void renameTask(id),
@@ -16313,24 +16830,15 @@ function TasksShell(props) {
16313
16830
  onArchiveRequest: (id) => void archiveTask(id),
16314
16831
  focused: () => dialog.stack.length === 0
16315
16832
  }));
16316
- insert(_el$, createComponent2(ShortcutHints, {
16317
- updateInfo,
16318
- onOpenUpdate: () => void openUpdate()
16319
- }), null);
16833
+ insert(_el$, createComponent2(ShortcutHints, {}), null);
16320
16834
  effect((_$p) => setProp(_el$, "backgroundColor", theme.background, _$p));
16321
16835
  return _el$;
16322
16836
  })();
16323
16837
  }
16324
- function ShortcutHints(props) {
16838
+ function ShortcutHints() {
16325
16839
  const {
16326
16840
  theme
16327
16841
  } = useTheme();
16328
- const updateLabel = createMemo(() => {
16329
- const info = props.updateInfo();
16330
- if (!info)
16331
- return `v${CURRENT_VERSION}`;
16332
- return info.hasUpdate ? `v${info.latest} available` : `v${CURRENT_VERSION} latest`;
16333
- });
16334
16842
  const HINTS = [{
16335
16843
  k: "\u23CE",
16336
16844
  label: "open"
@@ -16380,111 +16888,63 @@ function ShortcutHints(props) {
16380
16888
  k: "\u2303Q",
16381
16889
  label: "detach"
16382
16890
  }];
16891
+ const LABEL_COL_MAX = 18;
16892
+ const labelColWidth = Math.min(LABEL_COL_MAX, Math.max(...HINTS.map((h) => h.label.length)));
16893
+ const clipLabel = (s) => s.length <= labelColWidth ? s : `${s.slice(0, Math.max(0, labelColWidth - 1))}\u2026`;
16383
16894
  return (() => {
16384
- var _el$3 = createElement("box"), _el$4 = createElement("text"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$12 = createElement("text");
16895
+ var _el$3 = createElement("box"), _el$4 = createElement("text");
16385
16896
  insertNode(_el$3, _el$4);
16386
- insertNode(_el$3, _el$6);
16387
- insertNode(_el$3, _el$12);
16388
16897
  setProp(_el$3, "flexShrink", 0);
16389
16898
  setProp(_el$3, "flexDirection", "column");
16390
16899
  setProp(_el$3, "paddingLeft", 1);
16391
16900
  setProp(_el$3, "paddingRight", 1);
16392
16901
  setProp(_el$3, "paddingTop", 1);
16393
16902
  setProp(_el$3, "gap", 0);
16394
- insertNode(_el$4, createTextNode(`\u2500\u2500 system \u2500\u2500`));
16903
+ insertNode(_el$4, createTextNode(`\u2500\u2500 keys \u2500\u2500`));
16395
16904
  setProp(_el$4, "wrapMode", "none");
16396
- insertNode(_el$6, _el$7);
16397
- setProp(_el$6, "flexDirection", "row");
16398
- setProp(_el$6, "gap", 1);
16399
- setProp(_el$7, "wrapMode", "none");
16400
- setProp(_el$7, "onMouseUp", () => {
16401
- if (props.updateInfo()?.hasUpdate)
16402
- props.onOpenUpdate();
16403
- });
16404
- insert(_el$7, updateLabel);
16405
- insert(_el$3, createComponent2(Show, {
16406
- get when() {
16407
- return props.updateInfo()?.hasUpdate;
16408
- },
16409
- get children() {
16410
- var _el$8 = createElement("box"), _el$9 = createElement("box"), _el$0 = createElement("text"), _el$10 = createElement("text");
16411
- insertNode(_el$8, _el$9);
16412
- insertNode(_el$8, _el$10);
16413
- setProp(_el$8, "flexDirection", "row");
16414
- setProp(_el$8, "gap", 1);
16415
- setProp(_el$8, "onMouseUp", () => props.onOpenUpdate());
16416
- insertNode(_el$9, _el$0);
16417
- setProp(_el$9, "width", 10);
16418
- setProp(_el$9, "flexShrink", 0);
16419
- insertNode(_el$0, createTextNode(`[U]`));
16420
- setProp(_el$0, "wrapMode", "none");
16421
- insertNode(_el$10, createTextNode(`update page`));
16422
- setProp(_el$10, "wrapMode", "none");
16423
- effect((_p$) => {
16424
- var _v$ = theme.accent, _v$2 = TextAttributes9.BOLD, _v$3 = theme.accent, _v$4 = TextAttributes9.DIM;
16425
- _v$ !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$, _p$.e));
16426
- _v$2 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$2, _p$.t));
16427
- _v$3 !== _p$.a && (_p$.a = setProp(_el$10, "fg", _v$3, _p$.a));
16428
- _v$4 !== _p$.o && (_p$.o = setProp(_el$10, "attributes", _v$4, _p$.o));
16429
- return _p$;
16430
- }, {
16431
- e: undefined,
16432
- t: undefined,
16433
- a: undefined,
16434
- o: undefined
16435
- });
16436
- return _el$8;
16437
- }
16438
- }), _el$12);
16439
- insertNode(_el$12, createTextNode(`\u2500\u2500 keys \u2500\u2500`));
16440
- setProp(_el$12, "wrapMode", "none");
16441
16905
  insert(_el$3, createComponent2(For, {
16442
16906
  each: HINTS,
16443
16907
  children: (h) => (() => {
16444
- var _el$14 = createElement("box"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$17 = createTextNode(`[`), _el$18 = createTextNode(`]`), _el$19 = createElement("text");
16445
- insertNode(_el$14, _el$15);
16446
- insertNode(_el$14, _el$19);
16447
- setProp(_el$14, "flexDirection", "row");
16448
- setProp(_el$14, "gap", 1);
16449
- insertNode(_el$15, _el$16);
16450
- setProp(_el$15, "width", 10);
16451
- setProp(_el$15, "flexShrink", 0);
16452
- insertNode(_el$16, _el$17);
16453
- insertNode(_el$16, _el$18);
16454
- setProp(_el$16, "wrapMode", "none");
16455
- insert(_el$16, () => h.k, _el$18);
16456
- setProp(_el$19, "wrapMode", "none");
16457
- insert(_el$19, () => h.label);
16908
+ var _el$6 = createElement("box"), _el$7 = createElement("box"), _el$8 = createElement("text"), _el$9 = createTextNode(`[`), _el$0 = createTextNode(`]`), _el$1 = createElement("box"), _el$10 = createElement("text");
16909
+ insertNode(_el$6, _el$7);
16910
+ insertNode(_el$6, _el$1);
16911
+ setProp(_el$6, "flexDirection", "row");
16912
+ setProp(_el$6, "gap", 1);
16913
+ setProp(_el$6, "justifyContent", "space-between");
16914
+ insertNode(_el$7, _el$8);
16915
+ setProp(_el$7, "width", 10);
16916
+ setProp(_el$7, "flexShrink", 0);
16917
+ insertNode(_el$8, _el$9);
16918
+ insertNode(_el$8, _el$0);
16919
+ setProp(_el$8, "wrapMode", "none");
16920
+ insert(_el$8, () => h.k, _el$0);
16921
+ insertNode(_el$1, _el$10);
16922
+ setProp(_el$1, "width", labelColWidth);
16923
+ setProp(_el$1, "flexShrink", 0);
16924
+ setProp(_el$10, "wrapMode", "none");
16925
+ insert(_el$10, () => clipLabel(h.label));
16458
16926
  effect((_p$) => {
16459
- var _v$1 = theme.accent, _v$10 = TextAttributes9.BOLD, _v$11 = theme.textMuted;
16460
- _v$1 !== _p$.e && (_p$.e = setProp(_el$16, "fg", _v$1, _p$.e));
16461
- _v$10 !== _p$.t && (_p$.t = setProp(_el$16, "attributes", _v$10, _p$.t));
16462
- _v$11 !== _p$.a && (_p$.a = setProp(_el$19, "fg", _v$11, _p$.a));
16927
+ var _v$3 = theme.accent, _v$4 = TextAttributes9.BOLD, _v$5 = theme.textMuted;
16928
+ _v$3 !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$3, _p$.e));
16929
+ _v$4 !== _p$.t && (_p$.t = setProp(_el$8, "attributes", _v$4, _p$.t));
16930
+ _v$5 !== _p$.a && (_p$.a = setProp(_el$10, "fg", _v$5, _p$.a));
16463
16931
  return _p$;
16464
16932
  }, {
16465
16933
  e: undefined,
16466
16934
  t: undefined,
16467
16935
  a: undefined
16468
16936
  });
16469
- return _el$14;
16937
+ return _el$6;
16470
16938
  })()
16471
16939
  }), null);
16472
16940
  effect((_p$) => {
16473
- var _v$5 = theme.textMuted, _v$6 = TextAttributes9.DIM, _v$7 = props.updateInfo()?.hasUpdate ? theme.warning : theme.textMuted, _v$8 = props.updateInfo()?.hasUpdate ? TextAttributes9.BOLD : TextAttributes9.DIM, _v$9 = theme.textMuted, _v$0 = TextAttributes9.DIM;
16474
- _v$5 !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$5, _p$.e));
16475
- _v$6 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$6, _p$.t));
16476
- _v$7 !== _p$.a && (_p$.a = setProp(_el$7, "fg", _v$7, _p$.a));
16477
- _v$8 !== _p$.o && (_p$.o = setProp(_el$7, "attributes", _v$8, _p$.o));
16478
- _v$9 !== _p$.i && (_p$.i = setProp(_el$12, "fg", _v$9, _p$.i));
16479
- _v$0 !== _p$.n && (_p$.n = setProp(_el$12, "attributes", _v$0, _p$.n));
16941
+ var _v$ = theme.textMuted, _v$2 = TextAttributes9.DIM;
16942
+ _v$ !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$, _p$.e));
16943
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$2, _p$.t));
16480
16944
  return _p$;
16481
16945
  }, {
16482
16946
  e: undefined,
16483
- t: undefined,
16484
- a: undefined,
16485
- o: undefined,
16486
- i: undefined,
16487
- n: undefined
16947
+ t: undefined
16488
16948
  });
16489
16949
  return _el$3;
16490
16950
  })();
@@ -16613,13 +17073,29 @@ function SettingsPage(props) {
16613
17073
  const {
16614
17074
  theme
16615
17075
  } = themeCtx;
17076
+ let visualPrefsChanged = false;
17077
+ let exiting = false;
16616
17078
  onMount(() => {
16617
17079
  themeCtx.setTransparentBackground(props.transparent);
16618
17080
  if (props.focusAccent)
16619
17081
  themeCtx.setFocusAccent(props.focusAccent);
16620
17082
  });
16621
- function exit() {
16622
- process.exit(0);
17083
+ async function exit() {
17084
+ if (exiting)
17085
+ return;
17086
+ exiting = true;
17087
+ try {
17088
+ const flushed = kv.flush();
17089
+ if (flushed && visualPrefsChanged) {
17090
+ const session = await currentSessionName();
17091
+ if (session)
17092
+ await refreshKobeWorkspacePanes(session);
17093
+ }
17094
+ } catch (err) {
17095
+ console.error("[kobe settings] failed to refresh workspace panes:", err);
17096
+ } finally {
17097
+ process.exit(0);
17098
+ }
16623
17099
  }
16624
17100
  useBindings(() => ({
16625
17101
  enabled: dialog.stack.length === 0,
@@ -16635,15 +17111,23 @@ function SettingsPage(props) {
16635
17111
  }]
16636
17112
  }));
16637
17113
  return (() => {
16638
- var _el$ = createElement("box");
16639
- setProp(_el$, "flexDirection", "column");
17114
+ var _el$ = createElement("scrollbox");
16640
17115
  setProp(_el$, "flexGrow", 1);
16641
17116
  setProp(_el$, "paddingTop", 1);
17117
+ setProp(_el$, "verticalScrollbarOptions", {
17118
+ trackOptions: {
17119
+ foregroundColor: "transparent"
17120
+ }
17121
+ });
16642
17122
  insert(_el$, createComponent2(SettingsDialog, {
16643
17123
  kv,
16644
17124
  get orchestrator() {
16645
17125
  return props.orchestrator ?? undefined;
16646
17126
  },
17127
+ standalone: true,
17128
+ onVisualPrefsChange: () => {
17129
+ visualPrefsChanged = true;
17130
+ },
16647
17131
  onClose: exit
16648
17132
  }));
16649
17133
  effect((_$p) => setProp(_el$, "backgroundColor", theme.background, _$p));
@@ -16726,6 +17210,7 @@ var init_host2 = __esm(() => {
16726
17210
  init_loader();
16727
17211
  init_keymap();
16728
17212
  init_persisted_ui_prefs();
17213
+ init_tmux();
16729
17214
  init_dialog();
16730
17215
  });
16731
17216
 
@@ -16870,7 +17355,7 @@ var exports_host4 = {};
16870
17355
  __export(exports_host4, {
16871
17356
  startUpdateHost: () => startUpdateHost
16872
17357
  });
16873
- import { spawn as spawn4, spawnSync as spawnSync9 } from "child_process";
17358
+ import { spawn as spawn4, spawnSync as spawnSync8 } from "child_process";
16874
17359
  import { TextAttributes as TextAttributes10 } from "@opentui/core";
16875
17360
  function openExternalUrl(url) {
16876
17361
  if (!url)
@@ -16976,7 +17461,7 @@ kobe ${CURRENT_VERSION} -> latest
16976
17461
  process.stdout.write(`running: ${UPDATE_COMMAND}
16977
17462
 
16978
17463
  `);
16979
- const result = spawnSync9("sh", ["-c", UPDATE_COMMAND], {
17464
+ const result = spawnSync8("sh", ["-c", UPDATE_COMMAND], {
16980
17465
  stdio: "inherit"
16981
17466
  });
16982
17467
  const code = result.status ?? (result.error ? 1 : 0);
@@ -17423,6 +17908,63 @@ var init_activity = __esm(() => {
17423
17908
  init_history3();
17424
17909
  });
17425
17910
 
17911
+ // src/tmux/editor-launch.ts
17912
+ function firstToken(cmd) {
17913
+ return cmd.trim().split(/\s+/)[0] ?? "";
17914
+ }
17915
+ function buildEditorCommand(kind, customCommand, absPath, envEditor) {
17916
+ const file = shellQuote(absPath);
17917
+ if (kind === "vim")
17918
+ return { bin: "vim", command: `vim ${file}` };
17919
+ if (kind === "nano")
17920
+ return { bin: "nano", command: `nano ${file}` };
17921
+ const tmpl = (customCommand.trim() || (envEditor ?? "").trim()).trim();
17922
+ if (!tmpl)
17923
+ return null;
17924
+ const bin = firstToken(tmpl);
17925
+ if (!bin)
17926
+ return null;
17927
+ const command = tmpl.includes(FILE_PLACEHOLDER) ? tmpl.split(FILE_PLACEHOLDER).join(file) : `${tmpl} ${file}`;
17928
+ return { bin, command };
17929
+ }
17930
+ function resolveEditorCommand(absPath) {
17931
+ const kind = normalizeEditorKind(getPersistedString(EDITOR_KIND_KEY));
17932
+ const custom = getPersistedString(EDITOR_CUSTOM_KEY) ?? "";
17933
+ const env = process.env.VISUAL ?? process.env.EDITOR ?? "";
17934
+ return buildEditorCommand(kind, custom, absPath, env);
17935
+ }
17936
+ async function binaryAvailable(bin) {
17937
+ try {
17938
+ const proc = Bun.spawn(["sh", "-c", `command -v ${shellQuote(bin)} >/dev/null 2>&1`], {
17939
+ stdin: "ignore",
17940
+ stdout: "ignore",
17941
+ stderr: "ignore"
17942
+ });
17943
+ return await proc.exited === 0;
17944
+ } catch {
17945
+ return false;
17946
+ }
17947
+ }
17948
+ async function openInEditor(session, worktree, absPath) {
17949
+ const resolved = resolveEditorCommand(absPath);
17950
+ if (!resolved)
17951
+ return false;
17952
+ if (!await binaryAvailable(resolved.bin))
17953
+ return false;
17954
+ await newWindow(session, { cwd: worktree, command: resolved.command, name: editorWindowLabel(absPath) });
17955
+ return true;
17956
+ }
17957
+ function editorWindowLabel(absPath) {
17958
+ const base = absPath.slice(absPath.lastIndexOf("/") + 1).trim();
17959
+ return base.length > 0 ? base : "edit";
17960
+ }
17961
+ var FILE_PLACEHOLDER = "{file}";
17962
+ var init_editor_launch = __esm(() => {
17963
+ init_repos();
17964
+ init_editor_prefs();
17965
+ init_client2();
17966
+ });
17967
+
17426
17968
  // src/tui/panes/filetree/git.ts
17427
17969
  import { spawn as nodeSpawn } from "child_process";
17428
17970
  async function runGit(args, cwd) {
@@ -17577,7 +18119,8 @@ var init_git2 = __esm(() => {
17577
18119
  const child = nodeSpawn("git", [...args], {
17578
18120
  cwd,
17579
18121
  shell: false,
17580
- stdio: ["ignore", "pipe", "pipe"]
18122
+ stdio: ["ignore", "pipe", "pipe"],
18123
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
17581
18124
  });
17582
18125
  let stdout = "";
17583
18126
  let stderr = "";
@@ -17628,6 +18171,7 @@ function useFileTreeBindings(opts) {
17628
18171
  opts.setTab(next);
17629
18172
  },
17630
18173
  "files.open": () => opts.openCurrent(),
18174
+ "files.edit": () => opts.editCurrent?.(),
17631
18175
  "files.mention": () => opts.mentionCurrent?.(),
17632
18176
  "files.createPR": () => opts.createPR?.(),
17633
18177
  "files.openExternal": () => opts.openExternal(),
@@ -17722,10 +18266,16 @@ function summarizeGitError(raw) {
17722
18266
  return raw.slice(colon + 2).trim() || "git command failed";
17723
18267
  return raw.trim() || "git command failed";
17724
18268
  }
18269
+ function truncatePathTail2(path11, max) {
18270
+ if (max <= 0 || path11.length <= max)
18271
+ return path11;
18272
+ return `\u2026${path11.slice(path11.length - Math.max(0, max - 1))}`;
18273
+ }
17725
18274
  function FileTree(props) {
17726
18275
  const {
17727
18276
  theme
17728
18277
  } = useTheme();
18278
+ const dims = useTerminalDimensions();
17729
18279
  const focusedAccessor = () => props.focused ? props.focused() : true;
17730
18280
  const [tab, setTab] = createSignal("all");
17731
18281
  const [cursorIndex, setCursorIndex] = createSignal(0);
@@ -17890,6 +18440,11 @@ function FileTree(props) {
17890
18440
  deleted
17891
18441
  };
17892
18442
  });
18443
+ const pathBudget = createMemo(() => {
18444
+ const w = statWidths();
18445
+ const stats = (w.added > 0 ? w.added + 1 : 0) + (w.deleted > 0 ? w.deleted + 1 : 0);
18446
+ return Math.max(8, dims().width - 6 - stats);
18447
+ });
17893
18448
  function moveDown() {
17894
18449
  const r = rows();
17895
18450
  if (r.length === 0)
@@ -17982,6 +18537,16 @@ function FileTree(props) {
17982
18537
  return;
17983
18538
  props.onMention?.(row.path);
17984
18539
  }
18540
+ function editCurrent() {
18541
+ const r = rows();
18542
+ const i = cursorIndex();
18543
+ if (i < 0 || i >= r.length)
18544
+ return;
18545
+ const row = r[i];
18546
+ if (!row || row.kind === "dir")
18547
+ return;
18548
+ props.onEditFile?.(row.path);
18549
+ }
17985
18550
  function refresh() {
17986
18551
  setRefreshTick((n) => n + 1);
17987
18552
  props.onRefresh?.();
@@ -18008,6 +18573,7 @@ function FileTree(props) {
18008
18573
  currentTab: tab,
18009
18574
  openCurrent,
18010
18575
  mentionCurrent,
18576
+ editCurrent,
18011
18577
  createPR: props.onCreatePR,
18012
18578
  openExternal,
18013
18579
  refresh,
@@ -18044,8 +18610,8 @@ function FileTree(props) {
18044
18610
  setProp(_el$, "flexGrow", 1);
18045
18611
  setProp(_el$, "paddingTop", 1);
18046
18612
  setProp(_el$, "paddingBottom", 1);
18047
- setProp(_el$, "paddingLeft", 2);
18048
- setProp(_el$, "paddingRight", 2);
18613
+ setProp(_el$, "paddingLeft", 0);
18614
+ setProp(_el$, "paddingRight", 0);
18049
18615
  insert(_el$, createComponent2(Show, {
18050
18616
  get when() {
18051
18617
  return props.onCreatePR;
@@ -18092,20 +18658,20 @@ function FileTree(props) {
18092
18658
  children: (t) => {
18093
18659
  const isActive = () => tab() === t;
18094
18660
  return (() => {
18095
- var _el$23 = createElement("text");
18096
- setProp(_el$23, "wrapMode", "none");
18097
- setProp(_el$23, "onMouseUp", () => setTab(t));
18098
- insert(_el$23, () => TAB_LABEL[t]);
18661
+ var _el$26 = createElement("text");
18662
+ setProp(_el$26, "wrapMode", "none");
18663
+ setProp(_el$26, "onMouseUp", () => setTab(t));
18664
+ insert(_el$26, () => TAB_LABEL[t]);
18099
18665
  effect((_p$) => {
18100
18666
  var _v$6 = isActive() ? theme.primary : theme.textMuted, _v$7 = isActive() ? TextAttributes11.BOLD : undefined;
18101
- _v$6 !== _p$.e && (_p$.e = setProp(_el$23, "fg", _v$6, _p$.e));
18102
- _v$7 !== _p$.t && (_p$.t = setProp(_el$23, "attributes", _v$7, _p$.t));
18667
+ _v$6 !== _p$.e && (_p$.e = setProp(_el$26, "fg", _v$6, _p$.e));
18668
+ _v$7 !== _p$.t && (_p$.t = setProp(_el$26, "attributes", _v$7, _p$.t));
18103
18669
  return _p$;
18104
18670
  }, {
18105
18671
  e: undefined,
18106
18672
  t: undefined
18107
18673
  });
18108
- return _el$23;
18674
+ return _el$26;
18109
18675
  })();
18110
18676
  }
18111
18677
  }));
@@ -18114,19 +18680,19 @@ function FileTree(props) {
18114
18680
  return props.cornerBadge?.();
18115
18681
  },
18116
18682
  children: (badge) => (() => {
18117
- var _el$24 = createElement("text");
18118
- setProp(_el$24, "wrapMode", "none");
18119
- insert(_el$24, () => badge().text);
18683
+ var _el$27 = createElement("text");
18684
+ setProp(_el$27, "wrapMode", "none");
18685
+ insert(_el$27, () => badge().text);
18120
18686
  effect((_p$) => {
18121
18687
  var _v$8 = badge().active ? theme.accent : theme.textMuted, _v$9 = badge().active ? TextAttributes11.BOLD : undefined;
18122
- _v$8 !== _p$.e && (_p$.e = setProp(_el$24, "fg", _v$8, _p$.e));
18123
- _v$9 !== _p$.t && (_p$.t = setProp(_el$24, "attributes", _v$9, _p$.t));
18688
+ _v$8 !== _p$.e && (_p$.e = setProp(_el$27, "fg", _v$8, _p$.e));
18689
+ _v$9 !== _p$.t && (_p$.t = setProp(_el$27, "attributes", _v$9, _p$.t));
18124
18690
  return _p$;
18125
18691
  }, {
18126
18692
  e: undefined,
18127
18693
  t: undefined
18128
18694
  });
18129
- return _el$24;
18695
+ return _el$27;
18130
18696
  })()
18131
18697
  }), null);
18132
18698
  insert(_el$, createComponent2(Show, {
@@ -18238,15 +18804,23 @@ function FileTree(props) {
18238
18804
  },
18239
18805
  children: (row, index) => {
18240
18806
  const isCursor = () => index() === cursorIndex();
18807
+ const bar = (() => {
18808
+ var _el$28 = createElement("text");
18809
+ setProp(_el$28, "wrapMode", "none");
18810
+ insert(_el$28, () => isCursor() ? "\u258C" : " ");
18811
+ effect((_$p) => setProp(_el$28, "fg", isCursor() ? theme.focusAccent : undefined, _$p));
18812
+ return _el$28;
18813
+ })();
18814
+ const rowBg = () => isCursor() ? theme.backgroundElement : undefined;
18241
18815
  if (row.kind === "dir") {
18242
18816
  const indent = " ".repeat(row.depth);
18243
18817
  const marker = row.expanded ? "\u25BE" : "\u25B8";
18244
18818
  return (() => {
18245
- var _el$25 = createElement("box"), _el$26 = createElement("text");
18246
- insertNode(_el$25, _el$26);
18247
- setProp(_el$25, "paddingLeft", 1);
18248
- setProp(_el$25, "paddingRight", 1);
18249
- setProp(_el$25, "onMouseUp", () => {
18819
+ var _el$29 = createElement("box"), _el$30 = createElement("box"), _el$31 = createElement("text");
18820
+ insertNode(_el$29, _el$30);
18821
+ setProp(_el$29, "flexDirection", "row");
18822
+ setProp(_el$29, "gap", 0);
18823
+ setProp(_el$29, "onMouseUp", () => {
18250
18824
  setCursorIndex(index());
18251
18825
  setExpandedDirs((prev) => {
18252
18826
  const next = new Set(prev);
@@ -18257,45 +18831,53 @@ function FileTree(props) {
18257
18831
  return next;
18258
18832
  });
18259
18833
  });
18260
- setProp(_el$26, "wrapMode", "none");
18261
- insert(_el$26, () => `${indent}${marker} ${row.name}/`);
18834
+ insert(_el$29, bar, _el$30);
18835
+ insertNode(_el$30, _el$31);
18836
+ setProp(_el$30, "flexGrow", 1);
18837
+ setProp(_el$30, "paddingRight", 1);
18838
+ setProp(_el$31, "wrapMode", "none");
18839
+ insert(_el$31, () => `${indent}${marker} ${row.name}/`);
18262
18840
  effect((_p$) => {
18263
- var _v$0 = isCursor() ? theme.primary : undefined, _v$1 = isCursor() ? theme.selectedListItemText : theme.textMuted, _v$10 = TextAttributes11.BOLD;
18264
- _v$0 !== _p$.e && (_p$.e = setProp(_el$25, "backgroundColor", _v$0, _p$.e));
18265
- _v$1 !== _p$.t && (_p$.t = setProp(_el$26, "fg", _v$1, _p$.t));
18266
- _v$10 !== _p$.a && (_p$.a = setProp(_el$26, "attributes", _v$10, _p$.a));
18841
+ var _v$0 = rowBg(), _v$1 = theme.textMuted, _v$10 = TextAttributes11.BOLD;
18842
+ _v$0 !== _p$.e && (_p$.e = setProp(_el$29, "backgroundColor", _v$0, _p$.e));
18843
+ _v$1 !== _p$.t && (_p$.t = setProp(_el$31, "fg", _v$1, _p$.t));
18844
+ _v$10 !== _p$.a && (_p$.a = setProp(_el$31, "attributes", _v$10, _p$.a));
18267
18845
  return _p$;
18268
18846
  }, {
18269
18847
  e: undefined,
18270
18848
  t: undefined,
18271
18849
  a: undefined
18272
18850
  });
18273
- return _el$25;
18851
+ return _el$29;
18274
18852
  })();
18275
18853
  }
18276
18854
  if (row.kind === "file") {
18277
18855
  const indent = " ".repeat(row.depth);
18278
18856
  return (() => {
18279
- var _el$27 = createElement("box"), _el$28 = createElement("text");
18280
- insertNode(_el$27, _el$28);
18281
- setProp(_el$27, "paddingLeft", 1);
18282
- setProp(_el$27, "paddingRight", 1);
18283
- setProp(_el$27, "onMouseUp", () => {
18857
+ var _el$32 = createElement("box"), _el$33 = createElement("box"), _el$34 = createElement("text");
18858
+ insertNode(_el$32, _el$33);
18859
+ setProp(_el$32, "flexDirection", "row");
18860
+ setProp(_el$32, "gap", 0);
18861
+ setProp(_el$32, "onMouseUp", () => {
18284
18862
  setCursorIndex(index());
18285
18863
  props.onOpenFile(row.path);
18286
18864
  });
18287
- setProp(_el$28, "wrapMode", "none");
18288
- insert(_el$28, () => `${indent} ${row.name}`);
18865
+ insert(_el$32, bar, _el$33);
18866
+ insertNode(_el$33, _el$34);
18867
+ setProp(_el$33, "flexGrow", 1);
18868
+ setProp(_el$33, "paddingRight", 1);
18869
+ setProp(_el$34, "wrapMode", "none");
18870
+ insert(_el$34, () => `${indent} ${row.name}`);
18289
18871
  effect((_p$) => {
18290
- var _v$11 = isCursor() ? theme.primary : undefined, _v$12 = isCursor() ? theme.selectedListItemText : theme.text;
18291
- _v$11 !== _p$.e && (_p$.e = setProp(_el$27, "backgroundColor", _v$11, _p$.e));
18292
- _v$12 !== _p$.t && (_p$.t = setProp(_el$28, "fg", _v$12, _p$.t));
18872
+ var _v$11 = rowBg(), _v$12 = theme.text;
18873
+ _v$11 !== _p$.e && (_p$.e = setProp(_el$32, "backgroundColor", _v$11, _p$.e));
18874
+ _v$12 !== _p$.t && (_p$.t = setProp(_el$34, "fg", _v$12, _p$.t));
18293
18875
  return _p$;
18294
18876
  }, {
18295
18877
  e: undefined,
18296
18878
  t: undefined
18297
18879
  });
18298
- return _el$27;
18880
+ return _el$32;
18299
18881
  })();
18300
18882
  }
18301
18883
  const tone = statusToken(row.status);
@@ -18317,64 +18899,84 @@ function FileTree(props) {
18317
18899
  const addedText = row.added == null ? " ".repeat(w.added) : `+${row.added}`.padStart(w.added);
18318
18900
  const deletedText = row.deleted == null ? " ".repeat(w.deleted) : `-${row.deleted}`.padStart(w.deleted);
18319
18901
  return (() => {
18320
- var _el$29 = createElement("box"), _el$30 = createElement("text"), _el$31 = createElement("text");
18321
- insertNode(_el$29, _el$30);
18322
- insertNode(_el$29, _el$31);
18323
- setProp(_el$29, "flexDirection", "row");
18324
- setProp(_el$29, "gap", 1);
18325
- setProp(_el$29, "paddingLeft", 1);
18326
- setProp(_el$29, "paddingRight", 1);
18327
- setProp(_el$29, "onMouseUp", () => {
18902
+ var _el$35 = createElement("box"), _el$36 = createElement("box"), _el$37 = createElement("text"), _el$38 = createElement("text");
18903
+ insertNode(_el$35, _el$36);
18904
+ setProp(_el$35, "flexDirection", "row");
18905
+ setProp(_el$35, "gap", 0);
18906
+ setProp(_el$35, "onMouseUp", () => {
18328
18907
  setCursorIndex(index());
18329
18908
  props.onOpenFile(row.path);
18330
18909
  });
18331
- setProp(_el$30, "wrapMode", "none");
18332
- insert(_el$30, () => row.status);
18333
- setProp(_el$31, "wrapMode", "none");
18334
- setProp(_el$31, "flexGrow", 1);
18335
- insert(_el$31, () => row.path);
18336
- insert(_el$29, createComponent2(Show, {
18910
+ insert(_el$35, bar, _el$36);
18911
+ insertNode(_el$36, _el$37);
18912
+ insertNode(_el$36, _el$38);
18913
+ setProp(_el$36, "flexDirection", "row");
18914
+ setProp(_el$36, "flexGrow", 1);
18915
+ setProp(_el$36, "gap", 1);
18916
+ setProp(_el$36, "paddingRight", 1);
18917
+ setProp(_el$37, "wrapMode", "none");
18918
+ insert(_el$37, () => row.status);
18919
+ setProp(_el$38, "wrapMode", "none");
18920
+ setProp(_el$38, "flexGrow", 1);
18921
+ insert(_el$38, () => truncatePathTail2(row.path, pathBudget()));
18922
+ insert(_el$36, createComponent2(Show, {
18337
18923
  get when() {
18338
18924
  return w.added > 0;
18339
18925
  },
18340
18926
  get children() {
18341
- var _el$32 = createElement("text");
18342
- setProp(_el$32, "wrapMode", "none");
18343
- insert(_el$32, addedText);
18344
- effect((_$p) => setProp(_el$32, "fg", isCursor() ? theme.selectedListItemText : theme.success, _$p));
18345
- return _el$32;
18927
+ var _el$39 = createElement("text");
18928
+ setProp(_el$39, "wrapMode", "none");
18929
+ insert(_el$39, addedText);
18930
+ effect((_$p) => setProp(_el$39, "fg", theme.success, _$p));
18931
+ return _el$39;
18346
18932
  }
18347
18933
  }), null);
18348
- insert(_el$29, createComponent2(Show, {
18934
+ insert(_el$36, createComponent2(Show, {
18349
18935
  get when() {
18350
18936
  return w.deleted > 0;
18351
18937
  },
18352
18938
  get children() {
18353
- var _el$33 = createElement("text");
18354
- setProp(_el$33, "wrapMode", "none");
18355
- insert(_el$33, deletedText);
18356
- effect((_$p) => setProp(_el$33, "fg", isCursor() ? theme.selectedListItemText : theme.error, _$p));
18357
- return _el$33;
18939
+ var _el$40 = createElement("text");
18940
+ setProp(_el$40, "wrapMode", "none");
18941
+ insert(_el$40, deletedText);
18942
+ effect((_$p) => setProp(_el$40, "fg", theme.error, _$p));
18943
+ return _el$40;
18358
18944
  }
18359
18945
  }), null);
18360
18946
  effect((_p$) => {
18361
- var _v$13 = isCursor() ? theme.primary : undefined, _v$14 = isCursor() ? theme.selectedListItemText : statusColor(), _v$15 = isCursor() ? theme.selectedListItemText : theme.text;
18362
- _v$13 !== _p$.e && (_p$.e = setProp(_el$29, "backgroundColor", _v$13, _p$.e));
18363
- _v$14 !== _p$.t && (_p$.t = setProp(_el$30, "fg", _v$14, _p$.t));
18364
- _v$15 !== _p$.a && (_p$.a = setProp(_el$31, "fg", _v$15, _p$.a));
18947
+ var _v$13 = rowBg(), _v$14 = statusColor(), _v$15 = theme.text;
18948
+ _v$13 !== _p$.e && (_p$.e = setProp(_el$35, "backgroundColor", _v$13, _p$.e));
18949
+ _v$14 !== _p$.t && (_p$.t = setProp(_el$37, "fg", _v$14, _p$.t));
18950
+ _v$15 !== _p$.a && (_p$.a = setProp(_el$38, "fg", _v$15, _p$.a));
18365
18951
  return _p$;
18366
18952
  }, {
18367
18953
  e: undefined,
18368
18954
  t: undefined,
18369
18955
  a: undefined
18370
18956
  });
18371
- return _el$29;
18957
+ return _el$35;
18372
18958
  })();
18373
18959
  }
18374
18960
  }));
18375
18961
  return _el$22;
18376
18962
  }
18377
18963
  }), null);
18964
+ insert(_el$, createComponent2(Show, {
18965
+ get when() {
18966
+ return props.worktreePath() != null;
18967
+ },
18968
+ get children() {
18969
+ var _el$23 = createElement("box"), _el$24 = createElement("text");
18970
+ insertNode(_el$23, _el$24);
18971
+ setProp(_el$23, "flexDirection", "row");
18972
+ setProp(_el$23, "paddingTop", 1);
18973
+ setProp(_el$23, "flexShrink", 0);
18974
+ insertNode(_el$24, createTextNode(`\u21B5 preview \xB7 e edit`));
18975
+ setProp(_el$24, "wrapMode", "none");
18976
+ effect((_$p) => setProp(_el$24, "fg", theme.textMuted, _$p));
18977
+ return _el$23;
18978
+ }
18979
+ }), null);
18378
18980
  return _el$;
18379
18981
  })();
18380
18982
  }
@@ -18389,6 +18991,7 @@ var init_FileTree = __esm(() => {
18389
18991
  init_solid();
18390
18992
  init_solid();
18391
18993
  init_solid();
18994
+ init_solid();
18392
18995
  init_dev();
18393
18996
  init_theme2();
18394
18997
  init_git2();
@@ -18409,15 +19012,16 @@ var init_filetree = __esm(() => {
18409
19012
  });
18410
19013
 
18411
19014
  // src/tui/ops/pr-prompt.ts
18412
- import { spawnSync as spawnSync10 } from "child_process";
19015
+ import { spawnSync as spawnSync9 } from "child_process";
18413
19016
  import { promises as fs4 } from "fs";
18414
19017
  import path11 from "path";
18415
19018
  function git2(cwd, args) {
18416
19019
  try {
18417
- const out = spawnSync10("git", args.slice(), {
19020
+ const out = spawnSync9("git", args.slice(), {
18418
19021
  cwd,
18419
19022
  encoding: "utf8",
18420
- timeout: GIT_TIMEOUT_MS
19023
+ timeout: GIT_TIMEOUT_MS,
19024
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
18421
19025
  });
18422
19026
  if (out.error)
18423
19027
  return null;
@@ -18618,6 +19222,17 @@ function OpsShell(props) {
18618
19222
  name: basename5(rel)
18619
19223
  });
18620
19224
  }
19225
+ function openEditor(rel) {
19226
+ if (!props.taskId) {
19227
+ openPreview(rel);
19228
+ return;
19229
+ }
19230
+ const abs = `${props.worktree}/${rel}`;
19231
+ openInEditor(tmuxSessionName(props.taskId), props.worktree, abs).then((launched) => {
19232
+ if (!launched)
19233
+ openPreview(rel);
19234
+ });
19235
+ }
18621
19236
  function injectMention(rel) {
18622
19237
  if (!props.targetPane)
18623
19238
  return;
@@ -18638,6 +19253,7 @@ function OpsShell(props) {
18638
19253
  worktreePath: () => props.worktree,
18639
19254
  focused: () => true,
18640
19255
  onOpenFile: openPreview,
19256
+ onEditFile: openEditor,
18641
19257
  onMention: injectMention,
18642
19258
  onCreatePR: () => void createPR(),
18643
19259
  cornerBadge,
@@ -18713,7 +19329,11 @@ async function gitDiff(worktree, relPath) {
18713
19329
  cwd: worktree,
18714
19330
  stdin: "ignore",
18715
19331
  stdout: "pipe",
18716
- stderr: "ignore"
19332
+ stderr: "ignore",
19333
+ env: {
19334
+ ...process.env,
19335
+ GIT_OPTIONAL_LOCKS: "0"
19336
+ }
18717
19337
  });
18718
19338
  const text = await new Response(proc.stdout).text();
18719
19339
  await proc.exited;
@@ -18940,6 +19560,7 @@ var init_host5 = __esm(() => {
18940
19560
  init_turn_detector();
18941
19561
  init_activity();
18942
19562
  init_client2();
19563
+ init_editor_launch();
18943
19564
  init_solid();
18944
19565
  init_dev();
18945
19566
  init_theme2();
@@ -19855,7 +20476,7 @@ function repoBasename2(repo) {
19855
20476
  }
19856
20477
 
19857
20478
  // src/tui/component/top-bar.tsx
19858
- import { spawnSync as spawnSync11 } from "child_process";
20479
+ import { spawnSync as spawnSync10 } from "child_process";
19859
20480
  import { TextAttributes as TextAttributes16 } from "@opentui/core";
19860
20481
  function TopBar(props) {
19861
20482
  const {
@@ -19884,7 +20505,7 @@ function TopBar(props) {
19884
20505
  `);
19885
20506
  process.stderr.write(`running: ${UPDATE_COMMAND}
19886
20507
  `);
19887
- const result = spawnSync11("sh", ["-c", UPDATE_COMMAND], {
20508
+ const result = spawnSync10("sh", ["-c", UPDATE_COMMAND], {
19888
20509
  stdio: "inherit"
19889
20510
  });
19890
20511
  if (result.error) {