@sma1lboy/kobe 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -41,6 +41,7 @@ var init_env = () => {};
41
41
  var exports_repos = {};
42
42
  __export(exports_repos, {
43
43
  statePath: () => statePath,
44
+ sameRepoToplevel: () => sameRepoToplevel,
44
45
  resolveRepoRoot: () => resolveRepoRoot,
45
46
  removeSavedRepo: () => removeSavedRepo,
46
47
  normalizeSavedRepos: () => normalizeSavedRepos,
@@ -67,6 +68,19 @@ function resolveRepoRoot(absPath) {
67
68
  } catch {}
68
69
  return top;
69
70
  }
71
+ function sameRepoToplevel(a, b) {
72
+ if (a === b)
73
+ return true;
74
+ const topA = resolveRepoRoot(a);
75
+ const topB = resolveRepoRoot(b);
76
+ if (topA === topB)
77
+ return true;
78
+ try {
79
+ return realpathSync(topA) === realpathSync(topB);
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
70
84
  function statePath() {
71
85
  return kvStatePath();
72
86
  }
@@ -5494,6 +5508,38 @@ var init_keybindings = __esm(() => {
5494
5508
  description: "Previous chat tab",
5495
5509
  hint: { keys: "ctrl+[", label: "prev tab" }
5496
5510
  },
5511
+ {
5512
+ id: "chat.question.nav",
5513
+ scope: "workspace",
5514
+ keys: ["j", "k", "down", "up"],
5515
+ category: "Workspace",
5516
+ description: "Move highlight in question picker",
5517
+ hint: { keys: "j/k", label: "pick" }
5518
+ },
5519
+ {
5520
+ id: "chat.question.toggle",
5521
+ scope: "workspace",
5522
+ keys: ["space"],
5523
+ category: "Workspace",
5524
+ description: "Toggle highlighted option in question picker",
5525
+ hint: { keys: "space", label: "toggle" }
5526
+ },
5527
+ {
5528
+ id: "chat.question.submit",
5529
+ scope: "workspace",
5530
+ keys: ["return"],
5531
+ category: "Workspace",
5532
+ description: "Advance / submit question picker",
5533
+ hint: { keys: "enter", label: "submit" }
5534
+ },
5535
+ {
5536
+ id: "chat.question.pick-number",
5537
+ scope: "workspace",
5538
+ keys: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
5539
+ category: "Workspace",
5540
+ description: "Pick option by number in question picker",
5541
+ hint: { keys: "1-9", label: "pick" }
5542
+ },
5497
5543
  {
5498
5544
  id: "files.nav",
5499
5545
  scope: "files",
@@ -5601,8 +5647,8 @@ var init_keys = __esm(() => {
5601
5647
  });
5602
5648
 
5603
5649
  // src/tui/panes/filetree/FileTree.tsx
5604
- import { TextAttributes as TextAttributes3 } from "@opentui/core";
5605
5650
  import { watch } from "fs";
5651
+ import { TextAttributes as TextAttributes3 } from "@opentui/core";
5606
5652
  function statusToken(s) {
5607
5653
  switch (s) {
5608
5654
  case "M":
@@ -6543,7 +6589,7 @@ function FileLine(props) {
6543
6589
  insertNode(_el$3, _el$4);
6544
6590
  setProp(_el$3, "paddingLeft", 1);
6545
6591
  setProp(_el$3, "paddingRight", 1);
6546
- setProp(_el$4, "wrapMode", "none");
6592
+ setProp(_el$4, "wrapMode", "word");
6547
6593
  insert(_el$4, () => props.text || " ");
6548
6594
  effect((_$p) => setProp(_el$4, "fg", theme.text, _$p));
6549
6595
  return _el$3;
@@ -8825,7 +8871,7 @@ var init_package = __esm(() => {
8825
8871
  package_default = {
8826
8872
  $schema: "https://json.schemastore.org/package.json",
8827
8873
  name: "@sma1lboy/kobe",
8828
- version: "0.5.3",
8874
+ version: "0.5.5",
8829
8875
  description: "TUI orchestrator for Claude Code (codename)",
8830
8876
  type: "module",
8831
8877
  packageManager: "bun@1.3.13",
@@ -8853,6 +8899,7 @@ var init_package = __esm(() => {
8853
8899
  "dev:test": "bun run scripts/dev-fixture.ts && KOBE_DEV=1 KOBE_TEST_ENGINE=dev-fake KOBE_HOME_DIR=$PWD/.dev-fixture/home bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
8854
8900
  "dev:test:reset": "bun run scripts/dev-fixture.ts --reset",
8855
8901
  build: "bun run scripts/build.ts",
8902
+ compile: "bun run scripts/compile.ts",
8856
8903
  typecheck: "tsc --noEmit",
8857
8904
  test: "vitest run --passWithNoTests",
8858
8905
  "test:behavior": "vitest run test/behavior --passWithNoTests",
@@ -10047,8 +10094,12 @@ async function connectOrStartDaemon() {
10047
10094
  const client = new KobeDaemonClient(socketPath);
10048
10095
  if (await canConnect(client))
10049
10096
  return client;
10050
- const entry = resolveKobedEntry();
10051
- const child = spawn2(process.execPath, [entry, "start"], {
10097
+ const { entry, runWithBun } = resolveKobedEntry();
10098
+ const child = runWithBun ? spawn2(process.execPath, [entry, "start"], {
10099
+ detached: true,
10100
+ stdio: "ignore",
10101
+ env: process.env
10102
+ }) : spawn2(entry, ["start"], {
10052
10103
  detached: true,
10053
10104
  stdio: "ignore",
10054
10105
  env: process.env
@@ -10080,14 +10131,23 @@ async function canConnect(client) {
10080
10131
  }
10081
10132
  function resolveKobedEntry() {
10082
10133
  const here = fileURLToPath(import.meta.url);
10134
+ if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
10135
+ const exeDir = dirname4(process.execPath);
10136
+ const ext = process.platform === "win32" ? ".exe" : "";
10137
+ const sibling = join8(exeDir, `kobed${ext}`);
10138
+ if (!existsSync4(sibling)) {
10139
+ throw new Error(`kobe: standalone build expected sibling kobed binary at ${sibling}; extract the full release tarball.`);
10140
+ }
10141
+ return { entry: sibling, runWithBun: false };
10142
+ }
10083
10143
  const dir = dirname4(here);
10084
10144
  const sourceEntry = resolve2(dir, "../bin/kobed.ts");
10085
10145
  if (existsSync4(sourceEntry))
10086
- return sourceEntry;
10146
+ return { entry: sourceEntry, runWithBun: true };
10087
10147
  const distEntry = join8(dirname4(process.argv[1] ?? here), "bin", "kobed.js");
10088
10148
  if (existsSync4(distEntry))
10089
- return distEntry;
10090
- return join8(process.cwd(), "dist", "bin", "kobed.js");
10149
+ return { entry: distEntry, runWithBun: true };
10150
+ return { entry: join8(process.cwd(), "dist", "bin", "kobed.js"), runWithBun: true };
10091
10151
  }
10092
10152
  var init_daemon_process = __esm(() => {
10093
10153
  init_paths2();
@@ -10628,13 +10688,13 @@ class Orchestrator {
10628
10688
  throw new Error("ensureMainTask: repo is required");
10629
10689
  const normalized = resolveRepoRoot(repo);
10630
10690
  const all = this.store.list();
10631
- const candidates = all.filter((t) => t.kind === "main" && (t.repo === normalized || resolveRepoRoot(t.repo) === normalized));
10691
+ const candidates = all.filter((t) => t.kind === "main" && sameRepoToplevel(t.repo, normalized));
10632
10692
  const winner = candidates.find((t) => t.repo === normalized) ?? candidates[0];
10633
10693
  if (winner) {
10634
10694
  for (const dup of candidates) {
10635
10695
  if (dup.id !== winner.id) {
10636
10696
  try {
10637
- await this.deleteTask(dup.id);
10697
+ await this.store.remove(dup.id);
10638
10698
  } catch (err) {
10639
10699
  console.error(`[kobe orchestrator] ensureMainTask: failed to remove duplicate ${dup.id}:`, err);
10640
10700
  }
@@ -11035,6 +11095,15 @@ class Orchestrator {
11035
11095
  await this.store.update(task.id, { tabs });
11036
11096
  return tab;
11037
11097
  }
11098
+ async clearTab(id, tabId) {
11099
+ const task = this.requireTask(id);
11100
+ if (!task.tabs.some((t) => t.id === tabId)) {
11101
+ throw new Error(`clearTab: tab ${tabId} not found on task ${task.id}`);
11102
+ }
11103
+ await this.stopTab(task.id, tabId);
11104
+ await this.updateTab(task.id, tabId, { sessionId: null });
11105
+ this.dispatchEvent(task.id, tabId, { type: "chat.tab.cleared" });
11106
+ }
11038
11107
  async closeTab(id, tabId) {
11039
11108
  const task = this.requireTask(id);
11040
11109
  if (task.tabs.length <= 1) {
@@ -11408,6 +11477,9 @@ class RemoteOrchestrator {
11408
11477
  const res = await this.client.request("chat.tab.close", { taskId, tabId });
11409
11478
  return res.nextActive;
11410
11479
  }
11480
+ async clearTab(taskId, tabId) {
11481
+ await this.client.request("chat.tab.clear", { taskId, tabId });
11482
+ }
11411
11483
  async setActiveTab(taskId, tabId) {
11412
11484
  await this.client.request("chat.tab.activate", { taskId, tabId });
11413
11485
  }
@@ -11949,7 +12021,7 @@ function SettingsDialog(props) {
11949
12021
  }
11950
12022
  function bodyRowCount() {
11951
12023
  if (section() === "general")
11952
- return themeNames().length + 1 + FOCUS_ACCENT_SLOTS.length;
12024
+ return themeNames().length + 1 + FOCUS_ACCENT_SLOTS.length + 2;
11953
12025
  if (section() === "dev")
11954
12026
  return devRowCount();
11955
12027
  return 0;
@@ -11966,6 +12038,30 @@ function SettingsDialog(props) {
11966
12038
  return null;
11967
12039
  return i;
11968
12040
  }
12041
+ function toastRowIndex() {
12042
+ return themeNames().length + 1 + FOCUS_ACCENT_SLOTS.length;
12043
+ }
12044
+ function soundRowIndex() {
12045
+ return toastRowIndex() + 1;
12046
+ }
12047
+ function isToastRow() {
12048
+ return section() === "general" && bodyRow() === toastRowIndex();
12049
+ }
12050
+ function isSoundRow() {
12051
+ return section() === "general" && bodyRow() === soundRowIndex();
12052
+ }
12053
+ function toastEnabled() {
12054
+ return props.kv.get("notifications.toast.enabled", true) !== false;
12055
+ }
12056
+ function soundEnabled() {
12057
+ return props.kv.get("notifications.sound.enabled", true) !== false;
12058
+ }
12059
+ function toggleToast() {
12060
+ props.kv.set("notifications.toast.enabled", !toastEnabled());
12061
+ }
12062
+ function toggleSound() {
12063
+ props.kv.set("notifications.sound.enabled", !soundEnabled());
12064
+ }
11969
12065
  function moveCursor(delta) {
11970
12066
  if (level() === "sidebar") {
11971
12067
  const next2 = (cursor() + delta + SECTIONS.length) % SECTIONS.length;
@@ -12108,6 +12204,14 @@ function SettingsDialog(props) {
12108
12204
  themeCtx.setFocusAccent(slot);
12109
12205
  return;
12110
12206
  }
12207
+ if (isToastRow()) {
12208
+ toggleToast();
12209
+ return;
12210
+ }
12211
+ if (isSoundRow()) {
12212
+ toggleSound();
12213
+ return;
12214
+ }
12111
12215
  const name = themeNames()[bodyRow()];
12112
12216
  if (name)
12113
12217
  themeCtx.set(name);
@@ -12128,10 +12232,10 @@ function SettingsDialog(props) {
12128
12232
  ]
12129
12233
  }));
12130
12234
  return (() => {
12131
- var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("box"), _el$9 = createElement("box"), _el$36 = createElement("box"), _el$37 = createElement("text");
12235
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("box"), _el$9 = createElement("box"), _el$47 = createElement("box"), _el$48 = createElement("text");
12132
12236
  insertNode(_el$, _el$2);
12133
12237
  insertNode(_el$, _el$7);
12134
- insertNode(_el$, _el$36);
12238
+ insertNode(_el$, _el$47);
12135
12239
  setProp(_el$, "paddingLeft", 2);
12136
12240
  setProp(_el$, "paddingRight", 2);
12137
12241
  setProp(_el$, "paddingBottom", 1);
@@ -12157,28 +12261,28 @@ function SettingsDialog(props) {
12157
12261
  const isSection = () => i() === cursor();
12158
12262
  const isSidebarFocused = () => isSection() && level() === "sidebar";
12159
12263
  return (() => {
12160
- var _el$39 = createElement("box"), _el$40 = createElement("text");
12161
- insertNode(_el$39, _el$40);
12162
- setProp(_el$39, "paddingLeft", 1);
12163
- setProp(_el$39, "paddingRight", 1);
12164
- setProp(_el$39, "onMouseUp", () => {
12264
+ var _el$50 = createElement("box"), _el$51 = createElement("text");
12265
+ insertNode(_el$50, _el$51);
12266
+ setProp(_el$50, "paddingLeft", 1);
12267
+ setProp(_el$50, "paddingRight", 1);
12268
+ setProp(_el$50, "onMouseUp", () => {
12165
12269
  switchSection(s.id);
12166
12270
  setLevel("sidebar");
12167
12271
  });
12168
- setProp(_el$40, "wrapMode", "none");
12169
- insert(_el$40, () => s.label);
12272
+ setProp(_el$51, "wrapMode", "none");
12273
+ insert(_el$51, () => s.label);
12170
12274
  effect((_p$) => {
12171
- var _v$21 = isSidebarFocused() ? theme.primary : undefined, _v$22 = isSidebarFocused() ? theme.selectedListItemText : isSection() ? theme.accent : theme.textMuted, _v$23 = isSection() ? TextAttributes8.BOLD : undefined;
12172
- _v$21 !== _p$.e && (_p$.e = setProp(_el$39, "backgroundColor", _v$21, _p$.e));
12173
- _v$22 !== _p$.t && (_p$.t = setProp(_el$40, "fg", _v$22, _p$.t));
12174
- _v$23 !== _p$.a && (_p$.a = setProp(_el$40, "attributes", _v$23, _p$.a));
12275
+ var _v$30 = isSidebarFocused() ? theme.primary : undefined, _v$31 = isSidebarFocused() ? theme.selectedListItemText : isSection() ? theme.accent : theme.textMuted, _v$32 = isSection() ? TextAttributes8.BOLD : undefined;
12276
+ _v$30 !== _p$.e && (_p$.e = setProp(_el$50, "backgroundColor", _v$30, _p$.e));
12277
+ _v$31 !== _p$.t && (_p$.t = setProp(_el$51, "fg", _v$31, _p$.t));
12278
+ _v$32 !== _p$.a && (_p$.a = setProp(_el$51, "attributes", _v$32, _p$.a));
12175
12279
  return _p$;
12176
12280
  }, {
12177
12281
  e: undefined,
12178
12282
  t: undefined,
12179
12283
  a: undefined
12180
12284
  });
12181
- return _el$39;
12285
+ return _el$50;
12182
12286
  })();
12183
12287
  }
12184
12288
  }));
@@ -12191,12 +12295,13 @@ function SettingsDialog(props) {
12191
12295
  return section() === "general";
12192
12296
  },
12193
12297
  get children() {
12194
- var _el$0 = createElement("box"), _el$1 = createElement("text"), _el$11 = createElement("text"), _el$13 = createElement("box"), _el$14 = createElement("box"), _el$15 = createElement("text"), _el$17 = createElement("text"), _el$19 = createElement("box"), _el$20 = createElement("text"), _el$21 = createElement("box"), _el$22 = createElement("text"), _el$24 = createElement("text");
12298
+ var _el$0 = createElement("box"), _el$1 = createElement("text"), _el$11 = createElement("text"), _el$13 = createElement("box"), _el$14 = createElement("box"), _el$15 = createElement("text"), _el$17 = createElement("text"), _el$19 = createElement("box"), _el$20 = createElement("text"), _el$21 = createElement("box"), _el$22 = createElement("text"), _el$24 = createElement("text"), _el$26 = createElement("box"), _el$27 = createElement("text"), _el$29 = createElement("text"), _el$31 = createElement("box"), _el$32 = createElement("text"), _el$33 = createTextNode(` Toast`), _el$34 = createElement("box"), _el$35 = createElement("text"), _el$36 = createTextNode(` Sound`);
12195
12299
  insertNode(_el$0, _el$1);
12196
12300
  insertNode(_el$0, _el$11);
12197
12301
  insertNode(_el$0, _el$13);
12198
12302
  insertNode(_el$0, _el$14);
12199
12303
  insertNode(_el$0, _el$21);
12304
+ insertNode(_el$0, _el$26);
12200
12305
  setProp(_el$0, "flexDirection", "column");
12201
12306
  setProp(_el$0, "gap", 1);
12202
12307
  insertNode(_el$1, createTextNode(`Theme`));
@@ -12211,33 +12316,33 @@ function SettingsDialog(props) {
12211
12316
  const isCursor = () => level() === "body" && bodyRow() === i();
12212
12317
  const isSelected = () => name === themeCtx.selected;
12213
12318
  return (() => {
12214
- var _el$41 = createElement("box"), _el$42 = createElement("text");
12215
- insertNode(_el$41, _el$42);
12216
- setProp(_el$41, "flexDirection", "row");
12217
- setProp(_el$41, "gap", 1);
12218
- setProp(_el$41, "paddingLeft", 1);
12219
- setProp(_el$41, "paddingRight", 1);
12220
- setProp(_el$41, "onMouseUp", () => {
12319
+ var _el$52 = createElement("box"), _el$53 = createElement("text");
12320
+ insertNode(_el$52, _el$53);
12321
+ setProp(_el$52, "flexDirection", "row");
12322
+ setProp(_el$52, "gap", 1);
12323
+ setProp(_el$52, "paddingLeft", 1);
12324
+ setProp(_el$52, "paddingRight", 1);
12325
+ setProp(_el$52, "onMouseUp", () => {
12221
12326
  setLevel("body");
12222
12327
  setBodyRow(i());
12223
12328
  setThemeCursor(i());
12224
12329
  themeCtx.set(name);
12225
12330
  });
12226
- setProp(_el$42, "wrapMode", "none");
12227
- insert(_el$42, () => isSelected() ? "\u25CF " : " ", null);
12228
- insert(_el$42, name, null);
12331
+ setProp(_el$53, "wrapMode", "none");
12332
+ insert(_el$53, () => isSelected() ? "\u25CF " : " ", null);
12333
+ insert(_el$53, name, null);
12229
12334
  effect((_p$) => {
12230
- var _v$24 = isCursor() ? theme.primary : undefined, _v$25 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.accent : theme.text, _v$26 = isCursor() || isSelected() ? TextAttributes8.BOLD : undefined;
12231
- _v$24 !== _p$.e && (_p$.e = setProp(_el$41, "backgroundColor", _v$24, _p$.e));
12232
- _v$25 !== _p$.t && (_p$.t = setProp(_el$42, "fg", _v$25, _p$.t));
12233
- _v$26 !== _p$.a && (_p$.a = setProp(_el$42, "attributes", _v$26, _p$.a));
12335
+ var _v$33 = isCursor() ? theme.primary : undefined, _v$34 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.accent : theme.text, _v$35 = isCursor() || isSelected() ? TextAttributes8.BOLD : undefined;
12336
+ _v$33 !== _p$.e && (_p$.e = setProp(_el$52, "backgroundColor", _v$33, _p$.e));
12337
+ _v$34 !== _p$.t && (_p$.t = setProp(_el$53, "fg", _v$34, _p$.t));
12338
+ _v$35 !== _p$.a && (_p$.a = setProp(_el$53, "attributes", _v$35, _p$.a));
12234
12339
  return _p$;
12235
12340
  }, {
12236
12341
  e: undefined,
12237
12342
  t: undefined,
12238
12343
  a: undefined
12239
12344
  });
12240
- return _el$41;
12345
+ return _el$52;
12241
12346
  })();
12242
12347
  }
12243
12348
  }));
@@ -12276,37 +12381,73 @@ function SettingsDialog(props) {
12276
12381
  const isCursor = () => level() === "body" && bodyRow() === rowIndex();
12277
12382
  const isSelected = () => themeCtx.focusAccent === slot;
12278
12383
  return (() => {
12279
- var _el$43 = createElement("box"), _el$44 = createElement("text");
12280
- insertNode(_el$43, _el$44);
12281
- setProp(_el$43, "flexDirection", "row");
12282
- setProp(_el$43, "gap", 1);
12283
- setProp(_el$43, "paddingLeft", 1);
12284
- setProp(_el$43, "paddingRight", 1);
12285
- setProp(_el$43, "onMouseUp", () => {
12384
+ var _el$54 = createElement("box"), _el$55 = createElement("text");
12385
+ insertNode(_el$54, _el$55);
12386
+ setProp(_el$54, "flexDirection", "row");
12387
+ setProp(_el$54, "gap", 1);
12388
+ setProp(_el$54, "paddingLeft", 1);
12389
+ setProp(_el$54, "paddingRight", 1);
12390
+ setProp(_el$54, "onMouseUp", () => {
12286
12391
  setLevel("body");
12287
12392
  setBodyRow(rowIndex());
12288
12393
  themeCtx.setFocusAccent(slot);
12289
12394
  });
12290
- setProp(_el$44, "wrapMode", "none");
12291
- insert(_el$44, () => isSelected() ? "\u25CF " : " ", null);
12292
- insert(_el$44, () => FOCUS_ACCENT_LABEL[slot], null);
12395
+ setProp(_el$55, "wrapMode", "none");
12396
+ insert(_el$55, () => isSelected() ? "\u25CF " : " ", null);
12397
+ insert(_el$55, () => FOCUS_ACCENT_LABEL[slot], null);
12293
12398
  effect((_p$) => {
12294
- var _v$27 = isCursor() ? theme.primary : undefined, _v$28 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.focusAccent : theme.text, _v$29 = isCursor() || isSelected() ? TextAttributes8.BOLD : undefined;
12295
- _v$27 !== _p$.e && (_p$.e = setProp(_el$43, "backgroundColor", _v$27, _p$.e));
12296
- _v$28 !== _p$.t && (_p$.t = setProp(_el$44, "fg", _v$28, _p$.t));
12297
- _v$29 !== _p$.a && (_p$.a = setProp(_el$44, "attributes", _v$29, _p$.a));
12399
+ var _v$36 = isCursor() ? theme.primary : undefined, _v$37 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.focusAccent : theme.text, _v$38 = isCursor() || isSelected() ? TextAttributes8.BOLD : undefined;
12400
+ _v$36 !== _p$.e && (_p$.e = setProp(_el$54, "backgroundColor", _v$36, _p$.e));
12401
+ _v$37 !== _p$.t && (_p$.t = setProp(_el$55, "fg", _v$37, _p$.t));
12402
+ _v$38 !== _p$.a && (_p$.a = setProp(_el$55, "attributes", _v$38, _p$.a));
12298
12403
  return _p$;
12299
12404
  }, {
12300
12405
  e: undefined,
12301
12406
  t: undefined,
12302
12407
  a: undefined
12303
12408
  });
12304
- return _el$43;
12409
+ return _el$54;
12305
12410
  })();
12306
12411
  }
12307
12412
  }), null);
12413
+ insertNode(_el$26, _el$27);
12414
+ insertNode(_el$26, _el$29);
12415
+ insertNode(_el$26, _el$31);
12416
+ insertNode(_el$26, _el$34);
12417
+ setProp(_el$26, "flexDirection", "column");
12418
+ setProp(_el$26, "gap", 0);
12419
+ setProp(_el$26, "paddingTop", 1);
12420
+ insertNode(_el$27, createTextNode(`Notifications`));
12421
+ insertNode(_el$29, createTextNode(`Fired when a background chat tab finishes or pauses on an approval. Toast = bottom-right popup; Sound = terminal bell + chime. Tab-chip unread dot is always on.`));
12422
+ setProp(_el$29, "wrapMode", "word");
12423
+ insertNode(_el$31, _el$32);
12424
+ setProp(_el$31, "flexDirection", "row");
12425
+ setProp(_el$31, "gap", 1);
12426
+ setProp(_el$31, "paddingLeft", 1);
12427
+ setProp(_el$31, "paddingRight", 1);
12428
+ setProp(_el$31, "onMouseUp", () => {
12429
+ setLevel("body");
12430
+ setBodyRow(toastRowIndex());
12431
+ toggleToast();
12432
+ });
12433
+ insertNode(_el$32, _el$33);
12434
+ setProp(_el$32, "wrapMode", "none");
12435
+ insert(_el$32, () => toastEnabled() ? "[x]" : "[ ]", _el$33);
12436
+ insertNode(_el$34, _el$35);
12437
+ setProp(_el$34, "flexDirection", "row");
12438
+ setProp(_el$34, "gap", 1);
12439
+ setProp(_el$34, "paddingLeft", 1);
12440
+ setProp(_el$34, "paddingRight", 1);
12441
+ setProp(_el$34, "onMouseUp", () => {
12442
+ setLevel("body");
12443
+ setBodyRow(soundRowIndex());
12444
+ toggleSound();
12445
+ });
12446
+ insertNode(_el$35, _el$36);
12447
+ setProp(_el$35, "wrapMode", "none");
12448
+ insert(_el$35, () => soundEnabled() ? "[x]" : "[ ]", _el$36);
12308
12449
  effect((_p$) => {
12309
- var _v$ = theme.text, _v$2 = TextAttributes8.BOLD, _v$3 = theme.textMuted, _v$4 = theme.text, _v$5 = TextAttributes8.BOLD, _v$6 = theme.textMuted, _v$7 = isTransparentRow() ? theme.primary : undefined, _v$8 = isTransparentRow() ? theme.selectedListItemText : themeCtx.transparentBackground ? theme.accent : theme.textMuted, _v$9 = TextAttributes8.BOLD, _v$0 = theme.text, _v$1 = TextAttributes8.BOLD, _v$10 = theme.textMuted;
12450
+ var _v$ = theme.text, _v$2 = TextAttributes8.BOLD, _v$3 = theme.textMuted, _v$4 = theme.text, _v$5 = TextAttributes8.BOLD, _v$6 = theme.textMuted, _v$7 = isTransparentRow() ? theme.primary : undefined, _v$8 = isTransparentRow() ? theme.selectedListItemText : themeCtx.transparentBackground ? theme.accent : theme.textMuted, _v$9 = TextAttributes8.BOLD, _v$0 = theme.text, _v$1 = TextAttributes8.BOLD, _v$10 = theme.textMuted, _v$11 = theme.text, _v$12 = TextAttributes8.BOLD, _v$13 = theme.textMuted, _v$14 = isToastRow() ? theme.primary : undefined, _v$15 = isToastRow() ? theme.selectedListItemText : toastEnabled() ? theme.accent : theme.textMuted, _v$16 = TextAttributes8.BOLD, _v$17 = isSoundRow() ? theme.primary : undefined, _v$18 = isSoundRow() ? theme.selectedListItemText : soundEnabled() ? theme.accent : theme.textMuted, _v$19 = TextAttributes8.BOLD;
12310
12451
  _v$ !== _p$.e && (_p$.e = setProp(_el$1, "fg", _v$, _p$.e));
12311
12452
  _v$2 !== _p$.t && (_p$.t = setProp(_el$1, "attributes", _v$2, _p$.t));
12312
12453
  _v$3 !== _p$.a && (_p$.a = setProp(_el$11, "fg", _v$3, _p$.a));
@@ -12319,6 +12460,15 @@ function SettingsDialog(props) {
12319
12460
  _v$0 !== _p$.d && (_p$.d = setProp(_el$22, "fg", _v$0, _p$.d));
12320
12461
  _v$1 !== _p$.l && (_p$.l = setProp(_el$22, "attributes", _v$1, _p$.l));
12321
12462
  _v$10 !== _p$.u && (_p$.u = setProp(_el$24, "fg", _v$10, _p$.u));
12463
+ _v$11 !== _p$.c && (_p$.c = setProp(_el$27, "fg", _v$11, _p$.c));
12464
+ _v$12 !== _p$.w && (_p$.w = setProp(_el$27, "attributes", _v$12, _p$.w));
12465
+ _v$13 !== _p$.m && (_p$.m = setProp(_el$29, "fg", _v$13, _p$.m));
12466
+ _v$14 !== _p$.f && (_p$.f = setProp(_el$31, "backgroundColor", _v$14, _p$.f));
12467
+ _v$15 !== _p$.y && (_p$.y = setProp(_el$32, "fg", _v$15, _p$.y));
12468
+ _v$16 !== _p$.g && (_p$.g = setProp(_el$32, "attributes", _v$16, _p$.g));
12469
+ _v$17 !== _p$.p && (_p$.p = setProp(_el$34, "backgroundColor", _v$17, _p$.p));
12470
+ _v$18 !== _p$.b && (_p$.b = setProp(_el$35, "fg", _v$18, _p$.b));
12471
+ _v$19 !== _p$.T && (_p$.T = setProp(_el$35, "attributes", _v$19, _p$.T));
12322
12472
  return _p$;
12323
12473
  }, {
12324
12474
  e: undefined,
@@ -12332,7 +12482,16 @@ function SettingsDialog(props) {
12332
12482
  r: undefined,
12333
12483
  d: undefined,
12334
12484
  l: undefined,
12335
- u: undefined
12485
+ u: undefined,
12486
+ c: undefined,
12487
+ w: undefined,
12488
+ m: undefined,
12489
+ f: undefined,
12490
+ y: undefined,
12491
+ g: undefined,
12492
+ p: undefined,
12493
+ b: undefined,
12494
+ T: undefined
12336
12495
  });
12337
12496
  return _el$0;
12338
12497
  }
@@ -12342,119 +12501,119 @@ function SettingsDialog(props) {
12342
12501
  return section() === "dev";
12343
12502
  },
12344
12503
  get children() {
12345
- var _el$26 = createElement("box"), _el$27 = createElement("text"), _el$29 = createElement("text");
12346
- insertNode(_el$26, _el$27);
12347
- insertNode(_el$26, _el$29);
12348
- setProp(_el$26, "flexDirection", "column");
12349
- setProp(_el$26, "gap", 1);
12350
- insertNode(_el$27, createTextNode(`Reset UI state`));
12351
- insertNode(_el$29, 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.`));
12352
- setProp(_el$29, "wrapMode", "word");
12353
- insert(_el$26, () => {
12504
+ var _el$37 = createElement("box"), _el$38 = createElement("text"), _el$40 = createElement("text");
12505
+ insertNode(_el$37, _el$38);
12506
+ insertNode(_el$37, _el$40);
12507
+ setProp(_el$37, "flexDirection", "column");
12508
+ setProp(_el$37, "gap", 1);
12509
+ insertNode(_el$38, createTextNode(`Reset UI state`));
12510
+ insertNode(_el$40, 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.`));
12511
+ setProp(_el$40, "wrapMode", "word");
12512
+ insert(_el$37, () => {
12354
12513
  const isCursor = () => level() === "body" && bodyRow() === 0;
12355
12514
  return (() => {
12356
- var _el$45 = createElement("box"), _el$46 = createElement("text");
12357
- insertNode(_el$45, _el$46);
12358
- setProp(_el$45, "flexDirection", "row");
12359
- setProp(_el$45, "paddingLeft", 1);
12360
- setProp(_el$45, "paddingRight", 1);
12361
- setProp(_el$45, "onMouseUp", () => {
12515
+ var _el$56 = createElement("box"), _el$57 = createElement("text");
12516
+ insertNode(_el$56, _el$57);
12517
+ setProp(_el$56, "flexDirection", "row");
12518
+ setProp(_el$56, "paddingLeft", 1);
12519
+ setProp(_el$56, "paddingRight", 1);
12520
+ setProp(_el$56, "onMouseUp", () => {
12362
12521
  setLevel("body");
12363
12522
  setBodyRow(0);
12364
12523
  confirmReset();
12365
12524
  });
12366
- insertNode(_el$46, createTextNode(`[enter] Reset`));
12525
+ insertNode(_el$57, createTextNode(`[enter] Reset`));
12367
12526
  effect((_p$) => {
12368
- var _v$30 = isCursor() ? theme.primary : theme.backgroundElement, _v$31 = isCursor() ? theme.selectedListItemText : theme.warning, _v$32 = TextAttributes8.BOLD;
12369
- _v$30 !== _p$.e && (_p$.e = setProp(_el$45, "backgroundColor", _v$30, _p$.e));
12370
- _v$31 !== _p$.t && (_p$.t = setProp(_el$46, "fg", _v$31, _p$.t));
12371
- _v$32 !== _p$.a && (_p$.a = setProp(_el$46, "attributes", _v$32, _p$.a));
12527
+ var _v$39 = isCursor() ? theme.primary : theme.backgroundElement, _v$40 = isCursor() ? theme.selectedListItemText : theme.warning, _v$41 = TextAttributes8.BOLD;
12528
+ _v$39 !== _p$.e && (_p$.e = setProp(_el$56, "backgroundColor", _v$39, _p$.e));
12529
+ _v$40 !== _p$.t && (_p$.t = setProp(_el$57, "fg", _v$40, _p$.t));
12530
+ _v$41 !== _p$.a && (_p$.a = setProp(_el$57, "attributes", _v$41, _p$.a));
12372
12531
  return _p$;
12373
12532
  }, {
12374
12533
  e: undefined,
12375
12534
  t: undefined,
12376
12535
  a: undefined
12377
12536
  });
12378
- return _el$45;
12537
+ return _el$56;
12379
12538
  })();
12380
12539
  }, null);
12381
- insert(_el$26, createComponent2(Show, {
12540
+ insert(_el$37, createComponent2(Show, {
12382
12541
  when: hasDaemon,
12383
12542
  get children() {
12384
- var _el$31 = createElement("box"), _el$32 = createElement("text"), _el$34 = createElement("text");
12385
- insertNode(_el$31, _el$32);
12386
- insertNode(_el$31, _el$34);
12387
- setProp(_el$31, "flexDirection", "column");
12388
- setProp(_el$31, "gap", 0);
12389
- setProp(_el$31, "paddingTop", 1);
12390
- insertNode(_el$32, createTextNode(`Restart backend`));
12391
- insertNode(_el$34, createTextNode(`Stops the kobed 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.`));
12392
- setProp(_el$34, "wrapMode", "word");
12393
- insert(_el$31, () => {
12543
+ var _el$42 = createElement("box"), _el$43 = createElement("text"), _el$45 = createElement("text");
12544
+ insertNode(_el$42, _el$43);
12545
+ insertNode(_el$42, _el$45);
12546
+ setProp(_el$42, "flexDirection", "column");
12547
+ setProp(_el$42, "gap", 0);
12548
+ setProp(_el$42, "paddingTop", 1);
12549
+ insertNode(_el$43, createTextNode(`Restart backend`));
12550
+ insertNode(_el$45, createTextNode(`Stops the kobed 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.`));
12551
+ setProp(_el$45, "wrapMode", "word");
12552
+ insert(_el$42, () => {
12394
12553
  const isCursor = () => level() === "body" && bodyRow() === 1;
12395
12554
  return (() => {
12396
- var _el$48 = createElement("box"), _el$49 = createElement("text");
12397
- insertNode(_el$48, _el$49);
12398
- setProp(_el$48, "flexDirection", "row");
12399
- setProp(_el$48, "paddingLeft", 1);
12400
- setProp(_el$48, "paddingRight", 1);
12401
- setProp(_el$48, "onMouseUp", () => {
12555
+ var _el$59 = createElement("box"), _el$60 = createElement("text");
12556
+ insertNode(_el$59, _el$60);
12557
+ setProp(_el$59, "flexDirection", "row");
12558
+ setProp(_el$59, "paddingLeft", 1);
12559
+ setProp(_el$59, "paddingRight", 1);
12560
+ setProp(_el$59, "onMouseUp", () => {
12402
12561
  setLevel("body");
12403
12562
  setBodyRow(1);
12404
12563
  confirmRestartDaemon();
12405
12564
  });
12406
- insertNode(_el$49, createTextNode(`[enter] Restart`));
12565
+ insertNode(_el$60, createTextNode(`[enter] Restart`));
12407
12566
  effect((_p$) => {
12408
- var _v$33 = isCursor() ? theme.primary : theme.backgroundElement, _v$34 = isCursor() ? theme.selectedListItemText : theme.accent, _v$35 = TextAttributes8.BOLD;
12409
- _v$33 !== _p$.e && (_p$.e = setProp(_el$48, "backgroundColor", _v$33, _p$.e));
12410
- _v$34 !== _p$.t && (_p$.t = setProp(_el$49, "fg", _v$34, _p$.t));
12411
- _v$35 !== _p$.a && (_p$.a = setProp(_el$49, "attributes", _v$35, _p$.a));
12567
+ var _v$42 = isCursor() ? theme.primary : theme.backgroundElement, _v$43 = isCursor() ? theme.selectedListItemText : theme.accent, _v$44 = TextAttributes8.BOLD;
12568
+ _v$42 !== _p$.e && (_p$.e = setProp(_el$59, "backgroundColor", _v$42, _p$.e));
12569
+ _v$43 !== _p$.t && (_p$.t = setProp(_el$60, "fg", _v$43, _p$.t));
12570
+ _v$44 !== _p$.a && (_p$.a = setProp(_el$60, "attributes", _v$44, _p$.a));
12412
12571
  return _p$;
12413
12572
  }, {
12414
12573
  e: undefined,
12415
12574
  t: undefined,
12416
12575
  a: undefined
12417
12576
  });
12418
- return _el$48;
12577
+ return _el$59;
12419
12578
  })();
12420
12579
  }, null);
12421
12580
  effect((_p$) => {
12422
- var _v$11 = theme.text, _v$12 = TextAttributes8.BOLD, _v$13 = theme.textMuted;
12423
- _v$11 !== _p$.e && (_p$.e = setProp(_el$32, "fg", _v$11, _p$.e));
12424
- _v$12 !== _p$.t && (_p$.t = setProp(_el$32, "attributes", _v$12, _p$.t));
12425
- _v$13 !== _p$.a && (_p$.a = setProp(_el$34, "fg", _v$13, _p$.a));
12581
+ var _v$20 = theme.text, _v$21 = TextAttributes8.BOLD, _v$22 = theme.textMuted;
12582
+ _v$20 !== _p$.e && (_p$.e = setProp(_el$43, "fg", _v$20, _p$.e));
12583
+ _v$21 !== _p$.t && (_p$.t = setProp(_el$43, "attributes", _v$21, _p$.t));
12584
+ _v$22 !== _p$.a && (_p$.a = setProp(_el$45, "fg", _v$22, _p$.a));
12426
12585
  return _p$;
12427
12586
  }, {
12428
12587
  e: undefined,
12429
12588
  t: undefined,
12430
12589
  a: undefined
12431
12590
  });
12432
- return _el$31;
12591
+ return _el$42;
12433
12592
  }
12434
12593
  }), null);
12435
12594
  effect((_p$) => {
12436
- var _v$14 = theme.text, _v$15 = TextAttributes8.BOLD, _v$16 = theme.textMuted;
12437
- _v$14 !== _p$.e && (_p$.e = setProp(_el$27, "fg", _v$14, _p$.e));
12438
- _v$15 !== _p$.t && (_p$.t = setProp(_el$27, "attributes", _v$15, _p$.t));
12439
- _v$16 !== _p$.a && (_p$.a = setProp(_el$29, "fg", _v$16, _p$.a));
12595
+ var _v$23 = theme.text, _v$24 = TextAttributes8.BOLD, _v$25 = theme.textMuted;
12596
+ _v$23 !== _p$.e && (_p$.e = setProp(_el$38, "fg", _v$23, _p$.e));
12597
+ _v$24 !== _p$.t && (_p$.t = setProp(_el$38, "attributes", _v$24, _p$.t));
12598
+ _v$25 !== _p$.a && (_p$.a = setProp(_el$40, "fg", _v$25, _p$.a));
12440
12599
  return _p$;
12441
12600
  }, {
12442
12601
  e: undefined,
12443
12602
  t: undefined,
12444
12603
  a: undefined
12445
12604
  });
12446
- return _el$26;
12605
+ return _el$37;
12447
12606
  }
12448
12607
  }), null);
12449
- insertNode(_el$36, _el$37);
12450
- setProp(_el$36, "paddingTop", 0);
12451
- insertNode(_el$37, createTextNode(`j/k pick \xB7 h/l switch level \xB7 enter activate \xB7 esc close`));
12608
+ insertNode(_el$47, _el$48);
12609
+ setProp(_el$47, "paddingTop", 0);
12610
+ insertNode(_el$48, createTextNode(`j/k pick \xB7 h/l switch level \xB7 enter activate \xB7 esc close`));
12452
12611
  effect((_p$) => {
12453
- var _v$17 = TextAttributes8.BOLD, _v$18 = theme.text, _v$19 = theme.textMuted, _v$20 = theme.textMuted;
12454
- _v$17 !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$17, _p$.e));
12455
- _v$18 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$18, _p$.t));
12456
- _v$19 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$19, _p$.a));
12457
- _v$20 !== _p$.o && (_p$.o = setProp(_el$37, "fg", _v$20, _p$.o));
12612
+ var _v$26 = TextAttributes8.BOLD, _v$27 = theme.text, _v$28 = theme.textMuted, _v$29 = theme.textMuted;
12613
+ _v$26 !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$26, _p$.e));
12614
+ _v$27 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$27, _p$.t));
12615
+ _v$28 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$28, _p$.a));
12616
+ _v$29 !== _p$.o && (_p$.o = setProp(_el$48, "fg", _v$29, _p$.o));
12458
12617
  return _p$;
12459
12618
  }, {
12460
12619
  e: undefined,
@@ -12588,8 +12747,261 @@ var init_app_keymap = __esm(() => {
12588
12747
  init_dialog_confirm();
12589
12748
  });
12590
12749
 
12750
+ // src/tui/asset/pulse.wav
12751
+ var pulse_default = "../pulse-n3cq1btw.wav";
12752
+ var init_pulse = () => {};
12753
+
12754
+ // src/tui/lib/sound.ts
12755
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
12756
+ import { tmpdir as tmpdir2 } from "os";
12757
+ import { basename as basename3, isAbsolute, join as join11, resolve as resolve3 } from "path";
12758
+ function args(player, file, volume) {
12759
+ if (player === "ffplay")
12760
+ return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
12761
+ if (player === "mpv")
12762
+ return [player, "--no-video", "--audio-display=no", "--volume", String(Math.round(volume * 100)), file];
12763
+ if (player === "mpg123" || player === "mpg321")
12764
+ return [player, "-g", String(Math.round(volume * 100)), file];
12765
+ if (player === "mplayer")
12766
+ return [player, "-vo", "null", "-volume", String(Math.round(volume * 100)), file];
12767
+ if (player === "afplay" || player === "omxplayer" || player === "aplay" || player === "cmdmp3")
12768
+ return [player, file];
12769
+ if (player === "play")
12770
+ return [player, "-v", String(volume), file];
12771
+ if (player === "cvlc")
12772
+ return [player, `--gain=${volume}`, "--play-and-exit", file];
12773
+ return [player, "-c", `(New-Object Media.SoundPlayer '${file.replace(/'/g, "''")}').PlaySync()`];
12774
+ }
12775
+ function pickPlayer() {
12776
+ if (cachedPlayer !== undefined)
12777
+ return cachedPlayer;
12778
+ const path6 = process.env.PATH ?? "";
12779
+ const segments = path6.split(":").filter(Boolean);
12780
+ cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync5(join11(dir, p)))) ?? null;
12781
+ return cachedPlayer;
12782
+ }
12783
+ async function ensureAsset() {
12784
+ cachedPath ??= (async () => {
12785
+ mkdirSync3(DIR, { recursive: true });
12786
+ const dest = join11(DIR, basename3(pulseAsset));
12787
+ const out = Bun.file(dest);
12788
+ if (await out.exists())
12789
+ return dest;
12790
+ await Bun.write(out, Bun.file(pulseAsset));
12791
+ return dest;
12792
+ })();
12793
+ return cachedPath;
12794
+ }
12795
+ function pulse(volume = 0.4) {
12796
+ const player = pickPlayer();
12797
+ if (!player)
12798
+ return;
12799
+ ensureAsset().then((path6) => {
12800
+ try {
12801
+ const proc = Bun.spawn(args(player, path6, volume), {
12802
+ stdin: "ignore",
12803
+ stdout: "ignore",
12804
+ stderr: "ignore"
12805
+ });
12806
+ proc.unref?.();
12807
+ } catch {}
12808
+ }).catch(() => {
12809
+ return;
12810
+ });
12811
+ }
12812
+ var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
12813
+ var init_sound = __esm(() => {
12814
+ init_pulse();
12815
+ pulseAsset = isAbsolute(pulse_default) ? pulse_default : resolve3(import.meta.dir, pulse_default);
12816
+ DIR = join11(tmpdir2(), "kobe-sfx");
12817
+ PLAYERS = [
12818
+ "ffplay",
12819
+ "mpv",
12820
+ "mpg123",
12821
+ "mpg321",
12822
+ "mplayer",
12823
+ "afplay",
12824
+ "play",
12825
+ "omxplayer",
12826
+ "aplay",
12827
+ "cmdmp3",
12828
+ "cvlc",
12829
+ "powershell.exe"
12830
+ ];
12831
+ });
12832
+
12833
+ // src/tui/context/kv.tsx
12834
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
12835
+ import { homedir as homedir7 } from "os";
12836
+ import { dirname as dirname5, join as join12 } from "path";
12837
+ function loadInitial() {
12838
+ try {
12839
+ const text = readFileSync5(STATE_PATH, "utf8");
12840
+ const parsed = JSON.parse(text);
12841
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
12842
+ return parsed;
12843
+ }
12844
+ } catch {}
12845
+ return {};
12846
+ }
12847
+ var STATE_PATH, WRITE_DEBOUNCE_MS = 250, useKV, KVProvider;
12848
+ var init_kv = __esm(() => {
12849
+ init_dev2();
12850
+ init_helper();
12851
+ STATE_PATH = join12(homedir7(), ".config", "kobe", "state.json");
12852
+ ({
12853
+ use: useKV,
12854
+ provider: KVProvider
12855
+ } = createSimpleContext({
12856
+ name: "KV",
12857
+ init: () => {
12858
+ const [store2, setStore2] = createStore(loadInitial());
12859
+ let writeTimer = null;
12860
+ function scheduleWrite() {
12861
+ if (writeTimer)
12862
+ clearTimeout(writeTimer);
12863
+ writeTimer = setTimeout(() => {
12864
+ writeTimer = null;
12865
+ try {
12866
+ mkdirSync4(dirname5(STATE_PATH), {
12867
+ recursive: true
12868
+ });
12869
+ const tmp = `${STATE_PATH}.tmp`;
12870
+ writeFileSync3(tmp, JSON.stringify(store2, null, 2), "utf8");
12871
+ renameSync2(tmp, STATE_PATH);
12872
+ } catch (err) {
12873
+ console.error("[kobe] kv write failed:", err);
12874
+ }
12875
+ }, WRITE_DEBOUNCE_MS);
12876
+ }
12877
+ const result = {
12878
+ get ready() {
12879
+ return true;
12880
+ },
12881
+ get store() {
12882
+ return store2;
12883
+ },
12884
+ signal(name, defaultValue) {
12885
+ if (store2[name] === undefined)
12886
+ setStore2(name, defaultValue);
12887
+ return [() => result.get(name), (next) => result.set(name, next)];
12888
+ },
12889
+ get(key, defaultValue) {
12890
+ return store2[key] ?? defaultValue;
12891
+ },
12892
+ set(key, value) {
12893
+ setStore2(key, value);
12894
+ scheduleWrite();
12895
+ },
12896
+ clear() {
12897
+ for (const k of Object.keys(store2))
12898
+ setStore2(k, undefined);
12899
+ if (writeTimer) {
12900
+ clearTimeout(writeTimer);
12901
+ writeTimer = null;
12902
+ }
12903
+ try {
12904
+ mkdirSync4(dirname5(STATE_PATH), {
12905
+ recursive: true
12906
+ });
12907
+ const tmp = `${STATE_PATH}.tmp`;
12908
+ writeFileSync3(tmp, JSON.stringify(store2, null, 2), "utf8");
12909
+ renameSync2(tmp, STATE_PATH);
12910
+ } catch (err) {
12911
+ console.error("[kobe] kv clear write failed:", err);
12912
+ }
12913
+ }
12914
+ };
12915
+ return result;
12916
+ }
12917
+ }));
12918
+ });
12919
+
12920
+ // src/tui/context/notifications.tsx
12921
+ function unreadKey(taskId, tabId) {
12922
+ return `${taskId}:${tabId}`;
12923
+ }
12924
+ function NotificationsProvider(props) {
12925
+ const kv = useKV();
12926
+ const [toasts, setToasts] = createSignal([]);
12927
+ const [unread, setUnread] = createSignal(new Map);
12928
+ let counter = 0;
12929
+ function notify(input) {
12930
+ setUnread((prev) => {
12931
+ const next = new Map(prev);
12932
+ const key = unreadKey(input.taskId, input.tabId);
12933
+ const existing = prev.get(key);
12934
+ if (existing === "needs_input")
12935
+ return prev;
12936
+ next.set(key, input.kind);
12937
+ return next;
12938
+ });
12939
+ if (kv.get("notifications.sound.enabled", true) !== false) {
12940
+ try {
12941
+ process.stdout.write("\x07");
12942
+ } catch {}
12943
+ pulse();
12944
+ }
12945
+ if (kv.get("notifications.toast.enabled", true) !== false) {
12946
+ const id = ++counter;
12947
+ const toast = {
12948
+ id,
12949
+ kind: input.kind,
12950
+ taskId: input.taskId,
12951
+ tabId: input.tabId,
12952
+ title: input.title
12953
+ };
12954
+ setToasts((prev) => [...prev, toast]);
12955
+ setTimeout(() => dismiss(id), TOAST_DURATION_MS);
12956
+ }
12957
+ }
12958
+ function dismiss(id) {
12959
+ setToasts((prev) => prev.filter((t) => t.id !== id));
12960
+ }
12961
+ function markRead(taskId, tabId) {
12962
+ setUnread((prev) => {
12963
+ const key = unreadKey(taskId, tabId);
12964
+ if (!prev.has(key))
12965
+ return prev;
12966
+ const next = new Map(prev);
12967
+ next.delete(key);
12968
+ return next;
12969
+ });
12970
+ }
12971
+ const value = {
12972
+ toasts,
12973
+ unread,
12974
+ notify,
12975
+ dismiss,
12976
+ markRead
12977
+ };
12978
+ return createComponent2(ctx3.Provider, {
12979
+ value,
12980
+ get children() {
12981
+ return props.children;
12982
+ }
12983
+ });
12984
+ }
12985
+ function useNotifications() {
12986
+ const value = useContext(ctx3);
12987
+ if (!value)
12988
+ throw new Error("useNotifications must be used within a NotificationsProvider");
12989
+ return value;
12990
+ }
12991
+ function notificationKey(taskId, tabId) {
12992
+ return unreadKey(taskId, tabId);
12993
+ }
12994
+ var TOAST_DURATION_MS = 4500, ctx3;
12995
+ var init_notifications = __esm(() => {
12996
+ init_solid();
12997
+ init_dev();
12998
+ init_sound();
12999
+ init_kv();
13000
+ ctx3 = createContext();
13001
+ });
13002
+
12591
13003
  // src/tui/component/center-tab-strip.tsx
12592
- import { basename as basename3 } from "path";
13004
+ import { basename as basename4 } from "path";
12593
13005
  import { TextAttributes as TextAttributes9 } from "@opentui/core";
12594
13006
  function CenterTabStrip(props) {
12595
13007
  const {
@@ -12656,6 +13068,14 @@ function CenterTabStrip(props) {
12656
13068
  return theme.success;
12657
13069
  return theme.textMuted;
12658
13070
  };
13071
+ const unreadKind = () => {
13072
+ const taskId = props.activeTaskId();
13073
+ if (!taskId)
13074
+ return;
13075
+ return props.unread().get(notificationKey(taskId, tab.id));
13076
+ };
13077
+ const showUnread = () => !isPrimary() && unreadKind() !== undefined;
13078
+ const unreadColor = () => unreadKind() === "needs_input" ? theme.warning : theme.success;
12659
13079
  return (() => {
12660
13080
  var _el$5 = createElement("box"), _el$7 = createElement("text");
12661
13081
  insertNode(_el$5, _el$7);
@@ -12682,6 +13102,18 @@ function CenterTabStrip(props) {
12682
13102
  }), _el$7);
12683
13103
  setProp(_el$7, "wrapMode", "none");
12684
13104
  insert(_el$7, () => chatTabLabel(tab));
13105
+ insert(_el$5, createComponent2(Show, {
13106
+ get when() {
13107
+ return showUnread();
13108
+ },
13109
+ get children() {
13110
+ var _el$8 = createElement("text");
13111
+ insertNode(_el$8, createTextNode(`\u25CF`));
13112
+ setProp(_el$8, "wrapMode", "none");
13113
+ effect((_$p) => setProp(_el$8, "fg", unreadColor(), _$p));
13114
+ return _el$8;
13115
+ }
13116
+ }), null);
12685
13117
  effect((_p$) => {
12686
13118
  var _v$4 = isPrimary() ? theme.primary : theme.backgroundElement, _v$5 = isPrimary() ? theme.selectedListItemText : isVisibleButOther() ? theme.text : theme.textMuted, _v$6 = isPrimary() ? TextAttributes9.BOLD : undefined;
12687
13119
  _v$4 !== _p$.e && (_p$.e = setProp(_el$5, "backgroundColor", _v$4, _p$.e));
@@ -12706,24 +13138,24 @@ function CenterTabStrip(props) {
12706
13138
  children: (file) => {
12707
13139
  const isActive = () => !props.isChatActive();
12708
13140
  return (() => {
12709
- var _el$8 = createElement("box"), _el$9 = createElement("text"), _el$0 = createElement("text");
12710
- insertNode(_el$8, _el$9);
12711
- insertNode(_el$8, _el$0);
12712
- setProp(_el$8, "flexDirection", "row");
12713
- setProp(_el$8, "gap", 1);
12714
- setProp(_el$8, "paddingLeft", 1);
12715
- setProp(_el$8, "paddingRight", 1);
12716
- setProp(_el$8, "onMouseUp", () => props.onSelectFile(file()));
12717
- setProp(_el$9, "wrapMode", "none");
12718
- insert(_el$9, () => basename3(file()));
12719
- insertNode(_el$0, createTextNode(`x`));
12720
- setProp(_el$0, "onMouseUp", () => queueMicrotask(() => props.onCloseFile(file())));
13141
+ var _el$0 = createElement("box"), _el$1 = createElement("text"), _el$10 = createElement("text");
13142
+ insertNode(_el$0, _el$1);
13143
+ insertNode(_el$0, _el$10);
13144
+ setProp(_el$0, "flexDirection", "row");
13145
+ setProp(_el$0, "gap", 1);
13146
+ setProp(_el$0, "paddingLeft", 1);
13147
+ setProp(_el$0, "paddingRight", 1);
13148
+ setProp(_el$0, "onMouseUp", () => props.onSelectFile(file()));
13149
+ setProp(_el$1, "wrapMode", "none");
13150
+ insert(_el$1, () => basename4(file()));
13151
+ insertNode(_el$10, createTextNode(`x`));
13152
+ setProp(_el$10, "onMouseUp", () => queueMicrotask(() => props.onCloseFile(file())));
12721
13153
  effect((_p$) => {
12722
13154
  var _v$7 = isActive() ? theme.primary : theme.backgroundElement, _v$8 = isActive() ? theme.selectedListItemText : theme.text, _v$9 = isActive() ? TextAttributes9.BOLD : undefined, _v$0 = isActive() ? theme.selectedListItemText : theme.textMuted;
12723
- _v$7 !== _p$.e && (_p$.e = setProp(_el$8, "backgroundColor", _v$7, _p$.e));
12724
- _v$8 !== _p$.t && (_p$.t = setProp(_el$9, "fg", _v$8, _p$.t));
12725
- _v$9 !== _p$.a && (_p$.a = setProp(_el$9, "attributes", _v$9, _p$.a));
12726
- _v$0 !== _p$.o && (_p$.o = setProp(_el$0, "fg", _v$0, _p$.o));
13155
+ _v$7 !== _p$.e && (_p$.e = setProp(_el$0, "backgroundColor", _v$7, _p$.e));
13156
+ _v$8 !== _p$.t && (_p$.t = setProp(_el$1, "fg", _v$8, _p$.t));
13157
+ _v$9 !== _p$.a && (_p$.a = setProp(_el$1, "attributes", _v$9, _p$.a));
13158
+ _v$0 !== _p$.o && (_p$.o = setProp(_el$10, "fg", _v$0, _p$.o));
12727
13159
  return _p$;
12728
13160
  }, {
12729
13161
  e: undefined,
@@ -12731,7 +13163,7 @@ function CenterTabStrip(props) {
12731
13163
  a: undefined,
12732
13164
  o: undefined
12733
13165
  });
12734
- return _el$8;
13166
+ return _el$0;
12735
13167
  })();
12736
13168
  }
12737
13169
  }), null);
@@ -12749,6 +13181,7 @@ var init_center_tab_strip = __esm(() => {
12749
13181
  init_solid();
12750
13182
  init_dev();
12751
13183
  init_core();
13184
+ init_notifications();
12752
13185
  init_theme();
12753
13186
  });
12754
13187
 
@@ -13240,11 +13673,11 @@ function FocusProvider(props) {
13240
13673
  });
13241
13674
  }
13242
13675
  function useFocus() {
13243
- const ctx3 = useContext(FocusContext);
13244
- if (!ctx3) {
13676
+ const ctx4 = useContext(FocusContext);
13677
+ if (!ctx4) {
13245
13678
  throw new Error("useFocus: must be called inside <FocusProvider>. See src/tui/context/focus.tsx.");
13246
13679
  }
13247
- return ctx3;
13680
+ return ctx4;
13248
13681
  }
13249
13682
  var PANE_ORDER, FocusContext;
13250
13683
  var init_focus = __esm(() => {
@@ -13404,53 +13837,140 @@ var init_status_bar = __esm(() => {
13404
13837
  init_theme();
13405
13838
  });
13406
13839
 
13407
- // src/tui/component/create-pr-button.tsx
13840
+ // src/tui/component/toast-overlay.tsx
13408
13841
  import { TextAttributes as TextAttributes13 } from "@opentui/core";
13409
- function isEnabled(task) {
13410
- if (!task)
13411
- return false;
13412
- if (!task.worktreePath)
13413
- return false;
13414
- if (task.status === "canceled")
13415
- return false;
13416
- return true;
13417
- }
13418
- function CreatePRButton(props) {
13842
+ function ToastOverlay() {
13419
13843
  const {
13420
13844
  theme
13421
13845
  } = useTheme();
13422
- function onClick() {
13423
- const task = props.activeTask();
13424
- if (!isEnabled(task) || !task)
13425
- return;
13426
- props.orchestrator.requestPR(task.id).catch((err) => {
13427
- console.error("[kobe] requestPR failed:", err);
13428
- });
13429
- }
13430
- const enabled = () => isEnabled(props.activeTask());
13431
- const bracketColor = () => enabled() ? theme.accent : theme.textMuted;
13432
- const labelColor = () => enabled() ? theme.textMuted : theme.textMuted;
13433
- return (() => {
13434
- var _el$ = createElement("box"), _el$2 = createElement("text"), _el$4 = createElement("text");
13435
- insertNode(_el$, _el$2);
13436
- insertNode(_el$, _el$4);
13437
- setProp(_el$, "flexDirection", "row");
13438
- setProp(_el$, "gap", 1);
13439
- setProp(_el$, "flexShrink", 0);
13440
- insertNode(_el$2, createTextNode(`[PR]`));
13441
- setProp(_el$2, "wrapMode", "none");
13442
- insertNode(_el$4, createTextNode(`Create PR`));
13443
- setProp(_el$4, "wrapMode", "none");
13444
- effect((_p$) => {
13445
- var _v$ = enabled() ? onClick : undefined, _v$2 = bracketColor(), _v$3 = TextAttributes13.BOLD, _v$4 = labelColor();
13446
- _v$ !== _p$.e && (_p$.e = setProp(_el$, "onMouseUp", _v$, _p$.e));
13447
- _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "fg", _v$2, _p$.t));
13448
- _v$3 !== _p$.a && (_p$.a = setProp(_el$2, "attributes", _v$3, _p$.a));
13449
- _v$4 !== _p$.o && (_p$.o = setProp(_el$4, "fg", _v$4, _p$.o));
13450
- return _p$;
13451
- }, {
13452
- e: undefined,
13453
- t: undefined,
13846
+ const dims = useTerminalDimensions();
13847
+ const notif = useNotifications();
13848
+ const visibleToasts = () => notif.toasts().slice(-MAX_VISIBLE);
13849
+ const left = () => Math.max(0, dims().width - CHIP_WIDTH - RIGHT_MARGIN);
13850
+ const top = () => Math.max(0, dims().height - BOTTOM_MARGIN - MAX_VISIBLE - 1);
13851
+ return createComponent2(Show, {
13852
+ get when() {
13853
+ return notif.toasts().length > 0;
13854
+ },
13855
+ get children() {
13856
+ var _el$ = createElement("box");
13857
+ setProp(_el$, "position", "absolute");
13858
+ setProp(_el$, "zIndex", 2500);
13859
+ setProp(_el$, "width", 40);
13860
+ setProp(_el$, "flexDirection", "column");
13861
+ setProp(_el$, "gap", 0);
13862
+ insert(_el$, createComponent2(For, {
13863
+ get each() {
13864
+ return visibleToasts();
13865
+ },
13866
+ children: (toast) => {
13867
+ const bg = () => toast.kind === "needs_input" ? theme.warning : theme.success;
13868
+ const fg = () => theme.selectedListItemText;
13869
+ const prefix = () => toast.kind === "needs_input" ? "?" : "\u2713";
13870
+ return (() => {
13871
+ var _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createElement("text");
13872
+ insertNode(_el$2, _el$3);
13873
+ insertNode(_el$2, _el$4);
13874
+ setProp(_el$2, "flexDirection", "row");
13875
+ setProp(_el$2, "gap", 1);
13876
+ setProp(_el$2, "paddingLeft", 1);
13877
+ setProp(_el$2, "paddingRight", 1);
13878
+ setProp(_el$2, "onMouseUp", () => notif.dismiss(toast.id));
13879
+ setProp(_el$3, "wrapMode", "none");
13880
+ insert(_el$3, prefix);
13881
+ setProp(_el$4, "wrapMode", "none");
13882
+ insert(_el$4, () => toast.title);
13883
+ effect((_p$) => {
13884
+ var _v$3 = bg(), _v$4 = fg(), _v$5 = TextAttributes13.BOLD, _v$6 = fg();
13885
+ _v$3 !== _p$.e && (_p$.e = setProp(_el$2, "backgroundColor", _v$3, _p$.e));
13886
+ _v$4 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$4, _p$.t));
13887
+ _v$5 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$5, _p$.a));
13888
+ _v$6 !== _p$.o && (_p$.o = setProp(_el$4, "fg", _v$6, _p$.o));
13889
+ return _p$;
13890
+ }, {
13891
+ e: undefined,
13892
+ t: undefined,
13893
+ a: undefined,
13894
+ o: undefined
13895
+ });
13896
+ return _el$2;
13897
+ })();
13898
+ }
13899
+ }));
13900
+ effect((_p$) => {
13901
+ var _v$ = left(), _v$2 = top();
13902
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "left", _v$, _p$.e));
13903
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$, "top", _v$2, _p$.t));
13904
+ return _p$;
13905
+ }, {
13906
+ e: undefined,
13907
+ t: undefined
13908
+ });
13909
+ return _el$;
13910
+ }
13911
+ });
13912
+ }
13913
+ var MAX_VISIBLE = 4, CHIP_WIDTH = 40, RIGHT_MARGIN = 2, BOTTOM_MARGIN = 2;
13914
+ var init_toast_overlay = __esm(() => {
13915
+ init_solid();
13916
+ init_solid();
13917
+ init_solid();
13918
+ init_solid();
13919
+ init_solid();
13920
+ init_solid();
13921
+ init_solid();
13922
+ init_dev();
13923
+ init_notifications();
13924
+ init_theme();
13925
+ });
13926
+
13927
+ // src/tui/component/create-pr-button.tsx
13928
+ import { TextAttributes as TextAttributes14 } from "@opentui/core";
13929
+ function isEnabled(task) {
13930
+ if (!task)
13931
+ return false;
13932
+ if (!task.worktreePath)
13933
+ return false;
13934
+ if (task.status === "canceled")
13935
+ return false;
13936
+ return true;
13937
+ }
13938
+ function CreatePRButton(props) {
13939
+ const {
13940
+ theme
13941
+ } = useTheme();
13942
+ function onClick() {
13943
+ const task = props.activeTask();
13944
+ if (!isEnabled(task) || !task)
13945
+ return;
13946
+ props.orchestrator.requestPR(task.id).catch((err) => {
13947
+ console.error("[kobe] requestPR failed:", err);
13948
+ });
13949
+ }
13950
+ const enabled = () => isEnabled(props.activeTask());
13951
+ const bracketColor = () => enabled() ? theme.accent : theme.textMuted;
13952
+ const labelColor = () => enabled() ? theme.textMuted : theme.textMuted;
13953
+ return (() => {
13954
+ var _el$ = createElement("box"), _el$2 = createElement("text"), _el$4 = createElement("text");
13955
+ insertNode(_el$, _el$2);
13956
+ insertNode(_el$, _el$4);
13957
+ setProp(_el$, "flexDirection", "row");
13958
+ setProp(_el$, "gap", 1);
13959
+ setProp(_el$, "flexShrink", 0);
13960
+ insertNode(_el$2, createTextNode(`[PR]`));
13961
+ setProp(_el$2, "wrapMode", "none");
13962
+ insertNode(_el$4, createTextNode(`Create PR`));
13963
+ setProp(_el$4, "wrapMode", "none");
13964
+ effect((_p$) => {
13965
+ var _v$ = enabled() ? onClick : undefined, _v$2 = bracketColor(), _v$3 = TextAttributes14.BOLD, _v$4 = labelColor();
13966
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "onMouseUp", _v$, _p$.e));
13967
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "fg", _v$2, _p$.t));
13968
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$2, "attributes", _v$3, _p$.a));
13969
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$4, "fg", _v$4, _p$.o));
13970
+ return _p$;
13971
+ }, {
13972
+ e: undefined,
13973
+ t: undefined,
13454
13974
  a: undefined,
13455
13975
  o: undefined
13456
13976
  });
@@ -13732,7 +14252,7 @@ var init_markdown_parser = __esm(() => {
13732
14252
  });
13733
14253
 
13734
14254
  // src/tui/panes/chat/Markdown.tsx
13735
- import { TextAttributes as TextAttributes14 } from "@opentui/core";
14255
+ import { TextAttributes as TextAttributes15 } from "@opentui/core";
13736
14256
  function InlineSpans(props) {
13737
14257
  const {
13738
14258
  theme
@@ -13762,7 +14282,7 @@ function InlineSpans(props) {
13762
14282
  insert(_el$3, () => t.text, _el$5);
13763
14283
  effect((_$p) => setProp(_el$3, "style", {
13764
14284
  fg: theme.accent,
13765
- attributes: TextAttributes14.DIM
14285
+ attributes: TextAttributes15.DIM
13766
14286
  }, _$p));
13767
14287
  return _el$3;
13768
14288
  })();
@@ -13774,7 +14294,7 @@ function InlineSpans(props) {
13774
14294
  insert(_el$6, () => showUrl ? t.text : t.href);
13775
14295
  effect((_$p) => setProp(_el$6, "style", {
13776
14296
  fg: theme.accent,
13777
- attributes: TextAttributes14.UNDERLINE
14297
+ attributes: TextAttributes15.UNDERLINE
13778
14298
  }, _$p));
13779
14299
  return _el$6;
13780
14300
  })(), createComponent2(Show, {
@@ -13786,7 +14306,7 @@ function InlineSpans(props) {
13786
14306
  insert(_el$7, () => t.href, _el$9);
13787
14307
  effect((_$p) => setProp(_el$7, "style", {
13788
14308
  fg: theme.textMuted,
13789
- attributes: TextAttributes14.DIM
14309
+ attributes: TextAttributes15.DIM
13790
14310
  }, _$p));
13791
14311
  return _el$7;
13792
14312
  }
@@ -13872,7 +14392,7 @@ function Table(props) {
13872
14392
  const [lp, rp] = padding(cellWidth(cell), columnWidths[ci()] ?? 0, align);
13873
14393
  return [(() => {
13874
14394
  var _el$11 = createElement("span");
13875
- insert(_el$11, () => " " + " ".repeat(lp));
14395
+ insert(_el$11, () => ` ${" ".repeat(lp)}`);
13876
14396
  return _el$11;
13877
14397
  })(), memo2(() => memo2(() => !!rowProps.isHeader)() ? (() => {
13878
14398
  var _el$13 = createElement("b");
@@ -13884,7 +14404,7 @@ function Table(props) {
13884
14404
  tokens
13885
14405
  })), (() => {
13886
14406
  var _el$12 = createElement("span");
13887
- insert(_el$12, () => " ".repeat(rp) + " \u2502");
14407
+ insert(_el$12, () => `${" ".repeat(rp)} \u2502`);
13888
14408
  return _el$12;
13889
14409
  })()];
13890
14410
  }
@@ -13963,7 +14483,7 @@ function VerticalTable(props) {
13963
14483
  var _el$20 = createElement("text");
13964
14484
  insertNode(_el$20, createTextNode(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
13965
14485
  effect((_p$) => {
13966
- var _v$4 = theme.textMuted, _v$5 = TextAttributes14.DIM;
14486
+ var _v$4 = theme.textMuted, _v$5 = TextAttributes15.DIM;
13967
14487
  _v$4 !== _p$.e && (_p$.e = setProp(_el$20, "fg", _v$4, _p$.e));
13968
14488
  _v$5 !== _p$.t && (_p$.t = setProp(_el$20, "attributes", _v$5, _p$.t));
13969
14489
  return _p$;
@@ -13978,7 +14498,7 @@ function VerticalTable(props) {
13978
14498
  children: (cell, ci) => (() => {
13979
14499
  var _el$22 = createElement("text"), _el$23 = createElement("b");
13980
14500
  insertNode(_el$22, _el$23);
13981
- insert(_el$23, () => (props.block.header[ci()] ?? `Column ${ci() + 1}`) + ": ");
14501
+ insert(_el$23, () => `${props.block.header[ci()] ?? `Column ${ci() + 1}`}: `);
13982
14502
  insert(_el$22, createComponent2(InlineSpans, {
13983
14503
  get tokens() {
13984
14504
  return parseInline(cell);
@@ -14054,7 +14574,7 @@ function BlockNode(props) {
14054
14574
  })();
14055
14575
  }
14056
14576
  if (b.kind === "heading") {
14057
- const attrs = b.level === 1 ? TextAttributes14.BOLD | TextAttributes14.UNDERLINE : TextAttributes14.BOLD;
14577
+ const attrs = b.level === 1 ? TextAttributes15.BOLD | TextAttributes15.UNDERLINE : TextAttributes15.BOLD;
14058
14578
  const fg = b.level <= 2 ? theme.accent : theme.text;
14059
14579
  const tokens = parseInline(b.text);
14060
14580
  return (() => {
@@ -14080,7 +14600,7 @@ function BlockNode(props) {
14080
14600
  const checkbox = item.checked ? "[x] " : "[ ] ";
14081
14601
  const bullet = b.ordered ? `${b.start + idx()}. ` : "\u2022 ";
14082
14602
  const prefix = isTask ? checkbox : bullet;
14083
- const labelAttrs = isTask && item.checked ? TextAttributes14.DIM : 0;
14603
+ const labelAttrs = isTask && item.checked ? TextAttributes15.DIM : 0;
14084
14604
  return (() => {
14085
14605
  var _el$30 = createElement("text"), _el$31 = createElement("span");
14086
14606
  insertNode(_el$30, _el$31);
@@ -14127,9 +14647,9 @@ function BlockNode(props) {
14127
14647
  }
14128
14648
  }), null);
14129
14649
  effect((_p$) => {
14130
- var _v$10 = theme.textMuted, _v$11 = TextAttributes14.ITALIC, _v$12 = {
14650
+ var _v$10 = theme.textMuted, _v$11 = TextAttributes15.ITALIC, _v$12 = {
14131
14651
  fg: theme.textMuted,
14132
- attributes: TextAttributes14.DIM
14652
+ attributes: TextAttributes15.DIM
14133
14653
  };
14134
14654
  _v$10 !== _p$.e && (_p$.e = setProp(_el$33, "fg", _v$10, _p$.e));
14135
14655
  _v$11 !== _p$.t && (_p$.t = setProp(_el$33, "attributes", _v$11, _p$.t));
@@ -14171,7 +14691,7 @@ function BlockNode(props) {
14171
14691
  var _el$37 = createElement("text");
14172
14692
  insert(_el$37, () => b.lang);
14173
14693
  effect((_p$) => {
14174
- var _v$13 = theme.textMuted, _v$14 = TextAttributes14.DIM;
14694
+ var _v$13 = theme.textMuted, _v$14 = TextAttributes15.DIM;
14175
14695
  _v$13 !== _p$.e && (_p$.e = setProp(_el$37, "fg", _v$13, _p$.e));
14176
14696
  _v$14 !== _p$.t && (_p$.t = setProp(_el$37, "attributes", _v$14, _p$.t));
14177
14697
  return _p$;
@@ -14225,13 +14745,13 @@ var init_Markdown = __esm(() => {
14225
14745
  init_solid();
14226
14746
  init_solid();
14227
14747
  init_dev();
14228
- init_theme();
14229
14748
  init_border();
14749
+ init_theme();
14230
14750
  init_markdown_parser();
14231
14751
  });
14232
14752
 
14233
14753
  // src/tui/component/update-dialog.tsx
14234
- import { TextAttributes as TextAttributes15 } from "@opentui/core";
14754
+ import { TextAttributes as TextAttributes16 } from "@opentui/core";
14235
14755
  function UpdateDialog(props) {
14236
14756
  const dialog = useDialog();
14237
14757
  const {
@@ -14346,7 +14866,7 @@ function UpdateDialog(props) {
14346
14866
  }
14347
14867
  }));
14348
14868
  effect((_p$) => {
14349
- var _v$ = TextAttributes15.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted, _v$5 = theme.textMuted, _v$6 = theme.warning, _v$7 = TextAttributes15.BOLD, _v$8 = theme.textMuted, _v$9 = theme.accent, _v$0 = TextAttributes15.BOLD, _v$1 = theme.textMuted;
14869
+ var _v$ = TextAttributes16.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted, _v$5 = theme.textMuted, _v$6 = theme.warning, _v$7 = TextAttributes16.BOLD, _v$8 = theme.textMuted, _v$9 = theme.accent, _v$0 = TextAttributes16.BOLD, _v$1 = theme.textMuted;
14350
14870
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
14351
14871
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
14352
14872
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -14397,7 +14917,7 @@ var init_update_dialog = __esm(() => {
14397
14917
  });
14398
14918
 
14399
14919
  // src/tui/component/top-bar.tsx
14400
- import { TextAttributes as TextAttributes16 } from "@opentui/core";
14920
+ import { TextAttributes as TextAttributes17 } from "@opentui/core";
14401
14921
  function TopBar(props) {
14402
14922
  const {
14403
14923
  theme
@@ -14438,7 +14958,7 @@ function TopBar(props) {
14438
14958
  });
14439
14959
  insert(_el$7, () => props.updateInfo()?.latest, _el$9);
14440
14960
  effect((_p$) => {
14441
- var _v$ = theme.warning, _v$2 = TextAttributes16.BOLD;
14961
+ var _v$ = theme.warning, _v$2 = TextAttributes17.BOLD;
14442
14962
  _v$ !== _p$.e && (_p$.e = setProp(_el$7, "fg", _v$, _p$.e));
14443
14963
  _v$2 !== _p$.t && (_p$.t = setProp(_el$7, "attributes", _v$2, _p$.t));
14444
14964
  return _p$;
@@ -14464,7 +14984,7 @@ function TopBar(props) {
14464
14984
  setProp(_el$1, "wrapMode", "none");
14465
14985
  insert(_el$1, () => props.activeTask()?.branch);
14466
14986
  effect((_p$) => {
14467
- var _v$3 = theme.text, _v$4 = TextAttributes16.BOLD;
14987
+ var _v$3 = theme.text, _v$4 = TextAttributes17.BOLD;
14468
14988
  _v$3 !== _p$.e && (_p$.e = setProp(_el$1, "fg", _v$3, _p$.e));
14469
14989
  _v$4 !== _p$.t && (_p$.t = setProp(_el$1, "attributes", _v$4, _p$.t));
14470
14990
  return _p$;
@@ -14489,7 +15009,7 @@ function TopBar(props) {
14489
15009
  }
14490
15010
  }));
14491
15011
  effect((_p$) => {
14492
- var _v$5 = theme.primary, _v$6 = TextAttributes16.BOLD, _v$7 = theme.textMuted;
15012
+ var _v$5 = theme.primary, _v$6 = TextAttributes17.BOLD, _v$7 = theme.textMuted;
14493
15013
  _v$5 !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$5, _p$.e));
14494
15014
  _v$6 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$6, _p$.t));
14495
15015
  _v$7 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$7, _p$.a));
@@ -14518,93 +15038,6 @@ var init_top_bar = __esm(() => {
14518
15038
  init_update_dialog();
14519
15039
  });
14520
15040
 
14521
- // src/tui/context/kv.tsx
14522
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
14523
- import { homedir as homedir7 } from "os";
14524
- import { dirname as dirname5, join as join11 } from "path";
14525
- function loadInitial() {
14526
- try {
14527
- const text = readFileSync5(STATE_PATH, "utf8");
14528
- const parsed = JSON.parse(text);
14529
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
14530
- return parsed;
14531
- }
14532
- } catch {}
14533
- return {};
14534
- }
14535
- var STATE_PATH, WRITE_DEBOUNCE_MS = 250, useKV, KVProvider;
14536
- var init_kv = __esm(() => {
14537
- init_dev2();
14538
- init_helper();
14539
- STATE_PATH = join11(homedir7(), ".config", "kobe", "state.json");
14540
- ({
14541
- use: useKV,
14542
- provider: KVProvider
14543
- } = createSimpleContext({
14544
- name: "KV",
14545
- init: () => {
14546
- const [store2, setStore2] = createStore(loadInitial());
14547
- let writeTimer = null;
14548
- function scheduleWrite() {
14549
- if (writeTimer)
14550
- clearTimeout(writeTimer);
14551
- writeTimer = setTimeout(() => {
14552
- writeTimer = null;
14553
- try {
14554
- mkdirSync3(dirname5(STATE_PATH), {
14555
- recursive: true
14556
- });
14557
- const tmp = `${STATE_PATH}.tmp`;
14558
- writeFileSync3(tmp, JSON.stringify(store2, null, 2), "utf8");
14559
- renameSync2(tmp, STATE_PATH);
14560
- } catch (err) {
14561
- console.error("[kobe] kv write failed:", err);
14562
- }
14563
- }, WRITE_DEBOUNCE_MS);
14564
- }
14565
- const result = {
14566
- get ready() {
14567
- return true;
14568
- },
14569
- get store() {
14570
- return store2;
14571
- },
14572
- signal(name, defaultValue) {
14573
- if (store2[name] === undefined)
14574
- setStore2(name, defaultValue);
14575
- return [() => result.get(name), (next) => result.set(name, next)];
14576
- },
14577
- get(key, defaultValue) {
14578
- return store2[key] ?? defaultValue;
14579
- },
14580
- set(key, value) {
14581
- setStore2(key, value);
14582
- scheduleWrite();
14583
- },
14584
- clear() {
14585
- for (const k of Object.keys(store2))
14586
- setStore2(k, undefined);
14587
- if (writeTimer) {
14588
- clearTimeout(writeTimer);
14589
- writeTimer = null;
14590
- }
14591
- try {
14592
- mkdirSync3(dirname5(STATE_PATH), {
14593
- recursive: true
14594
- });
14595
- const tmp = `${STATE_PATH}.tmp`;
14596
- writeFileSync3(tmp, JSON.stringify(store2, null, 2), "utf8");
14597
- renameSync2(tmp, STATE_PATH);
14598
- } catch (err) {
14599
- console.error("[kobe] kv clear write failed:", err);
14600
- }
14601
- }
14602
- };
14603
- return result;
14604
- }
14605
- }));
14606
- });
14607
-
14608
15041
  // src/tui/context/sync.tsx
14609
15042
  var EMPTY, useSync, SyncProvider;
14610
15043
  var init_sync = __esm(() => {
@@ -14820,17 +15253,17 @@ class SessionRegistry {
14820
15253
  }
14821
15254
  }
14822
15255
  function waitForExit(proc) {
14823
- return new Promise((resolve3) => {
15256
+ return new Promise((resolve4) => {
14824
15257
  if (proc.exitCode !== null || proc.signalCode !== null) {
14825
- resolve3();
15258
+ resolve4();
14826
15259
  return;
14827
15260
  }
14828
- proc.once("close", () => resolve3());
14829
- proc.once("exit", () => resolve3());
15261
+ proc.once("close", () => resolve4());
15262
+ proc.once("exit", () => resolve4());
14830
15263
  });
14831
15264
  }
14832
15265
  function delay(ms) {
14833
- return new Promise((resolve3) => setTimeout(resolve3, ms));
15266
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
14834
15267
  }
14835
15268
 
14836
15269
  // src/engine/claude-code-local/sessions.ts
@@ -14931,8 +15364,8 @@ var init_sessions = __esm(() => {
14931
15364
  // src/engine/claude-code-local/spawn.ts
14932
15365
  import { spawn as spawn4 } from "child_process";
14933
15366
  function spawnClaudeProcess(opts) {
14934
- const args = buildArgs(opts);
14935
- const proc = spawn4(opts.binaryPath, args, {
15367
+ const args2 = buildArgs(opts);
15368
+ const proc = spawn4(opts.binaryPath, args2, {
14936
15369
  cwd: opts.cwd,
14937
15370
  env: { ...process.env, ...opts.env ?? {} },
14938
15371
  stdio: ["pipe", "pipe", "pipe"]
@@ -14941,30 +15374,30 @@ function spawnClaudeProcess(opts) {
14941
15374
  proc,
14942
15375
  stdout: proc.stdout,
14943
15376
  stderr: proc.stderr,
14944
- args
15377
+ args: args2
14945
15378
  };
14946
15379
  }
14947
15380
  function buildArgs(opts) {
14948
- const args = [];
15381
+ const args2 = [];
14949
15382
  if (opts.resumeSessionId) {
14950
- args.push("--resume", opts.resumeSessionId);
15383
+ args2.push("--resume", opts.resumeSessionId);
14951
15384
  }
14952
- args.push("-p", opts.prompt);
15385
+ args2.push("-p", opts.prompt);
14953
15386
  if (opts.model) {
14954
- args.push("--model", opts.model);
15387
+ args2.push("--model", opts.model);
14955
15388
  }
14956
15389
  if (opts.permissionMode) {
14957
- args.push("--permission-mode", opts.permissionMode);
15390
+ args2.push("--permission-mode", opts.permissionMode);
14958
15391
  }
14959
- args.push("--output-format", "stream-json", "--verbose");
15392
+ args2.push("--output-format", "stream-json", "--verbose");
14960
15393
  const mcpConfig = process.env.KOBE_MCP_CONFIG;
14961
15394
  if (mcpConfig && mcpConfig.length > 0) {
14962
- args.push("--mcp-config", mcpConfig);
15395
+ args2.push("--mcp-config", mcpConfig);
14963
15396
  }
14964
15397
  if (opts.extraArgs && opts.extraArgs.length > 0) {
14965
- args.push(...opts.extraArgs);
15398
+ args2.push(...opts.extraArgs);
14966
15399
  }
14967
- return args;
15400
+ return args2;
14968
15401
  }
14969
15402
  var init_spawn = () => {};
14970
15403
 
@@ -15138,7 +15571,7 @@ class ClaudeCodeLocal {
15138
15571
  }
15139
15572
  if (session.closed)
15140
15573
  return;
15141
- await new Promise((resolve3) => session.waiters.push(resolve3));
15574
+ await new Promise((resolve4) => session.waiters.push(resolve4));
15142
15575
  }
15143
15576
  }
15144
15577
  };
@@ -15162,17 +15595,17 @@ class ClaudeCodeLocal {
15162
15595
  this.running.delete(sid);
15163
15596
  }
15164
15597
  }
15165
- async start(args) {
15598
+ async start(args2) {
15166
15599
  const binaryPath = await this.binaryPathResolver();
15167
- const cliPermissionMode = args.opts?.permissionMode === "plan" ? "plan" : "bypassPermissions";
15600
+ const cliPermissionMode = args2.opts?.permissionMode === "plan" ? "plan" : "bypassPermissions";
15168
15601
  const spawned = spawnClaudeProcess({
15169
15602
  binaryPath,
15170
- cwd: args.cwd,
15171
- prompt: args.prompt,
15172
- model: args.opts?.model,
15603
+ cwd: args2.cwd,
15604
+ prompt: args2.prompt,
15605
+ model: args2.opts?.model,
15173
15606
  permissionMode: cliPermissionMode,
15174
- env: args.opts?.env,
15175
- resumeSessionId: args.resumeSessionId
15607
+ env: args2.opts?.env,
15608
+ resumeSessionId: args2.resumeSessionId
15176
15609
  });
15177
15610
  let resolveHandle = () => {};
15178
15611
  let rejectHandle = () => {};
@@ -15189,7 +15622,7 @@ class ClaudeCodeLocal {
15189
15622
  bound = true;
15190
15623
  session = {
15191
15624
  sessionId,
15192
- cwd: args.cwd,
15625
+ cwd: args2.cwd,
15193
15626
  spawned,
15194
15627
  queue,
15195
15628
  waiters: [],
@@ -15198,15 +15631,15 @@ class ClaudeCodeLocal {
15198
15631
  this.running.set(sessionId, session);
15199
15632
  this.registry.register({
15200
15633
  sessionId,
15201
- cwd: args.cwd,
15634
+ cwd: args2.cwd,
15202
15635
  proc: spawned.proc,
15203
15636
  startedAt: Date.now()
15204
15637
  });
15205
- resolveHandle({ sessionId, cwd: args.cwd });
15638
+ resolveHandle({ sessionId, cwd: args2.cwd });
15206
15639
  };
15207
- if (args.resumeSessionId) {
15640
+ if (args2.resumeSessionId) {
15208
15641
  try {
15209
- bind(args.resumeSessionId);
15642
+ bind(args2.resumeSessionId);
15210
15643
  } catch (err) {
15211
15644
  try {
15212
15645
  spawned.proc.kill("SIGKILL");
@@ -15333,7 +15766,7 @@ class FakeAIEngine {
15333
15766
  }
15334
15767
  if (q.closed)
15335
15768
  return;
15336
- await new Promise((resolve3) => q.waiters.push(resolve3));
15769
+ await new Promise((resolve4) => q.waiters.push(resolve4));
15337
15770
  }
15338
15771
  }
15339
15772
  };
@@ -15629,7 +16062,7 @@ async function mountFakeEngineServer(fake) {
15629
16062
  }
15630
16063
  });
15631
16064
  });
15632
- await new Promise((resolve3) => server.listen(port, "127.0.0.1", () => resolve3()));
16065
+ await new Promise((resolve4) => server.listen(port, "127.0.0.1", () => resolve4()));
15633
16066
  server.unref();
15634
16067
  }
15635
16068
  var init_engine_bootstrap = __esm(() => {
@@ -15657,6 +16090,65 @@ function formatPlanUsageCompact(usage) {
15657
16090
  return `Plan ${parts.join(" \xB7 ")}`;
15658
16091
  }
15659
16092
 
16093
+ // src/tui/lib/use-completion-notifications.ts
16094
+ function tabLabel2(tasks, taskId, tabId) {
16095
+ const task = tasks.find((t) => t.id === taskId);
16096
+ if (!task)
16097
+ return "chat tab";
16098
+ const tab = task.tabs.find((t) => t.id === tabId);
16099
+ const tabName = tab?.title && tab.title.length > 0 ? tab.title : tab ? `chat ${tab.seq}` : "chat";
16100
+ return `${task.title} \u203A ${tabName}`;
16101
+ }
16102
+ function useCompletionNotifications(deps) {
16103
+ let prev = new Map;
16104
+ createEffect(() => {
16105
+ const curr = deps.chatRunState();
16106
+ const visible = deps.visibleTabKey();
16107
+ const tasks = deps.tasks();
16108
+ for (const [key, state] of prev) {
16109
+ const next = curr.get(key);
16110
+ if (state === "running" && next === "awaiting_input") {
16111
+ if (key === visible)
16112
+ continue;
16113
+ const [taskId, tabId] = splitKey(key);
16114
+ if (!taskId || !tabId)
16115
+ continue;
16116
+ deps.notifications.notify({
16117
+ kind: "needs_input",
16118
+ taskId,
16119
+ tabId,
16120
+ title: tabLabel2(tasks, taskId, tabId)
16121
+ });
16122
+ continue;
16123
+ }
16124
+ if ((state === "running" || state === "awaiting_input") && next === undefined) {
16125
+ if (key === visible)
16126
+ continue;
16127
+ const [taskId, tabId] = splitKey(key);
16128
+ if (!taskId || !tabId)
16129
+ continue;
16130
+ deps.notifications.notify({
16131
+ kind: "done",
16132
+ taskId,
16133
+ tabId,
16134
+ title: tabLabel2(tasks, taskId, tabId)
16135
+ });
16136
+ }
16137
+ }
16138
+ prev = curr;
16139
+ });
16140
+ }
16141
+ function splitKey(key) {
16142
+ const idx = key.indexOf(":");
16143
+ if (idx < 0)
16144
+ return [null, null];
16145
+ return [key.slice(0, idx), key.slice(idx + 1)];
16146
+ }
16147
+ var init_use_completion_notifications = __esm(() => {
16148
+ init_dev();
16149
+ init_core();
16150
+ });
16151
+
15660
16152
  // src/tui/lib/use-pane-sizes.ts
15661
16153
  function usePaneSizes(kv) {
15662
16154
  const dims = useTerminalDimensions();
@@ -15844,7 +16336,7 @@ var DEFAULT_BASE_REF = "main", PICKER_MAX_VISIBLE = 8;
15844
16336
  var init_state2 = () => {};
15845
16337
 
15846
16338
  // src/tui/component/new-task-dialog/dialog.tsx
15847
- import { TextAttributes as TextAttributes17 } from "@opentui/core";
16339
+ import { TextAttributes as TextAttributes18 } from "@opentui/core";
15848
16340
  function NewTaskDialogView(props) {
15849
16341
  const dialog = useDialog();
15850
16342
  const {
@@ -16011,7 +16503,7 @@ function NewTaskDialogView(props) {
16011
16503
  insert(_el$36, path8, null);
16012
16504
  insert(_el$36, () => isCurrentDir() ? " (current dir)" : "", null);
16013
16505
  effect((_p$) => {
16014
- var _v$10 = isCursor() ? theme.primary : isSelected() ? theme.accent : theme.textMuted, _v$11 = isCursor() ? TextAttributes17.BOLD : undefined;
16506
+ var _v$10 = isCursor() ? theme.primary : isSelected() ? theme.accent : theme.textMuted, _v$11 = isCursor() ? TextAttributes18.BOLD : undefined;
16015
16507
  _v$10 !== _p$.e && (_p$.e = setProp(_el$36, "fg", _v$10, _p$.e));
16016
16508
  _v$11 !== _p$.t && (_p$.t = setProp(_el$36, "attributes", _v$11, _p$.t));
16017
16509
  return _p$;
@@ -16131,7 +16623,7 @@ function NewTaskDialogView(props) {
16131
16623
  insert(_el$37, () => isCursor() ? "\u25B8 " : " ", null);
16132
16624
  insert(_el$37, name, null);
16133
16625
  effect((_p$) => {
16134
- var _v$12 = isCursor() ? theme.primary : isSelected() ? theme.accent : theme.textMuted, _v$13 = isCursor() ? TextAttributes17.BOLD : undefined;
16626
+ var _v$12 = isCursor() ? theme.primary : isSelected() ? theme.accent : theme.textMuted, _v$13 = isCursor() ? TextAttributes18.BOLD : undefined;
16135
16627
  _v$12 !== _p$.e && (_p$.e = setProp(_el$37, "fg", _v$12, _p$.e));
16136
16628
  _v$13 !== _p$.t && (_p$.t = setProp(_el$37, "attributes", _v$13, _p$.t));
16137
16629
  return _p$;
@@ -16164,7 +16656,7 @@ function NewTaskDialogView(props) {
16164
16656
  setProp(_el$33, "paddingBottom", 1);
16165
16657
  insertNode(_el$34, createTextNode(`\u2191\u2193 pick \xB7 enter select \xB7 tab next field \xB7 esc cancel`));
16166
16658
  effect((_p$) => {
16167
- var _v$ = TextAttributes17.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = field() === "repoCustom" ? theme.accent : theme.textMuted, _v$5 = repo(), _v$6 = props.defaultRepo, _v$7 = field() === "repoCustom", _v$8 = field() === "baseRef" ? theme.accent : theme.textMuted, _v$9 = baseRef(), _v$0 = field() === "baseRef", _v$1 = theme.textMuted;
16659
+ var _v$ = TextAttributes18.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = field() === "repoCustom" ? theme.accent : theme.textMuted, _v$5 = repo(), _v$6 = props.defaultRepo, _v$7 = field() === "repoCustom", _v$8 = field() === "baseRef" ? theme.accent : theme.textMuted, _v$9 = baseRef(), _v$0 = field() === "baseRef", _v$1 = theme.textMuted;
16168
16660
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
16169
16661
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
16170
16662
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -16211,13 +16703,13 @@ var init_dialog2 = __esm(() => {
16211
16703
 
16212
16704
  // src/tui/component/new-task-dialog/index.tsx
16213
16705
  function show(dialog, defaultRepo, savedRepos) {
16214
- return new Promise((resolve3) => {
16706
+ return new Promise((resolve4) => {
16215
16707
  dialog.replace(() => createComponent2(NewTaskDialogView, {
16216
16708
  defaultRepo,
16217
16709
  savedRepos,
16218
- onSubmit: (v) => resolve3(v),
16219
- onCancel: () => resolve3(undefined)
16220
- }), () => resolve3(undefined));
16710
+ onSubmit: (v) => resolve4(v),
16711
+ onCancel: () => resolve4(undefined)
16712
+ }), () => resolve4(undefined));
16221
16713
  dialog.setSize("medium");
16222
16714
  });
16223
16715
  }
@@ -16232,7 +16724,7 @@ var init_new_task_dialog = __esm(() => {
16232
16724
  });
16233
16725
 
16234
16726
  // src/tui/component/rename-task-dialog/dialog.tsx
16235
- import { TextAttributes as TextAttributes18 } from "@opentui/core";
16727
+ import { TextAttributes as TextAttributes19 } from "@opentui/core";
16236
16728
  function RenameTaskDialogView(props) {
16237
16729
  const dialog = useDialog();
16238
16730
  const {
@@ -16272,7 +16764,7 @@ function RenameTaskDialogView(props) {
16272
16764
  setProp(_el$0, "paddingBottom", 1);
16273
16765
  insertNode(_el$1, createTextNode(`enter rename \xB7 esc cancel`));
16274
16766
  effect((_p$) => {
16275
- var _v$ = TextAttributes18.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.accent, _v$5 = title(), _v$6 = props.currentTitle, _v$7 = theme.textMuted;
16767
+ var _v$ = TextAttributes19.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.accent, _v$5 = title(), _v$6 = props.currentTitle, _v$7 = theme.textMuted;
16276
16768
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
16277
16769
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
16278
16770
  _v$3 !== _p$.a && (_p$.a = setProp(_el$4, "fg", _v$3, _p$.a));
@@ -16309,15 +16801,15 @@ var init_dialog3 = __esm(() => {
16309
16801
 
16310
16802
  // src/tui/component/rename-task-dialog/index.tsx
16311
16803
  function show2(dialog, currentTitle, opts = {}) {
16312
- return new Promise((resolve3) => {
16804
+ return new Promise((resolve4) => {
16313
16805
  dialog.replace(() => createComponent2(RenameTaskDialogView, {
16314
16806
  currentTitle,
16315
16807
  get dialogTitle() {
16316
16808
  return opts.dialogTitle;
16317
16809
  },
16318
- onSubmit: (v) => resolve3(v),
16319
- onCancel: () => resolve3(undefined)
16320
- }), () => resolve3(undefined));
16810
+ onSubmit: (v) => resolve4(v),
16811
+ onCancel: () => resolve4(undefined)
16812
+ }), () => resolve4(undefined));
16321
16813
  });
16322
16814
  }
16323
16815
  var RenameTaskDialog;
@@ -16595,7 +17087,7 @@ var init_use_workspace_tabs = __esm(() => {
16595
17087
  });
16596
17088
 
16597
17089
  // src/tui/component/resume-dialog.tsx
16598
- import { TextAttributes as TextAttributes19 } from "@opentui/core";
17090
+ import { TextAttributes as TextAttributes20 } from "@opentui/core";
16599
17091
  function ResumeDialog(props) {
16600
17092
  const dialog = useDialog();
16601
17093
  const {
@@ -16742,7 +17234,7 @@ function ResumeDialog(props) {
16742
17234
  setProp(_el$9, "paddingBottom", 1);
16743
17235
  insertNode(_el$0, createTextNode(`j/k or \u2191\u2193 navigate \u2022 enter resume \u2022 esc dismiss`));
16744
17236
  effect((_p$) => {
16745
- var _v$ = TextAttributes19.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted;
17237
+ var _v$ = TextAttributes20.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted;
16746
17238
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
16747
17239
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
16748
17240
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -16881,10 +17373,10 @@ var init_history2 = __esm(() => {
16881
17373
 
16882
17374
  // src/tui/panes/chat/composer/image-paste.ts
16883
17375
  import { randomUUID } from "crypto";
16884
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
16885
- import { join as join12 } from "path";
17376
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
17377
+ import { join as join13 } from "path";
16886
17378
  function pastedImagesDir() {
16887
- return join12(kobeStateDir(), "pasted-images");
17379
+ return join13(kobeStateDir(), "pasted-images");
16888
17380
  }
16889
17381
 
16890
17382
  class ImagePasteRegistry {
@@ -16893,7 +17385,7 @@ class ImagePasteRegistry {
16893
17385
  saveBytes(bytes, mimeType) {
16894
17386
  const ext = mimeTypeToExt(mimeType);
16895
17387
  const absPath = mintPath(ext);
16896
- mkdirSync4(pastedImagesDir(), { recursive: true });
17388
+ mkdirSync5(pastedImagesDir(), { recursive: true });
16897
17389
  writeFileSync4(absPath, bytes);
16898
17390
  return this.register(absPath);
16899
17391
  }
@@ -16901,7 +17393,7 @@ class ImagePasteRegistry {
16901
17393
  if (!clipboardImageSupported())
16902
17394
  return null;
16903
17395
  const absPath = mintPath(".png");
16904
- mkdirSync4(pastedImagesDir(), { recursive: true });
17396
+ mkdirSync5(pastedImagesDir(), { recursive: true });
16905
17397
  const meta = readClipboardImageToFile(absPath);
16906
17398
  if (!meta)
16907
17399
  return null;
@@ -16964,7 +17456,7 @@ function mimeTypeToExt(mimeType) {
16964
17456
  return ".png";
16965
17457
  }
16966
17458
  function mintPath(ext) {
16967
- return join12(pastedImagesDir(), `${randomUUID()}${ext}`);
17459
+ return join13(pastedImagesDir(), `${randomUUID()}${ext}`);
16968
17460
  }
16969
17461
  var IMAGE_TOKEN_RE;
16970
17462
  var init_image_paste = __esm(() => {
@@ -17067,7 +17559,7 @@ var init_mention = __esm(() => {
17067
17559
  });
17068
17560
 
17069
17561
  // src/tui/panes/chat/Composer.tsx
17070
- import { TextAttributes as TextAttributes20 } from "@opentui/core";
17562
+ import { TextAttributes as TextAttributes21 } from "@opentui/core";
17071
17563
  function resolvePlaceholder(opts) {
17072
17564
  if (!opts.hasTask)
17073
17565
  return opts.noTaskMessage ?? "(no task \u2014 press n to create)";
@@ -17161,21 +17653,21 @@ function Composer(props) {
17161
17653
  return findMentionContext(liveBuffer(), liveCursor());
17162
17654
  });
17163
17655
  createEffect(() => {
17164
- const ctx3 = mentionContext();
17656
+ const ctx4 = mentionContext();
17165
17657
  const dismissed = mentionDismissedAt();
17166
17658
  if (dismissed === null)
17167
17659
  return;
17168
- if (!ctx3 || ctx3.atPos !== dismissed)
17660
+ if (!ctx4 || ctx4.atPos !== dismissed)
17169
17661
  setMentionDismissedAt(null);
17170
17662
  });
17171
17663
  const MENTION_MAX_VISIBLE = 8;
17172
17664
  const mentionMatches = createMemo(() => {
17173
- const ctx3 = mentionContext();
17174
- if (!ctx3)
17665
+ const ctx4 = mentionContext();
17666
+ if (!ctx4)
17175
17667
  return [];
17176
- if (mentionDismissedAt() === ctx3.atPos)
17668
+ if (mentionDismissedAt() === ctx4.atPos)
17177
17669
  return [];
17178
- return filterMentionMatches(mentionFiles(), ctx3.query, MENTION_MAX_VISIBLE * 4);
17670
+ return filterMentionMatches(mentionFiles(), ctx4.query, MENTION_MAX_VISIBLE * 4);
17179
17671
  });
17180
17672
  const mentionOpen = createMemo(() => !slashOpen() && mentionMatches().length > 0);
17181
17673
  createEffect(() => {
@@ -17204,11 +17696,11 @@ function Composer(props) {
17204
17696
  });
17205
17697
  function insertMentionSelection(path8) {
17206
17698
  const ref = textareaRef;
17207
- const ctx3 = mentionContext();
17208
- if (!ref || !ctx3)
17699
+ const ctx4 = mentionContext();
17700
+ if (!ref || !ctx4)
17209
17701
  return;
17210
17702
  const cursor = ref.cursorOffset;
17211
- ref.setSelection(ctx3.atPos, cursor);
17703
+ ref.setSelection(ctx4.atPos, cursor);
17212
17704
  ref.deleteSelection();
17213
17705
  const inserted = `@${path8} `;
17214
17706
  ref.insertText(inserted);
@@ -17444,9 +17936,9 @@ function Composer(props) {
17444
17936
  }
17445
17937
  }
17446
17938
  if (key.name === "escape") {
17447
- const ctx3 = mentionContext();
17448
- if (ctx3)
17449
- setMentionDismissedAt(ctx3.atPos);
17939
+ const ctx4 = mentionContext();
17940
+ if (ctx4)
17941
+ setMentionDismissedAt(ctx4.atPos);
17450
17942
  key.preventDefault();
17451
17943
  return;
17452
17944
  }
@@ -17635,7 +18127,7 @@ function Composer(props) {
17635
18127
  }
17636
18128
  }), null);
17637
18129
  effect((_p$) => {
17638
- var _v$20 = active() ? theme.primary : theme.text, _v$21 = active() ? TextAttributes20.BOLD : undefined;
18130
+ var _v$20 = active() ? theme.primary : theme.text, _v$21 = active() ? TextAttributes21.BOLD : undefined;
17639
18131
  _v$20 !== _p$.e && (_p$.e = setProp(_el$28, "fg", _v$20, _p$.e));
17640
18132
  _v$21 !== _p$.t && (_p$.t = setProp(_el$28, "attributes", _v$21, _p$.t));
17641
18133
  return _p$;
@@ -17742,7 +18234,7 @@ function Composer(props) {
17742
18234
  }
17743
18235
  }), null);
17744
18236
  effect((_p$) => {
17745
- var _v$22 = active() ? theme.primary : theme.text, _v$23 = active() ? TextAttributes20.BOLD : undefined;
18237
+ var _v$22 = active() ? theme.primary : theme.text, _v$23 = active() ? TextAttributes21.BOLD : undefined;
17746
18238
  _v$22 !== _p$.e && (_p$.e = setProp(_el$31, "fg", _v$22, _p$.e));
17747
18239
  _v$23 !== _p$.t && (_p$.t = setProp(_el$31, "attributes", _v$23, _p$.t));
17748
18240
  return _p$;
@@ -18576,7 +19068,7 @@ function summarizeGlob(input, output, done) {
18576
19068
  }
18577
19069
 
18578
19070
  // src/tui/panes/chat/MessageList.tsx
18579
- import { TextAttributes as TextAttributes21 } from "@opentui/core";
19071
+ import { TextAttributes as TextAttributes22 } from "@opentui/core";
18580
19072
  function previewToolInput(input) {
18581
19073
  if (input == null)
18582
19074
  return "";
@@ -18624,11 +19116,11 @@ function UserRow(props) {
18624
19116
  const text = props.text;
18625
19117
  const cmd = extractTag(text, COMMAND_NAME_TAG);
18626
19118
  if (cmd) {
18627
- const args = extractTag(text, COMMAND_ARGS_TAG) ?? "";
19119
+ const args2 = extractTag(text, COMMAND_ARGS_TAG) ?? "";
18628
19120
  return {
18629
19121
  kind: "command",
18630
19122
  command: cmd,
18631
- args
19123
+ args: args2
18632
19124
  };
18633
19125
  }
18634
19126
  const stdout = extractTag(text, LOCAL_COMMAND_STDOUT_TAG);
@@ -18672,7 +19164,7 @@ function UserRow(props) {
18672
19164
  })() : null;
18673
19165
  })(), null);
18674
19166
  effect((_p$) => {
18675
- var _v$ = theme.accent, _v$2 = TextAttributes21.BOLD, _v$3 = theme.primary, _v$4 = TextAttributes21.BOLD;
19167
+ var _v$ = theme.accent, _v$2 = TextAttributes22.BOLD, _v$3 = theme.primary, _v$4 = TextAttributes22.BOLD;
18676
19168
  _v$ !== _p$.e && (_p$.e = setProp(_el$2, "fg", _v$, _p$.e));
18677
19169
  _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "attributes", _v$2, _p$.t));
18678
19170
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -18752,7 +19244,7 @@ function UserRow(props) {
18752
19244
  setProp(_el$21, "flexGrow", 1);
18753
19245
  insert(_el$22, () => view.text);
18754
19246
  effect((_p$) => {
18755
- var _v$9 = theme.accent, _v$0 = TextAttributes21.BOLD, _v$1 = theme.text;
19247
+ var _v$9 = theme.accent, _v$0 = TextAttributes22.BOLD, _v$1 = theme.text;
18756
19248
  _v$9 !== _p$.e && (_p$.e = setProp(_el$19, "fg", _v$9, _p$.e));
18757
19249
  _v$0 !== _p$.t && (_p$.t = setProp(_el$19, "attributes", _v$0, _p$.t));
18758
19250
  _v$1 !== _p$.a && (_p$.a = setProp(_el$22, "fg", _v$1, _p$.a));
@@ -18788,7 +19280,7 @@ function AssistantRow(props) {
18788
19280
  }
18789
19281
  }));
18790
19282
  effect((_p$) => {
18791
- var _v$10 = theme.accent, _v$11 = TextAttributes21.BOLD;
19283
+ var _v$10 = theme.accent, _v$11 = TextAttributes22.BOLD;
18792
19284
  _v$10 !== _p$.e && (_p$.e = setProp(_el$25, "fg", _v$10, _p$.e));
18793
19285
  _v$11 !== _p$.t && (_p$.t = setProp(_el$25, "attributes", _v$11, _p$.t));
18794
19286
  return _p$;
@@ -18863,7 +19355,7 @@ function ToolRow(props) {
18863
19355
  }), null);
18864
19356
  effect((_p$) => {
18865
19357
  var _v$18 = theme.text, _v$19 = {
18866
- attributes: TextAttributes21.BOLD
19358
+ attributes: TextAttributes22.BOLD
18867
19359
  };
18868
19360
  _v$18 !== _p$.e && (_p$.e = setProp(_el$45, "fg", _v$18, _p$.e));
18869
19361
  _v$19 !== _p$.t && (_p$.t = setProp(_el$46, "style", _v$19, _p$.t));
@@ -19031,7 +19523,7 @@ function ToolRow(props) {
19031
19523
  }
19032
19524
  }), null);
19033
19525
  effect((_p$) => {
19034
- var _v$16 = prefixColor(), _v$17 = TextAttributes21.BOLD;
19526
+ var _v$16 = prefixColor(), _v$17 = TextAttributes22.BOLD;
19035
19527
  _v$16 !== _p$.e && (_p$.e = setProp(_el$29, "fg", _v$16, _p$.e));
19036
19528
  _v$17 !== _p$.t && (_p$.t = setProp(_el$29, "attributes", _v$17, _p$.t));
19037
19529
  return _p$;
@@ -19270,7 +19762,7 @@ function BashBanner(props) {
19270
19762
  }
19271
19763
  }), null);
19272
19764
  effect((_p$) => {
19273
- var _v$28 = theme.accent, _v$29 = TextAttributes21.BOLD, _v$30 = theme.text;
19765
+ var _v$28 = theme.accent, _v$29 = TextAttributes22.BOLD, _v$30 = theme.text;
19274
19766
  _v$28 !== _p$.e && (_p$.e = setProp(_el$73, "fg", _v$28, _p$.e));
19275
19767
  _v$29 !== _p$.t && (_p$.t = setProp(_el$73, "attributes", _v$29, _p$.t));
19276
19768
  _v$30 !== _p$.a && (_p$.a = setProp(_el$76, "fg", _v$30, _p$.a));
@@ -19358,7 +19850,7 @@ function ReadGrepGlobBanner(props) {
19358
19850
  }), null);
19359
19851
  effect((_p$) => {
19360
19852
  var _v$31 = theme.text, _v$32 = {
19361
- attributes: TextAttributes21.BOLD
19853
+ attributes: TextAttributes22.BOLD
19362
19854
  };
19363
19855
  _v$31 !== _p$.e && (_p$.e = setProp(_el$81, "fg", _v$31, _p$.e));
19364
19856
  _v$32 !== _p$.t && (_p$.t = setProp(_el$82, "style", _v$32, _p$.t));
@@ -19387,7 +19879,7 @@ function SystemRow(props) {
19387
19879
  setProp(_el$87, "flexGrow", 1);
19388
19880
  insert(_el$88, () => props.text);
19389
19881
  effect((_p$) => {
19390
- var _v$33 = theme.textMuted, _v$34 = TextAttributes21.DIM, _v$35 = isError() ? theme.error : theme.textMuted;
19882
+ var _v$33 = theme.textMuted, _v$34 = TextAttributes22.DIM, _v$35 = isError() ? theme.error : theme.textMuted;
19391
19883
  _v$33 !== _p$.e && (_p$.e = setProp(_el$85, "fg", _v$33, _p$.e));
19392
19884
  _v$34 !== _p$.t && (_p$.t = setProp(_el$85, "attributes", _v$34, _p$.t));
19393
19885
  _v$35 !== _p$.a && (_p$.a = setProp(_el$88, "fg", _v$35, _p$.a));
@@ -19477,7 +19969,7 @@ function ApprovalRow(props) {
19477
19969
  insertNode(_el$103, _el$105);
19478
19970
  insert(_el$103, () => r().status, _el$105);
19479
19971
  effect((_p$) => {
19480
- var _v$44 = r().status === "approved" ? theme.success : theme.error, _v$45 = TextAttributes21.BOLD;
19972
+ var _v$44 = r().status === "approved" ? theme.success : theme.error, _v$45 = TextAttributes22.BOLD;
19481
19973
  _v$44 !== _p$.e && (_p$.e = setProp(_el$103, "fg", _v$44, _p$.e));
19482
19974
  _v$45 !== _p$.t && (_p$.t = setProp(_el$103, "attributes", _v$45, _p$.t));
19483
19975
  return _p$;
@@ -19494,7 +19986,7 @@ function ApprovalRow(props) {
19494
19986
  insertNode(_el$99, createTextNode(`[ Approve ]`));
19495
19987
  setProp(_el$99, "onMouseUp", () => props.onApprove(true));
19496
19988
  effect((_p$) => {
19497
- var _v$36 = theme.success, _v$37 = TextAttributes21.BOLD;
19989
+ var _v$36 = theme.success, _v$37 = TextAttributes22.BOLD;
19498
19990
  _v$36 !== _p$.e && (_p$.e = setProp(_el$99, "fg", _v$36, _p$.e));
19499
19991
  _v$37 !== _p$.t && (_p$.t = setProp(_el$99, "attributes", _v$37, _p$.t));
19500
19992
  return _p$;
@@ -19508,7 +20000,7 @@ function ApprovalRow(props) {
19508
20000
  insertNode(_el$101, createTextNode(`[ Reject ]`));
19509
20001
  setProp(_el$101, "onMouseUp", () => props.onApprove(false));
19510
20002
  effect((_p$) => {
19511
- var _v$38 = theme.error, _v$39 = TextAttributes21.BOLD;
20003
+ var _v$38 = theme.error, _v$39 = TextAttributes22.BOLD;
19512
20004
  _v$38 !== _p$.e && (_p$.e = setProp(_el$101, "fg", _v$38, _p$.e));
19513
20005
  _v$39 !== _p$.t && (_p$.t = setProp(_el$101, "attributes", _v$39, _p$.t));
19514
20006
  return _p$;
@@ -19521,7 +20013,7 @@ function ApprovalRow(props) {
19521
20013
  }
19522
20014
  }));
19523
20015
  effect((_p$) => {
19524
- var _v$40 = headerColor(), _v$41 = TextAttributes21.BOLD, _v$42 = headerColor(), _v$43 = TextAttributes21.BOLD;
20016
+ var _v$40 = headerColor(), _v$41 = TextAttributes22.BOLD, _v$42 = headerColor(), _v$43 = TextAttributes22.BOLD;
19525
20017
  _v$40 !== _p$.e && (_p$.e = setProp(_el$91, "fg", _v$40, _p$.e));
19526
20018
  _v$41 !== _p$.t && (_p$.t = setProp(_el$91, "attributes", _v$41, _p$.t));
19527
20019
  _v$42 !== _p$.a && (_p$.a = setProp(_el$92, "fg", _v$42, _p$.a));
@@ -19543,9 +20035,35 @@ function QuestionRow(props) {
19543
20035
  const r = () => props.row;
19544
20036
  const isAnswered = () => r().answers !== null;
19545
20037
  const [selections, setSelections] = createSignal({});
20038
+ const [otherText, setOtherText] = createSignal({});
20039
+ const [currentIndex, setCurrentIndex] = createSignal(0);
19546
20040
  function pickedFor(questionText) {
19547
20041
  return selections()[questionText] ?? new Set;
19548
20042
  }
20043
+ function customTextFor(questionText) {
20044
+ return otherText()[questionText] ?? "";
20045
+ }
20046
+ function setCustomText(questionText, value) {
20047
+ const sanitized = value.replace(/[\r\n]+/g, "");
20048
+ setOtherText((prev) => ({
20049
+ ...prev,
20050
+ [questionText]: sanitized
20051
+ }));
20052
+ }
20053
+ function currentOtherActive() {
20054
+ if (isAnswered())
20055
+ return false;
20056
+ const q = r().questions[currentIndex()];
20057
+ if (!q)
20058
+ return false;
20059
+ return pickedFor(q.question).has(OTHER_SENTINEL);
20060
+ }
20061
+ createEffect(() => {
20062
+ props.onClaimComposerFocus?.(currentOtherActive());
20063
+ });
20064
+ onCleanup(() => {
20065
+ props.onClaimComposerFocus?.(false);
20066
+ });
19549
20067
  function toggle(questionText, multi, label) {
19550
20068
  setSelections((prev) => {
19551
20069
  const cur = new Set(prev[questionText] ?? []);
@@ -19568,9 +20086,34 @@ function QuestionRow(props) {
19568
20086
  };
19569
20087
  });
19570
20088
  }
20089
+ function renderedAnswerFor(q) {
20090
+ const picked = pickedFor(q.question);
20091
+ const ordered = [];
20092
+ for (const o of q.options) {
20093
+ if (picked.has(o.label))
20094
+ ordered.push(o.label);
20095
+ }
20096
+ if (picked.has(OTHER_SENTINEL)) {
20097
+ const txt = customTextFor(q.question).trim();
20098
+ if (txt)
20099
+ ordered.push(txt);
20100
+ }
20101
+ return ordered.join(", ");
20102
+ }
20103
+ function isQuestionComplete(qIdx) {
20104
+ const q = r().questions[qIdx];
20105
+ if (!q)
20106
+ return false;
20107
+ const picked = pickedFor(q.question);
20108
+ if (picked.size === 0)
20109
+ return false;
20110
+ if (picked.has(OTHER_SENTINEL) && customTextFor(q.question).trim().length === 0)
20111
+ return false;
20112
+ return true;
20113
+ }
19571
20114
  const allAnswered = () => {
19572
- for (const q of r().questions) {
19573
- if (pickedFor(q.question).size === 0)
20115
+ for (let i = 0;i < r().questions.length; i++) {
20116
+ if (!isQuestionComplete(i))
19574
20117
  return false;
19575
20118
  }
19576
20119
  return true;
@@ -19580,16 +20123,73 @@ function QuestionRow(props) {
19580
20123
  return;
19581
20124
  const answers = {};
19582
20125
  for (const q of r().questions) {
19583
- const picked = Array.from(pickedFor(q.question));
19584
- const ordered = q.options.map((o) => o.label).filter((l) => picked.includes(l));
19585
- answers[q.question] = ordered.join(", ");
20126
+ answers[q.question] = renderedAnswerFor(q);
19586
20127
  }
19587
20128
  props.onAnswer(answers);
19588
20129
  }
20130
+ function advanceOrSubmit() {
20131
+ if (isAnswered())
20132
+ return;
20133
+ const i = currentIndex();
20134
+ if (!isQuestionComplete(i))
20135
+ return;
20136
+ if (i >= r().questions.length - 1) {
20137
+ submit();
20138
+ } else {
20139
+ setCurrentIndex(i + 1);
20140
+ }
20141
+ }
20142
+ const [highlighted, setHighlighted] = createSignal(0);
20143
+ createEffect(() => {
20144
+ currentIndex();
20145
+ setHighlighted(0);
20146
+ });
20147
+ function toggleByIndex(qIdx, optIdx) {
20148
+ const q = r().questions[qIdx];
20149
+ if (!q)
20150
+ return;
20151
+ if (optIdx === q.options.length) {
20152
+ toggle(q.question, q.multiSelect, OTHER_SENTINEL);
20153
+ } else {
20154
+ const opt = q.options[optIdx];
20155
+ if (opt)
20156
+ toggle(q.question, q.multiSelect, opt.label);
20157
+ }
20158
+ }
20159
+ useBindings(() => ({
20160
+ enabled: !isAnswered() && !currentOtherActive() && (props.chatFocused?.() ?? true),
20161
+ bindings: bindByIds({
20162
+ "chat.question.nav": (evt) => {
20163
+ const q = r().questions[currentIndex()];
20164
+ if (!q)
20165
+ return;
20166
+ const max = q.options.length;
20167
+ if (evt.name === "j" || evt.name === "down") {
20168
+ setHighlighted((i) => Math.min(i + 1, max));
20169
+ } else if (evt.name === "k" || evt.name === "up") {
20170
+ setHighlighted((i) => Math.max(i - 1, 0));
20171
+ }
20172
+ },
20173
+ "chat.question.toggle": () => toggleByIndex(currentIndex(), highlighted()),
20174
+ "chat.question.submit": () => advanceOrSubmit(),
20175
+ "chat.question.pick-number": (evt) => {
20176
+ const n = Number.parseInt(evt.name ?? "", 10);
20177
+ if (!Number.isFinite(n) || n < 1)
20178
+ return;
20179
+ const q = r().questions[currentIndex()];
20180
+ if (!q)
20181
+ return;
20182
+ const idx = n - 1;
20183
+ if (idx > q.options.length)
20184
+ return;
20185
+ setHighlighted(idx);
20186
+ toggleByIndex(currentIndex(), idx);
20187
+ }
20188
+ })
20189
+ }));
19589
20190
  return (() => {
19590
- var _el$106 = createElement("box"), _el$107 = createElement("box"), _el$108 = createElement("text"), _el$110 = createElement("text"), _el$111 = createElement("box");
20191
+ var _el$106 = createElement("box"), _el$107 = createElement("box"), _el$108 = createElement("text"), _el$110 = createElement("text");
19591
20192
  insertNode(_el$106, _el$107);
19592
- insertNode(_el$106, _el$111);
19593
20193
  setProp(_el$106, "paddingTop", 1);
19594
20194
  setProp(_el$106, "flexDirection", "column");
19595
20195
  setProp(_el$106, "gap", 0);
@@ -19603,184 +20203,302 @@ function QuestionRow(props) {
19603
20203
  get each() {
19604
20204
  return r().questions;
19605
20205
  },
19606
- children: (q) => {
20206
+ children: (q, index) => {
19607
20207
  const finalAnswer = () => r().answers?.[q.question] ?? null;
19608
20208
  const picked = () => pickedFor(q.question);
19609
- return (() => {
19610
- var _el$116 = createElement("box"), _el$117 = createElement("box"), _el$121 = createElement("text");
19611
- insertNode(_el$116, _el$117);
19612
- setProp(_el$116, "paddingLeft", 2);
19613
- setProp(_el$116, "paddingTop", 1);
19614
- setProp(_el$116, "flexDirection", "column");
19615
- setProp(_el$116, "gap", 0);
19616
- insertNode(_el$117, _el$121);
19617
- setProp(_el$117, "flexDirection", "row");
19618
- setProp(_el$117, "gap", 1);
19619
- insert(_el$117, createComponent2(Show, {
19620
- get when() {
19621
- return q.header;
19622
- },
19623
- get children() {
19624
- var _el$118 = createElement("text"), _el$119 = createTextNode(`[`), _el$120 = createTextNode(`]`);
19625
- insertNode(_el$118, _el$119);
19626
- insertNode(_el$118, _el$120);
19627
- insert(_el$118, () => q.header, _el$120);
19628
- effect((_p$) => {
19629
- var _v$52 = theme.accent, _v$53 = TextAttributes21.BOLD;
19630
- _v$52 !== _p$.e && (_p$.e = setProp(_el$118, "fg", _v$52, _p$.e));
19631
- _v$53 !== _p$.t && (_p$.t = setProp(_el$118, "attributes", _v$53, _p$.t));
19632
- return _p$;
19633
- }, {
19634
- e: undefined,
19635
- t: undefined
19636
- });
19637
- return _el$118;
19638
- }
19639
- }), _el$121);
19640
- insert(_el$121, () => q.question);
19641
- insert(_el$117, createComponent2(Show, {
19642
- get when() {
19643
- return q.multiSelect;
19644
- },
19645
- get children() {
19646
- var _el$122 = createElement("text");
19647
- insertNode(_el$122, createTextNode(`(pick any)`));
19648
- effect((_$p) => setProp(_el$122, "fg", theme.textMuted, _$p));
19649
- return _el$122;
19650
- }
19651
- }), null);
19652
- insert(_el$116, createComponent2(Show, {
19653
- get when() {
19654
- return isAnswered();
19655
- },
19656
- get children() {
19657
- var _el$124 = createElement("box"), _el$125 = createElement("text"), _el$126 = createTextNode(`\u23BF `);
19658
- insertNode(_el$124, _el$125);
19659
- setProp(_el$124, "paddingLeft", 2);
19660
- insertNode(_el$125, _el$126);
19661
- insert(_el$125, (() => {
19662
- var _c$2 = memo2(() => !!(finalAnswer() && finalAnswer().length > 0));
19663
- return () => _c$2() ? finalAnswer() : "(no answer)";
19664
- })(), null);
19665
- effect((_$p) => setProp(_el$125, "fg", theme.success, _$p));
19666
- return _el$124;
19667
- }
19668
- }), null);
19669
- insert(_el$116, createComponent2(Show, {
19670
- get when() {
19671
- return !isAnswered();
19672
- },
19673
- get children() {
19674
- var _el$127 = createElement("box");
19675
- setProp(_el$127, "paddingLeft", 2);
19676
- setProp(_el$127, "flexDirection", "column");
19677
- insert(_el$127, createComponent2(For, {
19678
- get each() {
19679
- return q.options;
19680
- },
19681
- children: (opt) => {
19682
- const isPicked = () => picked().has(opt.label);
19683
- const glyph = () => q.multiSelect ? isPicked() ? "[x]" : "[ ]" : isPicked() ? "(\u2022)" : "( )";
19684
- return (() => {
19685
- var _el$128 = createElement("box"), _el$129 = createElement("text"), _el$130 = createElement("box"), _el$131 = createElement("text");
19686
- insertNode(_el$128, _el$129);
19687
- insertNode(_el$128, _el$130);
19688
- setProp(_el$128, "flexDirection", "row");
19689
- setProp(_el$128, "gap", 1);
19690
- setProp(_el$128, "onMouseUp", () => toggle(q.question, q.multiSelect, opt.label));
19691
- insert(_el$129, glyph);
19692
- insertNode(_el$130, _el$131);
19693
- setProp(_el$130, "flexGrow", 1);
19694
- setProp(_el$130, "flexDirection", "column");
19695
- insert(_el$131, () => opt.label);
19696
- insert(_el$130, createComponent2(Show, {
19697
- get when() {
19698
- return opt.description;
19699
- },
19700
- get children() {
19701
- var _el$132 = createElement("text");
19702
- insert(_el$132, () => opt.description);
19703
- effect((_$p) => setProp(_el$132, "fg", theme.textMuted, _$p));
19704
- return _el$132;
19705
- }
19706
- }), null);
20209
+ const isCurrent = () => !isAnswered() && index() === currentIndex();
20210
+ const isPast = () => !isAnswered() && index() < currentIndex();
20211
+ const isFuture = () => !isAnswered() && index() > currentIndex();
20212
+ const isLast = () => index() === r().questions.length - 1;
20213
+ const buttonLabel = () => isLast() ? "[ Submit ]" : "[ Next ]";
20214
+ return createComponent2(Show, {
20215
+ get when() {
20216
+ return !isFuture();
20217
+ },
20218
+ get children() {
20219
+ var _el$114 = createElement("box"), _el$115 = createElement("box"), _el$119 = createElement("text");
20220
+ insertNode(_el$114, _el$115);
20221
+ setProp(_el$114, "paddingLeft", 2);
20222
+ setProp(_el$114, "paddingTop", 1);
20223
+ setProp(_el$114, "flexDirection", "column");
20224
+ setProp(_el$114, "gap", 0);
20225
+ insertNode(_el$115, _el$119);
20226
+ setProp(_el$115, "flexDirection", "row");
20227
+ setProp(_el$115, "gap", 1);
20228
+ insert(_el$115, createComponent2(Show, {
20229
+ get when() {
20230
+ return q.header;
20231
+ },
20232
+ get children() {
20233
+ var _el$116 = createElement("text"), _el$117 = createTextNode(`[`), _el$118 = createTextNode(`]`);
20234
+ insertNode(_el$116, _el$117);
20235
+ insertNode(_el$116, _el$118);
20236
+ insert(_el$116, () => q.header, _el$118);
20237
+ effect((_p$) => {
20238
+ var _v$52 = theme.accent, _v$53 = TextAttributes22.BOLD;
20239
+ _v$52 !== _p$.e && (_p$.e = setProp(_el$116, "fg", _v$52, _p$.e));
20240
+ _v$53 !== _p$.t && (_p$.t = setProp(_el$116, "attributes", _v$53, _p$.t));
20241
+ return _p$;
20242
+ }, {
20243
+ e: undefined,
20244
+ t: undefined
20245
+ });
20246
+ return _el$116;
20247
+ }
20248
+ }), _el$119);
20249
+ insert(_el$119, () => q.question);
20250
+ insert(_el$115, createComponent2(Show, {
20251
+ get when() {
20252
+ return memo2(() => !!q.multiSelect)() && isCurrent();
20253
+ },
20254
+ get children() {
20255
+ var _el$120 = createElement("text");
20256
+ insertNode(_el$120, createTextNode(`(pick any)`));
20257
+ effect((_$p) => setProp(_el$120, "fg", theme.textMuted, _$p));
20258
+ return _el$120;
20259
+ }
20260
+ }), null);
20261
+ insert(_el$115, createComponent2(Show, {
20262
+ get when() {
20263
+ return isPast();
20264
+ },
20265
+ get children() {
20266
+ var _el$122 = createElement("text");
20267
+ insertNode(_el$122, createTextNode(`(click to edit)`));
20268
+ effect((_$p) => setProp(_el$122, "fg", theme.textMuted, _$p));
20269
+ return _el$122;
20270
+ }
20271
+ }), null);
20272
+ insert(_el$114, createComponent2(Show, {
20273
+ get when() {
20274
+ return isAnswered();
20275
+ },
20276
+ get children() {
20277
+ var _el$124 = createElement("box"), _el$125 = createElement("text"), _el$126 = createTextNode(`\u23BF `);
20278
+ insertNode(_el$124, _el$125);
20279
+ setProp(_el$124, "paddingLeft", 2);
20280
+ insertNode(_el$125, _el$126);
20281
+ insert(_el$125, (() => {
20282
+ var _c$2 = memo2(() => !!(finalAnswer() && finalAnswer().length > 0));
20283
+ return () => _c$2() ? finalAnswer() : "(no answer)";
20284
+ })(), null);
20285
+ effect((_$p) => setProp(_el$125, "fg", theme.success, _$p));
20286
+ return _el$124;
20287
+ }
20288
+ }), null);
20289
+ insert(_el$114, createComponent2(Show, {
20290
+ get when() {
20291
+ return isPast();
20292
+ },
20293
+ get children() {
20294
+ var _el$127 = createElement("box"), _el$128 = createElement("text"), _el$129 = createTextNode(`\u23BF `);
20295
+ insertNode(_el$127, _el$128);
20296
+ setProp(_el$127, "paddingLeft", 2);
20297
+ insertNode(_el$128, _el$129);
20298
+ insert(_el$128, (() => {
20299
+ var _c$3 = memo2(() => renderedAnswerFor(q).length > 0);
20300
+ return () => _c$3() ? renderedAnswerFor(q) : "(no answer)";
20301
+ })(), null);
20302
+ effect((_$p) => setProp(_el$128, "fg", theme.textMuted, _$p));
20303
+ return _el$127;
20304
+ }
20305
+ }), null);
20306
+ insert(_el$114, createComponent2(Show, {
20307
+ get when() {
20308
+ return isCurrent();
20309
+ },
20310
+ get children() {
20311
+ var _el$130 = createElement("box"), _el$131 = createElement("box"), _el$132 = createElement("text");
20312
+ insertNode(_el$130, _el$131);
20313
+ setProp(_el$130, "paddingLeft", 2);
20314
+ setProp(_el$130, "flexDirection", "column");
20315
+ insert(_el$130, createComponent2(For, {
20316
+ get each() {
20317
+ return q.options;
20318
+ },
20319
+ children: (opt, optIndex) => {
20320
+ const isPicked = () => picked().has(opt.label);
20321
+ const isHl = () => highlighted() === optIndex();
20322
+ const glyph = () => q.multiSelect ? isPicked() ? "[x]" : "[ ]" : isPicked() ? "(\u2022)" : "( )";
20323
+ const digitChip = () => optIndex() < 9 ? `${optIndex() + 1}.` : " ";
20324
+ return (() => {
20325
+ var _el$135 = createElement("box"), _el$136 = createElement("text"), _el$137 = createElement("text"), _el$138 = createElement("text"), _el$139 = createElement("box"), _el$140 = createElement("text");
20326
+ insertNode(_el$135, _el$136);
20327
+ insertNode(_el$135, _el$137);
20328
+ insertNode(_el$135, _el$138);
20329
+ insertNode(_el$135, _el$139);
20330
+ setProp(_el$135, "flexDirection", "row");
20331
+ setProp(_el$135, "gap", 1);
20332
+ setProp(_el$135, "onMouseUp", () => toggle(q.question, q.multiSelect, opt.label));
20333
+ insert(_el$136, () => isHl() ? ">" : " ");
20334
+ insert(_el$137, digitChip);
20335
+ insert(_el$138, glyph);
20336
+ insertNode(_el$139, _el$140);
20337
+ setProp(_el$139, "flexGrow", 1);
20338
+ setProp(_el$139, "flexDirection", "column");
20339
+ insert(_el$140, () => opt.label);
20340
+ insert(_el$139, createComponent2(Show, {
20341
+ get when() {
20342
+ return opt.description;
20343
+ },
20344
+ get children() {
20345
+ var _el$141 = createElement("text");
20346
+ insert(_el$141, () => opt.description);
20347
+ effect((_$p) => setProp(_el$141, "fg", theme.textMuted, _$p));
20348
+ return _el$141;
20349
+ }
20350
+ }), null);
20351
+ effect((_p$) => {
20352
+ var _v$58 = isHl() ? theme.accent : theme.textMuted, _v$59 = TextAttributes22.BOLD, _v$60 = theme.textMuted, _v$61 = isPicked() ? theme.accent : theme.textMuted, _v$62 = TextAttributes22.BOLD, _v$63 = theme.text;
20353
+ _v$58 !== _p$.e && (_p$.e = setProp(_el$136, "fg", _v$58, _p$.e));
20354
+ _v$59 !== _p$.t && (_p$.t = setProp(_el$136, "attributes", _v$59, _p$.t));
20355
+ _v$60 !== _p$.a && (_p$.a = setProp(_el$137, "fg", _v$60, _p$.a));
20356
+ _v$61 !== _p$.o && (_p$.o = setProp(_el$138, "fg", _v$61, _p$.o));
20357
+ _v$62 !== _p$.i && (_p$.i = setProp(_el$138, "attributes", _v$62, _p$.i));
20358
+ _v$63 !== _p$.n && (_p$.n = setProp(_el$140, "fg", _v$63, _p$.n));
20359
+ return _p$;
20360
+ }, {
20361
+ e: undefined,
20362
+ t: undefined,
20363
+ a: undefined,
20364
+ o: undefined,
20365
+ i: undefined,
20366
+ n: undefined
20367
+ });
20368
+ return _el$135;
20369
+ })();
20370
+ }
20371
+ }), _el$131);
20372
+ insert(_el$130, () => {
20373
+ const otherPicked = () => picked().has(OTHER_SENTINEL);
20374
+ const otherIdx = q.options.length;
20375
+ const isOtherHl = () => highlighted() === otherIdx;
20376
+ const otherGlyph = () => q.multiSelect ? otherPicked() ? "[x]" : "[ ]" : otherPicked() ? "(\u2022)" : "( )";
20377
+ const otherDigitChip = () => otherIdx < 9 ? `${otherIdx + 1}.` : " ";
20378
+ return [(() => {
20379
+ var _el$142 = createElement("box"), _el$143 = createElement("text"), _el$144 = createElement("text"), _el$145 = createElement("text"), _el$146 = createElement("box"), _el$147 = createElement("text"), _el$149 = createElement("text");
20380
+ insertNode(_el$142, _el$143);
20381
+ insertNode(_el$142, _el$144);
20382
+ insertNode(_el$142, _el$145);
20383
+ insertNode(_el$142, _el$146);
20384
+ setProp(_el$142, "flexDirection", "row");
20385
+ setProp(_el$142, "gap", 1);
20386
+ setProp(_el$142, "onMouseUp", () => toggle(q.question, q.multiSelect, OTHER_SENTINEL));
20387
+ insert(_el$143, () => isOtherHl() ? ">" : " ");
20388
+ insert(_el$144, otherDigitChip);
20389
+ insert(_el$145, otherGlyph);
20390
+ insertNode(_el$146, _el$147);
20391
+ insertNode(_el$146, _el$149);
20392
+ setProp(_el$146, "flexGrow", 1);
20393
+ setProp(_el$146, "flexDirection", "column");
20394
+ insertNode(_el$147, createTextNode(`Other`));
20395
+ insertNode(_el$149, createTextNode(`Type your own answer`));
19707
20396
  effect((_p$) => {
19708
- var _v$54 = isPicked() ? theme.accent : theme.textMuted, _v$55 = TextAttributes21.BOLD, _v$56 = theme.text;
19709
- _v$54 !== _p$.e && (_p$.e = setProp(_el$129, "fg", _v$54, _p$.e));
19710
- _v$55 !== _p$.t && (_p$.t = setProp(_el$129, "attributes", _v$55, _p$.t));
19711
- _v$56 !== _p$.a && (_p$.a = setProp(_el$131, "fg", _v$56, _p$.a));
20397
+ var _v$64 = isOtherHl() ? theme.accent : theme.textMuted, _v$65 = TextAttributes22.BOLD, _v$66 = theme.textMuted, _v$67 = otherPicked() ? theme.accent : theme.textMuted, _v$68 = TextAttributes22.BOLD, _v$69 = theme.text, _v$70 = theme.textMuted;
20398
+ _v$64 !== _p$.e && (_p$.e = setProp(_el$143, "fg", _v$64, _p$.e));
20399
+ _v$65 !== _p$.t && (_p$.t = setProp(_el$143, "attributes", _v$65, _p$.t));
20400
+ _v$66 !== _p$.a && (_p$.a = setProp(_el$144, "fg", _v$66, _p$.a));
20401
+ _v$67 !== _p$.o && (_p$.o = setProp(_el$145, "fg", _v$67, _p$.o));
20402
+ _v$68 !== _p$.i && (_p$.i = setProp(_el$145, "attributes", _v$68, _p$.i));
20403
+ _v$69 !== _p$.n && (_p$.n = setProp(_el$147, "fg", _v$69, _p$.n));
20404
+ _v$70 !== _p$.s && (_p$.s = setProp(_el$149, "fg", _v$70, _p$.s));
19712
20405
  return _p$;
19713
20406
  }, {
19714
20407
  e: undefined,
19715
20408
  t: undefined,
19716
- a: undefined
20409
+ a: undefined,
20410
+ o: undefined,
20411
+ i: undefined,
20412
+ n: undefined,
20413
+ s: undefined
19717
20414
  });
19718
- return _el$128;
19719
- })();
19720
- }
19721
- }));
19722
- return _el$127;
19723
- }
19724
- }), null);
19725
- effect((_$p) => setProp(_el$121, "fg", theme.text, _$p));
19726
- return _el$116;
19727
- })();
20415
+ return _el$142;
20416
+ })(), createComponent2(Show, {
20417
+ get when() {
20418
+ return otherPicked();
20419
+ },
20420
+ get children() {
20421
+ var _el$151 = createElement("box"), _el$152 = createElement("input");
20422
+ insertNode(_el$151, _el$152);
20423
+ setProp(_el$151, "paddingLeft", 4);
20424
+ setProp(_el$151, "paddingTop", 0);
20425
+ setProp(_el$152, "placeholder", "type your answer\u2026");
20426
+ setProp(_el$152, "focused", true);
20427
+ setProp(_el$152, "onInput", (v) => setCustomText(q.question, v));
20428
+ setProp(_el$152, "onSubmit", () => advanceOrSubmit());
20429
+ effect((_$p) => setProp(_el$152, "value", customTextFor(q.question), _$p));
20430
+ return _el$151;
20431
+ }
20432
+ })];
20433
+ }, _el$131);
20434
+ insertNode(_el$131, _el$132);
20435
+ setProp(_el$131, "paddingLeft", 0);
20436
+ setProp(_el$131, "paddingTop", 1);
20437
+ setProp(_el$131, "flexDirection", "row");
20438
+ setProp(_el$131, "gap", 2);
20439
+ setProp(_el$132, "onMouseUp", () => advanceOrSubmit());
20440
+ insert(_el$132, buttonLabel);
20441
+ insert(_el$131, createComponent2(Show, {
20442
+ get when() {
20443
+ return !isQuestionComplete(index());
20444
+ },
20445
+ get children() {
20446
+ var _el$133 = createElement("text");
20447
+ insertNode(_el$133, createTextNode(`(pick an option to continue)`));
20448
+ effect((_$p) => setProp(_el$133, "fg", theme.textMuted, _$p));
20449
+ return _el$133;
20450
+ }
20451
+ }), null);
20452
+ effect((_p$) => {
20453
+ var _v$54 = isQuestionComplete(index()) ? theme.success : theme.textMuted, _v$55 = TextAttributes22.BOLD;
20454
+ _v$54 !== _p$.e && (_p$.e = setProp(_el$132, "fg", _v$54, _p$.e));
20455
+ _v$55 !== _p$.t && (_p$.t = setProp(_el$132, "attributes", _v$55, _p$.t));
20456
+ return _p$;
20457
+ }, {
20458
+ e: undefined,
20459
+ t: undefined
20460
+ });
20461
+ return _el$130;
20462
+ }
20463
+ }), null);
20464
+ effect((_p$) => {
20465
+ var _v$56 = isPast() ? () => setCurrentIndex(index()) : undefined, _v$57 = isPast() ? theme.textMuted : theme.text;
20466
+ _v$56 !== _p$.e && (_p$.e = setProp(_el$114, "onMouseUp", _v$56, _p$.e));
20467
+ _v$57 !== _p$.t && (_p$.t = setProp(_el$119, "fg", _v$57, _p$.t));
20468
+ return _p$;
20469
+ }, {
20470
+ e: undefined,
20471
+ t: undefined
20472
+ });
20473
+ return _el$114;
20474
+ }
20475
+ });
19728
20476
  }
19729
- }), _el$111);
19730
- setProp(_el$111, "paddingLeft", 2);
19731
- setProp(_el$111, "paddingTop", 1);
19732
- setProp(_el$111, "flexDirection", "row");
19733
- setProp(_el$111, "gap", 2);
19734
- insert(_el$111, createComponent2(Show, {
20477
+ }), null);
20478
+ insert(_el$106, createComponent2(Show, {
19735
20479
  get when() {
19736
- return !isAnswered();
19737
- },
19738
- get fallback() {
19739
- return (() => {
19740
- var _el$133 = createElement("text");
19741
- insertNode(_el$133, createTextNode(`[submitted]`));
19742
- effect((_p$) => {
19743
- var _v$57 = theme.success, _v$58 = TextAttributes21.BOLD;
19744
- _v$57 !== _p$.e && (_p$.e = setProp(_el$133, "fg", _v$57, _p$.e));
19745
- _v$58 !== _p$.t && (_p$.t = setProp(_el$133, "attributes", _v$58, _p$.t));
19746
- return _p$;
19747
- }, {
19748
- e: undefined,
19749
- t: undefined
19750
- });
19751
- return _el$133;
19752
- })();
20480
+ return isAnswered();
19753
20481
  },
19754
20482
  get children() {
19755
- return [(() => {
19756
- var _el$112 = createElement("text");
19757
- insertNode(_el$112, createTextNode(`[ Submit ]`));
19758
- setProp(_el$112, "onMouseUp", () => submit());
19759
- effect((_p$) => {
19760
- var _v$46 = allAnswered() ? theme.success : theme.textMuted, _v$47 = TextAttributes21.BOLD;
19761
- _v$46 !== _p$.e && (_p$.e = setProp(_el$112, "fg", _v$46, _p$.e));
19762
- _v$47 !== _p$.t && (_p$.t = setProp(_el$112, "attributes", _v$47, _p$.t));
19763
- return _p$;
19764
- }, {
19765
- e: undefined,
19766
- t: undefined
19767
- });
19768
- return _el$112;
19769
- })(), createComponent2(Show, {
19770
- get when() {
19771
- return !allAnswered();
19772
- },
19773
- get children() {
19774
- var _el$114 = createElement("text");
19775
- insertNode(_el$114, createTextNode(`(answer all questions to enable)`));
19776
- effect((_$p) => setProp(_el$114, "fg", theme.textMuted, _$p));
19777
- return _el$114;
19778
- }
19779
- })];
20483
+ var _el$111 = createElement("box"), _el$112 = createElement("text");
20484
+ insertNode(_el$111, _el$112);
20485
+ setProp(_el$111, "paddingLeft", 2);
20486
+ setProp(_el$111, "paddingTop", 1);
20487
+ insertNode(_el$112, createTextNode(`[submitted]`));
20488
+ effect((_p$) => {
20489
+ var _v$46 = theme.success, _v$47 = TextAttributes22.BOLD;
20490
+ _v$46 !== _p$.e && (_p$.e = setProp(_el$112, "fg", _v$46, _p$.e));
20491
+ _v$47 !== _p$.t && (_p$.t = setProp(_el$112, "attributes", _v$47, _p$.t));
20492
+ return _p$;
20493
+ }, {
20494
+ e: undefined,
20495
+ t: undefined
20496
+ });
20497
+ return _el$111;
19780
20498
  }
19781
- }));
20499
+ }), null);
19782
20500
  effect((_p$) => {
19783
- var _v$48 = theme.warning, _v$49 = TextAttributes21.BOLD, _v$50 = theme.warning, _v$51 = TextAttributes21.BOLD;
20501
+ var _v$48 = theme.warning, _v$49 = TextAttributes22.BOLD, _v$50 = theme.warning, _v$51 = TextAttributes22.BOLD;
19784
20502
  _v$48 !== _p$.e && (_p$.e = setProp(_el$108, "fg", _v$48, _p$.e));
19785
20503
  _v$49 !== _p$.t && (_p$.t = setProp(_el$108, "attributes", _v$49, _p$.t));
19786
20504
  _v$50 !== _p$.a && (_p$.a = setProp(_el$110, "fg", _v$50, _p$.a));
@@ -19800,23 +20518,23 @@ function MessageList(props) {
19800
20518
  theme
19801
20519
  } = useTheme();
19802
20520
  return (() => {
19803
- var _el$135 = createElement("box");
19804
- setProp(_el$135, "flexDirection", "column");
19805
- setProp(_el$135, "gap", 0);
19806
- insert(_el$135, createComponent2(Show, {
20521
+ var _el$153 = createElement("box");
20522
+ setProp(_el$153, "flexDirection", "column");
20523
+ setProp(_el$153, "gap", 0);
20524
+ insert(_el$153, createComponent2(Show, {
19807
20525
  get when() {
19808
20526
  return memo2(() => props.messages.length === 0)() && props.showEmptyPlaceholder;
19809
20527
  },
19810
20528
  get children() {
19811
- var _el$136 = createElement("box"), _el$137 = createElement("text");
19812
- insertNode(_el$136, _el$137);
19813
- setProp(_el$136, "paddingTop", 2);
19814
- insertNode(_el$137, createTextNode(`Type a prompt below.`));
19815
- effect((_$p) => setProp(_el$137, "fg", theme.textMuted, _$p));
19816
- return _el$136;
20529
+ var _el$154 = createElement("box"), _el$155 = createElement("text");
20530
+ insertNode(_el$154, _el$155);
20531
+ setProp(_el$154, "paddingTop", 2);
20532
+ insertNode(_el$155, createTextNode(`Type a prompt below.`));
20533
+ effect((_$p) => setProp(_el$155, "fg", theme.textMuted, _$p));
20534
+ return _el$154;
19817
20535
  }
19818
20536
  }), null);
19819
- insert(_el$135, createComponent2(For, {
20537
+ insert(_el$153, createComponent2(For, {
19820
20538
  get each() {
19821
20539
  return groupRenderItems(props.messages, props.expandedFoldStartIndex);
19822
20540
  },
@@ -19866,7 +20584,13 @@ function MessageList(props) {
19866
20584
  if (row.kind === "question") {
19867
20585
  return createComponent2(QuestionRow, {
19868
20586
  row,
19869
- onAnswer: (answers) => props.onAnswer?.(row.requestId, answers)
20587
+ onAnswer: (answers) => props.onAnswer?.(row.requestId, answers),
20588
+ get onClaimComposerFocus() {
20589
+ return props.onClaimComposerFocus;
20590
+ },
20591
+ get chatFocused() {
20592
+ return props.chatFocused;
20593
+ }
19870
20594
  });
19871
20595
  }
19872
20596
  return createComponent2(ToolRow, {
@@ -19879,35 +20603,35 @@ function MessageList(props) {
19879
20603
  });
19880
20604
  }
19881
20605
  }), null);
19882
- insert(_el$135, createComponent2(Show, {
20606
+ insert(_el$153, createComponent2(Show, {
19883
20607
  get when() {
19884
20608
  return props.error;
19885
20609
  },
19886
20610
  get children() {
19887
- var _el$139 = createElement("box"), _el$140 = createElement("text"), _el$142 = createElement("text"), _el$143 = createTextNode(`error: `);
19888
- insertNode(_el$139, _el$140);
19889
- insertNode(_el$139, _el$142);
19890
- setProp(_el$139, "paddingTop", 1);
19891
- setProp(_el$139, "flexDirection", "row");
19892
- setProp(_el$139, "gap", 1);
19893
- insertNode(_el$140, createTextNode(`\u203B`));
19894
- insertNode(_el$142, _el$143);
19895
- insert(_el$142, () => props.error, null);
20611
+ var _el$157 = createElement("box"), _el$158 = createElement("text"), _el$160 = createElement("text"), _el$161 = createTextNode(`error: `);
20612
+ insertNode(_el$157, _el$158);
20613
+ insertNode(_el$157, _el$160);
20614
+ setProp(_el$157, "paddingTop", 1);
20615
+ setProp(_el$157, "flexDirection", "row");
20616
+ setProp(_el$157, "gap", 1);
20617
+ insertNode(_el$158, createTextNode(`\u203B`));
20618
+ insertNode(_el$160, _el$161);
20619
+ insert(_el$160, () => props.error, null);
19896
20620
  effect((_p$) => {
19897
- var _v$59 = theme.error, _v$60 = TextAttributes21.BOLD, _v$61 = theme.error;
19898
- _v$59 !== _p$.e && (_p$.e = setProp(_el$140, "fg", _v$59, _p$.e));
19899
- _v$60 !== _p$.t && (_p$.t = setProp(_el$140, "attributes", _v$60, _p$.t));
19900
- _v$61 !== _p$.a && (_p$.a = setProp(_el$142, "fg", _v$61, _p$.a));
20621
+ var _v$71 = theme.error, _v$72 = TextAttributes22.BOLD, _v$73 = theme.error;
20622
+ _v$71 !== _p$.e && (_p$.e = setProp(_el$158, "fg", _v$71, _p$.e));
20623
+ _v$72 !== _p$.t && (_p$.t = setProp(_el$158, "attributes", _v$72, _p$.t));
20624
+ _v$73 !== _p$.a && (_p$.a = setProp(_el$160, "fg", _v$73, _p$.a));
19901
20625
  return _p$;
19902
20626
  }, {
19903
20627
  e: undefined,
19904
20628
  t: undefined,
19905
20629
  a: undefined
19906
20630
  });
19907
- return _el$139;
20631
+ return _el$157;
19908
20632
  }
19909
20633
  }), null);
19910
- return _el$135;
20634
+ return _el$153;
19911
20635
  })();
19912
20636
  }
19913
20637
  function groupRenderItems(messages, expandedFoldStartIndex = null) {
@@ -20038,32 +20762,32 @@ function ToolFoldRow(props) {
20038
20762
  const glyph = () => props.inFlight ? "\u273B" : props.expanded ? "\u25BC" : "\u25B6";
20039
20763
  const fg = () => props.inFlight ? theme.warning : theme.textMuted;
20040
20764
  return (() => {
20041
- var _el$144 = createElement("box"), _el$145 = createElement("text"), _el$146 = createElement("box"), _el$147 = createElement("text");
20042
- insertNode(_el$144, _el$145);
20043
- insertNode(_el$144, _el$146);
20044
- setProp(_el$144, "paddingTop", 1);
20045
- setProp(_el$144, "flexDirection", "row");
20046
- setProp(_el$144, "gap", 1);
20047
- setProp(_el$144, "onMouseUp", () => props.onToggle());
20048
- insert(_el$145, glyph);
20049
- insertNode(_el$146, _el$147);
20050
- setProp(_el$146, "flexGrow", 1);
20051
- insert(_el$147, () => props.summary);
20765
+ var _el$162 = createElement("box"), _el$163 = createElement("text"), _el$164 = createElement("box"), _el$165 = createElement("text");
20766
+ insertNode(_el$162, _el$163);
20767
+ insertNode(_el$162, _el$164);
20768
+ setProp(_el$162, "paddingTop", 1);
20769
+ setProp(_el$162, "flexDirection", "row");
20770
+ setProp(_el$162, "gap", 1);
20771
+ setProp(_el$162, "onMouseUp", () => props.onToggle());
20772
+ insert(_el$163, glyph);
20773
+ insertNode(_el$164, _el$165);
20774
+ setProp(_el$164, "flexGrow", 1);
20775
+ insert(_el$165, () => props.summary);
20052
20776
  effect((_p$) => {
20053
- var _v$62 = fg(), _v$63 = TextAttributes21.DIM, _v$64 = theme.textMuted;
20054
- _v$62 !== _p$.e && (_p$.e = setProp(_el$145, "fg", _v$62, _p$.e));
20055
- _v$63 !== _p$.t && (_p$.t = setProp(_el$145, "attributes", _v$63, _p$.t));
20056
- _v$64 !== _p$.a && (_p$.a = setProp(_el$147, "fg", _v$64, _p$.a));
20777
+ var _v$74 = fg(), _v$75 = TextAttributes22.DIM, _v$76 = theme.textMuted;
20778
+ _v$74 !== _p$.e && (_p$.e = setProp(_el$163, "fg", _v$74, _p$.e));
20779
+ _v$75 !== _p$.t && (_p$.t = setProp(_el$163, "attributes", _v$75, _p$.t));
20780
+ _v$76 !== _p$.a && (_p$.a = setProp(_el$165, "fg", _v$76, _p$.a));
20057
20781
  return _p$;
20058
20782
  }, {
20059
20783
  e: undefined,
20060
20784
  t: undefined,
20061
20785
  a: undefined
20062
20786
  });
20063
- return _el$144;
20787
+ return _el$162;
20064
20788
  })();
20065
20789
  }
20066
- var BLACK_CIRCLE, TOOL_FOLD_THRESHOLD = 3;
20790
+ var BLACK_CIRCLE, OTHER_SENTINEL = "__kobe_other__", TOOL_FOLD_THRESHOLD = 3;
20067
20791
  var init_MessageList = __esm(() => {
20068
20792
  init_solid();
20069
20793
  init_solid();
@@ -20074,7 +20798,9 @@ var init_MessageList = __esm(() => {
20074
20798
  init_solid();
20075
20799
  init_solid();
20076
20800
  init_dev();
20801
+ init_keybindings();
20077
20802
  init_theme();
20803
+ init_keymap();
20078
20804
  init_Markdown();
20079
20805
  init_image_paste();
20080
20806
  BLACK_CIRCLE = process.platform === "darwin" ? "\u23FA" : "\u25CF";
@@ -20113,7 +20839,7 @@ var init_models = __esm(() => {
20113
20839
  });
20114
20840
 
20115
20841
  // src/tui/panes/chat/composer/ModelPicker.tsx
20116
- import { TextAttributes as TextAttributes22 } from "@opentui/core";
20842
+ import { TextAttributes as TextAttributes23 } from "@opentui/core";
20117
20843
  function ModelPicker(props) {
20118
20844
  const dialog = useDialog();
20119
20845
  const {
@@ -20193,7 +20919,7 @@ function ModelPicker(props) {
20193
20919
  })() : null;
20194
20920
  })(), null);
20195
20921
  effect((_p$) => {
20196
- var _v$5 = active() ? theme.primary : undefined, _v$6 = active() ? theme.selectedListItemText : theme.text, _v$7 = active() ? TextAttributes22.BOLD : undefined;
20922
+ var _v$5 = active() ? theme.primary : undefined, _v$6 = active() ? theme.selectedListItemText : theme.text, _v$7 = active() ? TextAttributes23.BOLD : undefined;
20197
20923
  _v$5 !== _p$.e && (_p$.e = setProp(_el$1, "backgroundColor", _v$5, _p$.e));
20198
20924
  _v$6 !== _p$.t && (_p$.t = setProp(_el$10, "fg", _v$6, _p$.t));
20199
20925
  _v$7 !== _p$.a && (_p$.a = setProp(_el$10, "attributes", _v$7, _p$.a));
@@ -20211,7 +20937,7 @@ function ModelPicker(props) {
20211
20937
  setProp(_el$8, "paddingBottom", 1);
20212
20938
  insertNode(_el$9, createTextNode(`\u2191\u2193 pick \xB7 enter select \xB7 esc cancel`));
20213
20939
  effect((_p$) => {
20214
- var _v$ = TextAttributes22.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted;
20940
+ var _v$ = TextAttributes23.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted;
20215
20941
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
20216
20942
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
20217
20943
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -20241,12 +20967,12 @@ var init_ModelPicker = __esm(() => {
20241
20967
  init_dialog();
20242
20968
  init_models();
20243
20969
  ModelPicker.show = (dialog, current) => {
20244
- return new Promise((resolve3) => {
20970
+ return new Promise((resolve4) => {
20245
20971
  dialog.replace(() => createComponent2(ModelPicker, {
20246
20972
  current,
20247
- onPick: (id) => resolve3(id),
20248
- onCancel: () => resolve3(undefined)
20249
- }), () => resolve3(undefined));
20973
+ onPick: (id) => resolve4(id),
20974
+ onCancel: () => resolve4(undefined)
20975
+ }), () => resolve4(undefined));
20250
20976
  });
20251
20977
  };
20252
20978
  });
@@ -20254,7 +20980,7 @@ var init_ModelPicker = __esm(() => {
20254
20980
  // src/tui/panes/chat/composer/user-slashes.ts
20255
20981
  import { readFile as readFile6, readdir as readdir3, stat as stat3 } from "fs/promises";
20256
20982
  import { homedir as homedir10 } from "os";
20257
- import { join as join13 } from "path";
20983
+ import { join as join14 } from "path";
20258
20984
  function resolveHome() {
20259
20985
  return process.env.HOME ?? homedir10();
20260
20986
  }
@@ -20360,7 +21086,7 @@ async function scanCommandsDir(dir) {
20360
21086
  for (const entry of entries) {
20361
21087
  if (!entry.endsWith(".md"))
20362
21088
  continue;
20363
- const full = join13(dir, entry);
21089
+ const full = join14(dir, entry);
20364
21090
  if (!await isFile(full))
20365
21091
  continue;
20366
21092
  const name = entry.slice(0, -3);
@@ -20373,10 +21099,10 @@ async function scanSkillsDir(dir) {
20373
21099
  const entries = await safeReaddir(dir);
20374
21100
  const out = [];
20375
21101
  for (const entry of entries) {
20376
- const sub = join13(dir, entry);
21102
+ const sub = join14(dir, entry);
20377
21103
  if (!await isDir(sub))
20378
21104
  continue;
20379
- const skillMd = join13(sub, "SKILL.md");
21105
+ const skillMd = join14(sub, "SKILL.md");
20380
21106
  if (!await isFile(skillMd))
20381
21107
  continue;
20382
21108
  const description = await tryReadDescription(skillMd) ?? "";
@@ -20386,8 +21112,8 @@ async function scanSkillsDir(dir) {
20386
21112
  }
20387
21113
  async function scanBasePath(claudeDir) {
20388
21114
  const [skills, commands] = await Promise.all([
20389
- scanSkillsDir(join13(claudeDir, "skills")),
20390
- scanCommandsDir(join13(claudeDir, "commands"))
21115
+ scanSkillsDir(join14(claudeDir, "skills")),
21116
+ scanCommandsDir(join14(claudeDir, "commands"))
20391
21117
  ]);
20392
21118
  const map = new Map;
20393
21119
  for (const e of skills)
@@ -20397,8 +21123,8 @@ async function scanBasePath(claudeDir) {
20397
21123
  return [...map.values()];
20398
21124
  }
20399
21125
  async function loadUserSlashes(worktreePath) {
20400
- const projectScan = worktreePath ? scanBasePath(join13(worktreePath, ".claude")) : Promise.resolve([]);
20401
- const globalScan = scanBasePath(join13(resolveHome(), ".claude"));
21126
+ const projectScan = worktreePath ? scanBasePath(join14(worktreePath, ".claude")) : Promise.resolve([]);
21127
+ const globalScan = scanBasePath(join14(resolveHome(), ".claude"));
20402
21128
  const [project, global] = await Promise.all([projectScan, globalScan]);
20403
21129
  const map = new Map;
20404
21130
  for (const e of global)
@@ -20607,6 +21333,8 @@ function applyEvent(state, ev, nowIso = new Date().toISOString()) {
20607
21333
  ...state,
20608
21334
  messages: capMessages([...state.messages, { kind: "system", text: ev.text, ts: nowIso }], nowIso)
20609
21335
  };
21336
+ case "chat.tab.cleared":
21337
+ return createInitialState();
20610
21338
  case "user_input.request": {
20611
21339
  if (ev.payload.kind === "approve_plan") {
20612
21340
  return {
@@ -20973,7 +21701,7 @@ var init_use_chat_session = __esm(() => {
20973
21701
  });
20974
21702
 
20975
21703
  // src/tui/panes/chat/Chat.tsx
20976
- import { TextAttributes as TextAttributes23 } from "@opentui/core";
21704
+ import { TextAttributes as TextAttributes24 } from "@opentui/core";
20977
21705
  function Chat(props) {
20978
21706
  const {
20979
21707
  theme
@@ -20997,7 +21725,7 @@ function Chat(props) {
20997
21725
  entry: e,
20998
21726
  source: "user"
20999
21727
  });
21000
- return [...map.values()].sort((a, b) => a.entry.name.localeCompare(b.entry.name)).map(({
21728
+ const claudeEntries = [...map.values()].map(({
21001
21729
  entry,
21002
21730
  source
21003
21731
  }) => ({
@@ -21009,6 +21737,15 @@ function Chat(props) {
21009
21737
  send(`/${entry.name}`);
21010
21738
  }
21011
21739
  }));
21740
+ const kobeEntries = [{
21741
+ display: "/clear",
21742
+ description: "Reset this chat tab (drops session, keeps history on disk)",
21743
+ source: "builtin",
21744
+ onSelect: () => {
21745
+ send("/clear");
21746
+ }
21747
+ }];
21748
+ return [...kobeEntries, ...claudeEntries].sort((a, b) => a.display.localeCompare(b.display));
21012
21749
  });
21013
21750
  const session = useChatSession({
21014
21751
  taskId: () => props.taskId(),
@@ -21046,28 +21783,40 @@ function Chat(props) {
21046
21783
  createEffect(on(contextMeterLabel, (label) => {
21047
21784
  props.onContextMeter?.(label ?? null);
21048
21785
  }));
21049
- const taskStatus = createMemo(() => {
21786
+ const activeTask = createMemo(() => {
21050
21787
  const id = props.taskId();
21051
21788
  if (!id)
21052
21789
  return;
21053
- return tasksAcc().find((t) => t.id === id)?.status;
21790
+ return tasksAcc().find((t) => t.id === id);
21054
21791
  });
21792
+ const taskStatus = () => activeTask()?.status;
21055
21793
  const isCanceled = () => taskStatus() === "canceled";
21056
- const hasPendingInput = createMemo(() => {
21794
+ const isArchived = () => activeTask()?.archived === true;
21795
+ function findPending() {
21057
21796
  const msgs = activeState().messages;
21058
21797
  for (let i = msgs.length - 1;i >= 0; i--) {
21059
21798
  const m = msgs[i];
21060
21799
  if (!m)
21061
21800
  continue;
21062
21801
  if (m.kind === "approval")
21063
- return m.status === "pending";
21802
+ return m.status === "pending" ? m : null;
21064
21803
  if (m.kind === "question")
21065
- return m.answers === null;
21804
+ return m.answers === null ? m : null;
21066
21805
  if (m.kind === "user" || m.kind === "assistant")
21067
- return false;
21806
+ return null;
21068
21807
  }
21069
- return false;
21808
+ return null;
21809
+ }
21810
+ const pendingApproval = createMemo(() => {
21811
+ const p = findPending();
21812
+ return p?.kind === "approval" ? p : null;
21070
21813
  });
21814
+ const pendingQuestion = createMemo(() => {
21815
+ const p = findPending();
21816
+ return p?.kind === "question" ? p : null;
21817
+ });
21818
+ const hasPendingInput = createMemo(() => pendingApproval() !== null || pendingQuestion() !== null);
21819
+ const [questionInlineFocus, setQuestionInlineFocus] = createSignal(false);
21071
21820
  const permissionMode = createMemo(() => {
21072
21821
  const id = props.taskId();
21073
21822
  if (!id)
@@ -21209,6 +21958,15 @@ function Chat(props) {
21209
21958
  return;
21210
21959
  if (hasPendingInput())
21211
21960
  return;
21961
+ if (text === "/clear") {
21962
+ setDraft("");
21963
+ try {
21964
+ await props.orchestrator.clearTab(taskId, tabId);
21965
+ } catch (err) {
21966
+ patchActiveState((s) => pushSystemError(s, `/clear failed: ${stringifyErr2(err)}`));
21967
+ }
21968
+ return;
21969
+ }
21212
21970
  const streaming = activeState().isStreaming;
21213
21971
  if (streaming && mode === "steer") {
21214
21972
  setDraft("");
@@ -21389,6 +22147,24 @@ function Chat(props) {
21389
22147
  toggleExpandLastTool();
21390
22148
  return;
21391
22149
  }
22150
+ const q = pendingQuestion();
22151
+ if (q) {
22152
+ const taskId = props.taskId();
22153
+ if (!taskId)
22154
+ return;
22155
+ const answers = {};
22156
+ for (const entry of q.questions) {
22157
+ answers[entry.question] = trimmed;
22158
+ }
22159
+ props.orchestrator.respondToInput(taskId, q.requestId, {
22160
+ kind: "ask_question",
22161
+ answers
22162
+ }).catch((err) => {
22163
+ patchActiveState((s) => pushSystemError(s, `respondToInput failed: ${stringifyErr2(err)}`));
22164
+ });
22165
+ setDraft("");
22166
+ return;
22167
+ }
21392
22168
  send(trimmed, mode);
21393
22169
  }
21394
22170
  return (() => {
@@ -21466,7 +22242,9 @@ function Chat(props) {
21466
22242
  }).catch((err) => {
21467
22243
  patchActiveState((s) => pushSystemError(s, `respondToInput failed: ${stringifyErr2(err)}`));
21468
22244
  });
21469
- }
22245
+ },
22246
+ onClaimComposerFocus: setQuestionInlineFocus,
22247
+ chatFocused: () => props.focused?.() ?? false
21470
22248
  }));
21471
22249
  effect((_$p) => setProp(_el$5, "verticalScrollbarOptions", {
21472
22250
  trackOptions: {
@@ -21505,33 +22283,38 @@ function Chat(props) {
21505
22283
  });
21506
22284
  }
21507
22285
  }), null);
21508
- insert(_el$, createComponent2(Composer, {
21509
- get draft() {
21510
- return draft();
21511
- },
21512
- onDraftChange: setDraft,
21513
- get isStreaming() {
21514
- return activeState().isStreaming;
21515
- },
21516
- get hasTask() {
21517
- return memo2(() => !!(props.taskId() !== undefined && !isCanceled()))() && !hasPendingInput();
21518
- },
21519
- get noTaskMessage() {
21520
- return memo2(() => !!isCanceled())() ? "(task canceled \u2014 pick another or press ctrl+n to create)" : hasPendingInput() ? "(answer the prompt above to continue)" : undefined;
21521
- },
21522
- onSubmit: handleComposerSubmit,
21523
- get focused() {
21524
- return props.focused;
21525
- },
21526
- get historyKey() {
21527
- return activeTabId() ?? props.taskId();
22286
+ insert(_el$, createComponent2(Show, {
22287
+ get when() {
22288
+ return !pendingQuestion();
21528
22289
  },
21529
- slashes,
21530
- permissionMode,
21531
- onCyclePermissionMode: cyclePermissionMode,
21532
- modelLabel,
21533
- onChooseModel: () => void chooseModel(),
21534
- worktreePath
22290
+ get children() {
22291
+ return createComponent2(Composer, {
22292
+ get draft() {
22293
+ return draft();
22294
+ },
22295
+ onDraftChange: setDraft,
22296
+ get isStreaming() {
22297
+ return activeState().isStreaming;
22298
+ },
22299
+ get hasTask() {
22300
+ return memo2(() => !!(props.taskId() !== undefined && !isArchived() && !isCanceled()))() && !pendingApproval();
22301
+ },
22302
+ get noTaskMessage() {
22303
+ return memo2(() => !!isArchived())() ? "(archived \u2014 unarchive to resume)" : memo2(() => !!isCanceled())() ? "(task canceled \u2014 pick another or press ctrl+n to create)" : pendingApproval() ? "(answer the prompt above to continue)" : undefined;
22304
+ },
22305
+ onSubmit: handleComposerSubmit,
22306
+ focused: () => (props.focused?.() ?? false) && !questionInlineFocus(),
22307
+ get historyKey() {
22308
+ return activeTabId() ?? props.taskId();
22309
+ },
22310
+ slashes,
22311
+ permissionMode,
22312
+ onCyclePermissionMode: cyclePermissionMode,
22313
+ modelLabel,
22314
+ onChooseModel: () => void chooseModel(),
22315
+ worktreePath
22316
+ });
22317
+ }
21535
22318
  }), null);
21536
22319
  return _el$;
21537
22320
  })();
@@ -21586,7 +22369,7 @@ function QueuedPromptList(props) {
21586
22369
  insertNode(_el$19, createTextNode(`[x]`));
21587
22370
  setProp(_el$19, "onMouseUp", () => props.onCancel(entry.id));
21588
22371
  effect((_p$) => {
21589
- var _v$4 = theme.textMuted, _v$5 = TextAttributes23.BOLD, _v$6 = theme.textMuted, _v$7 = theme.text, _v$8 = theme.error, _v$9 = TextAttributes23.BOLD;
22372
+ var _v$4 = theme.textMuted, _v$5 = TextAttributes24.BOLD, _v$6 = theme.textMuted, _v$7 = theme.text, _v$8 = theme.error, _v$9 = TextAttributes24.BOLD;
21590
22373
  _v$4 !== _p$.e && (_p$.e = setProp(_el$11, "fg", _v$4, _p$.e));
21591
22374
  _v$5 !== _p$.t && (_p$.t = setProp(_el$11, "attributes", _v$5, _p$.t));
21592
22375
  _v$6 !== _p$.a && (_p$.a = setProp(_el$14, "fg", _v$6, _p$.a));
@@ -21619,7 +22402,7 @@ function QueuedPromptList(props) {
21619
22402
  insertNode(_el$9, createTextNode(`+`));
21620
22403
  insert(_el$1, () => `\u2026 ${hidden()} more queued`);
21621
22404
  effect((_p$) => {
21622
- var _v$ = theme.textMuted, _v$2 = TextAttributes23.BOLD, _v$3 = theme.textMuted;
22405
+ var _v$ = theme.textMuted, _v$2 = TextAttributes24.BOLD, _v$3 = theme.textMuted;
21623
22406
  _v$ !== _p$.e && (_p$.e = setProp(_el$9, "fg", _v$, _p$.e));
21624
22407
  _v$2 !== _p$.t && (_p$.t = setProp(_el$9, "attributes", _v$2, _p$.t));
21625
22408
  _v$3 !== _p$.a && (_p$.a = setProp(_el$1, "fg", _v$3, _p$.a));
@@ -21665,7 +22448,7 @@ var init_Chat = __esm(() => {
21665
22448
  });
21666
22449
 
21667
22450
  // src/tui/component/sidebar.tsx
21668
- import { TextAttributes as TextAttributes24 } from "@opentui/core";
22451
+ import { TextAttributes as TextAttributes25 } from "@opentui/core";
21669
22452
  function Sidebar(props) {
21670
22453
  const {
21671
22454
  theme
@@ -21691,7 +22474,7 @@ function Sidebar(props) {
21691
22474
  setProp(_el$2, "paddingBottom", 1);
21692
22475
  insert(_el$3, () => props.title);
21693
22476
  effect((_p$) => {
21694
- var _v$ = theme.text, _v$2 = TextAttributes24.BOLD;
22477
+ var _v$ = theme.text, _v$2 = TextAttributes25.BOLD;
21695
22478
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$, _p$.e));
21696
22479
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$2, _p$.t));
21697
22480
  return _p$;
@@ -21950,7 +22733,7 @@ var init_keys4 = __esm(() => {
21950
22733
  });
21951
22734
 
21952
22735
  // src/tui/panes/sidebar/Sidebar.tsx
21953
- import { TextAttributes as TextAttributes25 } from "@opentui/core";
22736
+ import { TextAttributes as TextAttributes26 } from "@opentui/core";
21954
22737
  function Sidebar2(props) {
21955
22738
  const {
21956
22739
  theme
@@ -22042,7 +22825,7 @@ function Sidebar2(props) {
22042
22825
  return () => _c$() ? `[ ${tab.label} ]` : tab.label;
22043
22826
  })());
22044
22827
  effect((_p$) => {
22045
- var _v$7 = active() ? theme.primary : theme.textMuted, _v$8 = active() ? TextAttributes25.BOLD : undefined;
22828
+ var _v$7 = active() ? theme.primary : theme.textMuted, _v$8 = active() ? TextAttributes26.BOLD : undefined;
22046
22829
  _v$7 !== _p$.e && (_p$.e = setProp(_el$13, "fg", _v$7, _p$.e));
22047
22830
  _v$8 !== _p$.t && (_p$.t = setProp(_el$13, "attributes", _v$8, _p$.t));
22048
22831
  return _p$;
@@ -22137,7 +22920,7 @@ function Sidebar2(props) {
22137
22920
  }
22138
22921
  }), null);
22139
22922
  effect((_p$) => {
22140
- var _v$9 = isCursor() ? theme.primary : isSelected() ? theme.backgroundElement : undefined, _v$0 = isCursor() ? theme.selectedListItemText : badgeColor(), _v$1 = isCursor() ? theme.selectedListItemText : theme.text, _v$10 = (isMain || isSelected() && !isCursor()) && !isCursor() ? TextAttributes25.BOLD : undefined;
22923
+ var _v$9 = isCursor() ? theme.primary : isSelected() ? theme.backgroundElement : undefined, _v$0 = isCursor() ? theme.selectedListItemText : badgeColor(), _v$1 = isCursor() ? theme.selectedListItemText : theme.text, _v$10 = (isMain || isSelected() && !isCursor()) && !isCursor() ? TextAttributes26.BOLD : undefined;
22141
22924
  _v$9 !== _p$.e && (_p$.e = setProp(_el$14, "backgroundColor", _v$9, _p$.e));
22142
22925
  _v$0 !== _p$.t && (_p$.t = setProp(_el$15, "fg", _v$0, _p$.t));
22143
22926
  _v$1 !== _p$.a && (_p$.a = setProp(_el$16, "fg", _v$1, _p$.a));
@@ -22175,7 +22958,7 @@ function Sidebar2(props) {
22175
22958
  setProp(_el$11, "wrapMode", "none");
22176
22959
  setProp(_el$11, "onMouseUp", () => props.onAddTask?.());
22177
22960
  effect((_p$) => {
22178
- var _v$ = props.width ? props.width() : SIDEBAR_WIDTH, _v$2 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$3 = TextAttributes25.BOLD, _v$4 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$5 = TextAttributes25.BOLD, _v$6 = theme.textMuted;
22961
+ var _v$ = props.width ? props.width() : SIDEBAR_WIDTH, _v$2 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$3 = TextAttributes26.BOLD, _v$4 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$5 = TextAttributes26.BOLD, _v$6 = theme.textMuted;
22179
22962
  _v$ !== _p$.e && (_p$.e = setProp(_el$, "width", _v$, _p$.e));
22180
22963
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
22181
22964
  _v$3 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$3, _p$.a));
@@ -22274,17 +23057,17 @@ async function startBridgeServer(orch, socketPath) {
22274
23057
  });
22275
23058
  conn.on("error", () => {});
22276
23059
  });
22277
- await new Promise((resolve3, reject) => {
23060
+ await new Promise((resolve4, reject) => {
22278
23061
  server.once("error", reject);
22279
23062
  server.listen(socketPath, () => {
22280
23063
  server.removeListener("error", reject);
22281
- resolve3();
23064
+ resolve4();
22282
23065
  });
22283
23066
  });
22284
23067
  return {
22285
23068
  socketPath,
22286
23069
  async close() {
22287
- await new Promise((resolve3) => server.close(() => resolve3()));
23070
+ await new Promise((resolve4) => server.close(() => resolve4()));
22288
23071
  await unlink3(socketPath).catch(() => {});
22289
23072
  }
22290
23073
  };
@@ -22378,13 +23161,13 @@ __export(exports_bridge, {
22378
23161
  });
22379
23162
  import { writeFile as writeFile3 } from "fs/promises";
22380
23163
  import { homedir as homedir11 } from "os";
22381
- import { join as join14 } from "path";
23164
+ import { join as join15 } from "path";
22382
23165
  import { fileURLToPath as fileURLToPath2 } from "url";
22383
23166
  async function startBridge(orch, opts = {}) {
22384
23167
  const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir11();
22385
- const runDir = join14(home, ".kobe", "run");
22386
- const socketPath = join14(runDir, `bridge-${process.pid}.sock`);
22387
- const mcpConfigPath = join14(runDir, `mcp-${process.pid}.json`);
23168
+ const runDir = join15(home, ".kobe", "run");
23169
+ const socketPath = join15(runDir, `bridge-${process.pid}.sock`);
23170
+ const mcpConfigPath = join15(runDir, `mcp-${process.pid}.json`);
22388
23171
  const server = await startBridgeServer(orch, socketPath);
22389
23172
  const moduleExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
22390
23173
  const entry = fileURLToPath2(new URL(`../../cli/index${moduleExt}`, import.meta.url));
@@ -22412,7 +23195,7 @@ var init_bridge = __esm(() => {
22412
23195
 
22413
23196
  // src/tui/app.tsx
22414
23197
  import { homedir as homedir12 } from "os";
22415
- import { join as join15 } from "path";
23198
+ import { join as join16 } from "path";
22416
23199
  function Shell(props) {
22417
23200
  const themeCtx = useTheme();
22418
23201
  const {
@@ -22420,6 +23203,7 @@ function Shell(props) {
22420
23203
  } = themeCtx;
22421
23204
  const dialog = useDialog();
22422
23205
  const kv = useKV();
23206
+ const notifications = useNotifications();
22423
23207
  useThemePersistence(themeCtx, kv);
22424
23208
  const tasksAcc = props.orchestrator.tasksSignal();
22425
23209
  const chatRunStateAcc = props.orchestrator.chatRunStateSignal();
@@ -22513,8 +23297,8 @@ function Shell(props) {
22513
23297
  } = workspaceTabs;
22514
23298
  const workspaceAsideRight = createMemo(() => {
22515
23299
  const plan = workspacePlanAside();
22516
- const ctx3 = isChatTabActive() ? workspaceContextAside() : null;
22517
- const parts = [plan, ctx3].filter((v) => Boolean(v));
23300
+ const ctx4 = isChatTabActive() ? workspaceContextAside() : null;
23301
+ const parts = [plan, ctx4].filter((v) => Boolean(v));
22518
23302
  if (parts.length === 0)
22519
23303
  return;
22520
23304
  return parts.join(" \u2022 ");
@@ -22578,6 +23362,30 @@ function Shell(props) {
22578
23362
  renderer,
22579
23363
  activeTask
22580
23364
  });
23365
+ const visibleTabKey = createMemo(() => {
23366
+ const taskId = selectedId();
23367
+ const tabId = activeChatTabIdAcc();
23368
+ if (!taskId || !tabId)
23369
+ return null;
23370
+ if (!isChatTabActive())
23371
+ return null;
23372
+ return chatRunStateKey(taskId, tabId);
23373
+ });
23374
+ useCompletionNotifications({
23375
+ chatRunState: chatRunStateAcc,
23376
+ tasks: tasksAcc,
23377
+ visibleTabKey,
23378
+ notifications
23379
+ });
23380
+ createEffect(() => {
23381
+ const key = visibleTabKey();
23382
+ if (!key)
23383
+ return;
23384
+ const idx = key.indexOf(":");
23385
+ if (idx < 0)
23386
+ return;
23387
+ notifications.markRead(key.slice(0, idx), key.slice(idx + 1));
23388
+ });
22581
23389
  useTestSideChannel({
22582
23390
  orchestrator: props.orchestrator,
22583
23391
  activeTask
@@ -22664,6 +23472,9 @@ function Shell(props) {
22664
23472
  activeChatTabId: activeChatTabIdAcc,
22665
23473
  activeTaskId: taskIdAcc,
22666
23474
  chatRunState: chatRunStateAcc,
23475
+ get unread() {
23476
+ return notifications.unread;
23477
+ },
22667
23478
  onSelectChat: selectChatTab,
22668
23479
  onSelectChatTab: selectChatTabById,
22669
23480
  onSelectFile: selectFileTab,
@@ -22773,6 +23584,7 @@ function Shell(props) {
22773
23584
  }
22774
23585
  }));
22775
23586
  insert(_el$, createComponent2(StatusBar, {}), null);
23587
+ insert(_el$, createComponent2(ToastOverlay, {}), null);
22776
23588
  effect((_p$) => {
22777
23589
  var _v$ = theme.backgroundPanel, _v$2 = workspaceWidth(), _v$3 = theme.backgroundPanel, _v$4 = filesHeight();
22778
23590
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "backgroundColor", _v$, _p$.e));
@@ -22796,15 +23608,19 @@ function App(props) {
22796
23608
  get children() {
22797
23609
  return createComponent2(KVProvider, {
22798
23610
  get children() {
22799
- return createComponent2(SyncProvider, {
23611
+ return createComponent2(NotificationsProvider, {
22800
23612
  get children() {
22801
- return createComponent2(DialogProvider, {
23613
+ return createComponent2(SyncProvider, {
22802
23614
  get children() {
22803
- return createComponent2(CommandPaletteProvider, {
23615
+ return createComponent2(DialogProvider, {
22804
23616
  get children() {
22805
- return createComponent2(FocusProvider, {
23617
+ return createComponent2(CommandPaletteProvider, {
22806
23618
  get children() {
22807
- return createComponent2(Shell, props);
23619
+ return createComponent2(FocusProvider, {
23620
+ get children() {
23621
+ return createComponent2(Shell, props);
23622
+ }
23623
+ });
22808
23624
  }
22809
23625
  });
22810
23626
  }
@@ -22894,15 +23710,18 @@ var init_app = __esm(() => {
22894
23710
  init_pane_header();
22895
23711
  init_resizable_edge();
22896
23712
  init_status_bar();
23713
+ init_toast_overlay();
22897
23714
  init_top_bar();
22898
23715
  init_command_palette();
22899
23716
  init_focus();
22900
23717
  init_keybindings();
22901
23718
  init_kv();
23719
+ init_notifications();
22902
23720
  init_sync();
22903
23721
  init_theme();
22904
23722
  init_loader();
22905
23723
  init_engine_bootstrap();
23724
+ init_use_completion_notifications();
22906
23725
  init_use_pane_sizes();
22907
23726
  init_use_task_actions();
22908
23727
  init_use_theme_persistence();
@@ -22920,7 +23739,7 @@ var exports_tui = {};
22920
23739
  __export(exports_tui, {
22921
23740
  startTui: () => startTui
22922
23741
  });
22923
- import { TextAttributes as TextAttributes26 } from "@opentui/core";
23742
+ import { TextAttributes as TextAttributes27 } from "@opentui/core";
22924
23743
  function HelpHint() {
22925
23744
  const {
22926
23745
  theme
@@ -22994,7 +23813,7 @@ function Banner() {
22994
23813
  insert(_el$20, selected);
22995
23814
  insert(_el$12, createComponent2(HelpHint, {}), null);
22996
23815
  effect((_p$) => {
22997
- var _v$7 = theme.primary, _v$8 = TextAttributes26.BOLD, _v$9 = theme.borderActive, _v$0 = theme.text, _v$1 = theme.textMuted, _v$10 = {
23816
+ var _v$7 = theme.primary, _v$8 = TextAttributes27.BOLD, _v$9 = theme.borderActive, _v$0 = theme.text, _v$1 = theme.textMuted, _v$10 = {
22998
23817
  fg: theme.accent
22999
23818
  };
23000
23819
  _v$7 !== _p$.e && (_p$.e = setProp(_el$13, "fg", _v$7, _p$.e));
@@ -23111,9 +23930,9 @@ var init_tui = __esm(() => {
23111
23930
  });
23112
23931
 
23113
23932
  // src/cli/index.ts
23114
- import { resolve as resolve3 } from "path";
23933
+ import { resolve as resolve4 } from "path";
23115
23934
  async function runAddSubcommand(arg) {
23116
- const target = resolve3(process.cwd(), arg && arg.length > 0 ? arg : ".");
23935
+ const target = resolve4(process.cwd(), arg && arg.length > 0 ? arg : ".");
23117
23936
  const { addSavedRepo: addSavedRepo2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
23118
23937
  const result = addSavedRepo2(target);
23119
23938
  if (result.added) {