@meowlynxsea/koi 0.1.5 → 0.1.7

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/main.js CHANGED
@@ -342737,7 +342737,7 @@ function DialogProvider(props) {
342737
342737
  }
342738
342738
 
342739
342739
  // src/tui/app.tsx
342740
- var import_react48 = __toESM(require_react(), 1);
342740
+ var import_react53 = __toESM(require_react(), 1);
342741
342741
 
342742
342742
  // src/tui/components/chat-panel.tsx
342743
342743
  var import_react19 = __toESM(require_react(), 1);
@@ -415433,23 +415433,40 @@ var InputBox = import_react20.forwardRef(function InputBox2({
415433
415433
  onSlashEmpty,
415434
415434
  focused = true,
415435
415435
  disabled = false,
415436
+ disabledHint,
415436
415437
  width,
415437
415438
  mode = "build",
415438
415439
  isBusy = false,
415439
- onModeSwitch
415440
+ onModeSwitch,
415441
+ externalEditorResult,
415442
+ onExternalEditorResultUsed
415440
415443
  }, ref) {
415441
415444
  const textareaRef = import_react20.useRef(null);
415442
415445
  const [historyIndex, setHistoryIndex] = import_react20.useState(-1);
415443
415446
  const [savedInput, setSavedInput] = import_react20.useState("");
415444
415447
  const getText = () => textareaRef.current?.editBuffer.getText() ?? "";
415448
+ import_react20.useEffect(() => {
415449
+ if (externalEditorResult !== undefined && externalEditorResult !== null) {
415450
+ textareaRef.current?.editBuffer.replaceText(externalEditorResult);
415451
+ setHistoryIndex(-1);
415452
+ setSavedInput("");
415453
+ onExternalEditorResultUsed?.();
415454
+ }
415455
+ }, [externalEditorResult, onExternalEditorResultUsed]);
415445
415456
  import_react20.useImperativeHandle(ref, () => ({
415446
415457
  clearInput: () => {
415447
415458
  textareaRef.current?.editBuffer.replaceText("");
415448
415459
  setHistoryIndex(-1);
415449
415460
  setSavedInput("");
415450
415461
  },
415451
- isInputEmpty: () => getText().trim() === ""
415452
- }), []);
415462
+ isInputEmpty: () => getText().trim() === "",
415463
+ getText,
415464
+ setText: (text) => {
415465
+ textareaRef.current?.editBuffer.replaceText(text);
415466
+ setHistoryIndex(-1);
415467
+ setSavedInput("");
415468
+ }
415469
+ }), [getText]);
415453
415470
  const navigateToPreviousHistory = import_react20.useCallback(() => {
415454
415471
  const history = getUserHistory();
415455
415472
  if (history.length === 0)
@@ -415526,7 +415543,7 @@ var InputBox = import_react20.forwardRef(function InputBox2({
415526
415543
  }
415527
415544
  }
415528
415545
  }
415529
- if (event.name === "tab" && event.shift && onModeSwitch) {
415546
+ if (event.name === "tab" && event.shift && onModeSwitch && !isBusy) {
415530
415547
  event.preventDefault();
415531
415548
  event.stopPropagation();
415532
415549
  onModeSwitch();
@@ -415556,6 +415573,7 @@ var InputBox = import_react20.forwardRef(function InputBox2({
415556
415573
  { name: "return", shift: true, action: "newline" }
415557
415574
  ], []);
415558
415575
  const [wavePhase, setWavePhase] = import_react20.useState(0);
415576
+ const [shimmerIndex, setShimmerIndex] = import_react20.useState(-1);
415559
415577
  import_react20.useEffect(() => {
415560
415578
  if (!isBusy)
415561
415579
  return;
@@ -415564,6 +415582,24 @@ var InputBox = import_react20.forwardRef(function InputBox2({
415564
415582
  }, 150);
415565
415583
  return () => clearInterval(interval);
415566
415584
  }, [isBusy]);
415585
+ import_react20.useEffect(() => {
415586
+ if (!isBusy) {
415587
+ setShimmerIndex(-1);
415588
+ return;
415589
+ }
415590
+ const modeText = MODE_PREFIX[mode];
415591
+ const textLength = modeText.length;
415592
+ let currentIdx = -1;
415593
+ const interval = setInterval(() => {
415594
+ currentIdx = (currentIdx + 1) % (textLength + 4);
415595
+ if (currentIdx >= textLength) {
415596
+ setShimmerIndex(-1);
415597
+ } else {
415598
+ setShimmerIndex(currentIdx);
415599
+ }
415600
+ }, 80);
415601
+ return () => clearInterval(interval);
415602
+ }, [isBusy, mode]);
415567
415603
  const getInkWaveChars = import_react20.useCallback(() => {
415568
415604
  const totalWidth = width ?? 80;
415569
415605
  const phaseData = INK_WAVE_PHASES[wavePhase];
@@ -415637,18 +415673,46 @@ var InputBox = import_react20.forwardRef(function InputBox2({
415637
415673
  height: 3,
415638
415674
  children: [
415639
415675
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
415676
+ flexDirection: "row",
415640
415677
  marginRight: 1,
415641
415678
  flexShrink: 0,
415642
- children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
415643
- fg: MODE_COLOR[mode],
415644
- attributes: createTextAttributes({ bold: true }),
415645
- children: MODE_PREFIX[mode]
415646
- }, undefined, false, undefined, this)
415679
+ children: (() => {
415680
+ const modeText = MODE_PREFIX[mode];
415681
+ const highlightColor = mode === "build" ? "#86efac" : mode === "ask" ? "#fde68a" : "#93c5fd";
415682
+ const nearColor = mode === "build" ? "#bbf7d0" : mode === "ask" ? "#fef08a" : "#bfdbfe";
415683
+ return modeText.split("").map((char, i) => {
415684
+ const isHighlighted = isBusy && shimmerIndex === i;
415685
+ const isNearHighlight = isBusy && shimmerIndex !== -1 && (shimmerIndex === i - 1 || shimmerIndex === i + 1);
415686
+ if (isHighlighted) {
415687
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
415688
+ fg: highlightColor,
415689
+ attributes: createTextAttributes({ bold: true }),
415690
+ children: char
415691
+ }, i, false, undefined, this);
415692
+ }
415693
+ if (isNearHighlight) {
415694
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
415695
+ fg: nearColor,
415696
+ attributes: createTextAttributes({ bold: true }),
415697
+ children: char
415698
+ }, i, false, undefined, this);
415699
+ }
415700
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
415701
+ fg: MODE_COLOR[mode],
415702
+ attributes: createTextAttributes({ bold: true }),
415703
+ children: char
415704
+ }, i, false, undefined, this);
415705
+ });
415706
+ })()
415647
415707
  }, undefined, false, undefined, this),
415648
415708
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
415649
415709
  flexGrow: 1,
415650
415710
  height: 3,
415651
- children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("textarea", {
415711
+ children: disabled && disabledHint ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
415712
+ fg: "#6c6c7c",
415713
+ attributes: createTextAttributes({ dim: true }),
415714
+ children: disabledHint
415715
+ }, undefined, false, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("textarea", {
415652
415716
  ref: textareaRef,
415653
415717
  focused,
415654
415718
  showCursor: true,
@@ -473776,6 +473840,7 @@ var sessionTitle = "New Session";
473776
473840
  var providerConfigs = new Map;
473777
473841
  var currentModel = null;
473778
473842
  var auxiliaryModel = null;
473843
+ var externalEditor = null;
473779
473844
  var piAuthStorage = null;
473780
473845
  var piModelRegistry = null;
473781
473846
  var piSettingsManager = null;
@@ -473836,7 +473901,8 @@ function saveSettings() {
473836
473901
  sessionTitle,
473837
473902
  providers: Object.fromEntries(providerConfigs),
473838
473903
  currentModel,
473839
- auxiliaryModel
473904
+ auxiliaryModel,
473905
+ externalEditor: externalEditor ?? undefined
473840
473906
  };
473841
473907
  const json3 = JSON.stringify(data, null, 2);
473842
473908
  fs15.writeFileSync(SETTINGS_PATH, json3 + `
@@ -473864,6 +473930,9 @@ function loadSettings() {
473864
473930
  if (data.auxiliaryModel) {
473865
473931
  auxiliaryModel = data.auxiliaryModel;
473866
473932
  }
473933
+ if (data.externalEditor) {
473934
+ externalEditor = data.externalEditor;
473935
+ }
473867
473936
  syncCredentialsToPi();
473868
473937
  } catch {}
473869
473938
  }
@@ -473917,6 +473986,13 @@ function setAuxiliaryModel(ref6) {
473917
473986
  auxiliaryModel = ref6;
473918
473987
  saveSettings();
473919
473988
  }
473989
+ function getExternalEditor() {
473990
+ return externalEditor;
473991
+ }
473992
+ function setExternalEditor(path7) {
473993
+ externalEditor = path7;
473994
+ saveSettings();
473995
+ }
473920
473996
  function getAllProviders() {
473921
473997
  return getProviders();
473922
473998
  }
@@ -474888,7 +474964,6 @@ class BunPty extends EventEmitter13 {
474888
474964
  const args = process.platform === "win32" ? [] : ["--login"];
474889
474965
  const command = options4.command ?? shell;
474890
474966
  const commandArgs = options4.args ?? args;
474891
- const self2 = this;
474892
474967
  this.proc = Bun.spawn([command, ...commandArgs], {
474893
474968
  cwd: options4.cwd ?? process.cwd(),
474894
474969
  env: {
@@ -474901,12 +474976,12 @@ class BunPty extends EventEmitter13 {
474901
474976
  name: options4.name ?? "xterm-256color",
474902
474977
  cols: options4.cols ?? DEFAULT_COLS,
474903
474978
  rows: options4.rows ?? DEFAULT_ROWS,
474904
- data(_terminal, data) {
474905
- self2.emit("data", self2.textDecoder.decode(data));
474979
+ data: (_terminal, data) => {
474980
+ this.emit("data", this.textDecoder.decode(data));
474906
474981
  }
474907
474982
  },
474908
- onExit(_subprocess, exitCode, signalCode) {
474909
- self2.emit("exit", { exitCode: exitCode ?? 0, signal: signalCode ?? "" });
474983
+ onExit: (_subprocess, exitCode, signalCode) => {
474984
+ this.emit("exit", { exitCode: exitCode ?? 0, signal: signalCode !== null ? String(signalCode) : "" });
474910
474985
  }
474911
474986
  });
474912
474987
  this.pid = this.proc.pid;
@@ -474980,7 +475055,7 @@ class PtySession extends EventEmitter13 {
474980
475055
  resize(cols, rows) {
474981
475056
  try {
474982
475057
  this.pty.resize(cols, rows);
474983
- } catch (e) {}
475058
+ } catch {}
474984
475059
  }
474985
475060
  get isRunning() {
474986
475061
  return this._isRunning;
@@ -475246,7 +475321,6 @@ async function execBashWithPty(command, timeoutSec = 60, onData, signal, onTimeo
475246
475321
  let output = "";
475247
475322
  let timeoutHandle;
475248
475323
  let timedOut = false;
475249
- let ptySession;
475250
475324
  let resolvePromise;
475251
475325
  const ptyProcess = spawnPty({
475252
475326
  command: "bash",
@@ -475256,7 +475330,7 @@ async function execBashWithPty(command, timeoutSec = 60, onData, signal, onTimeo
475256
475330
  output += data;
475257
475331
  onData?.(data);
475258
475332
  });
475259
- ptySession = new PtySession(ptyId, ptyProcess, command);
475333
+ const ptySession = new PtySession(ptyId, ptyProcess, command);
475260
475334
  if (timeoutSec > 0) {
475261
475335
  timeoutHandle = setTimeout(() => {
475262
475336
  timedOut = true;
@@ -482606,7 +482680,7 @@ function generateLoremIpsum(targetTokens) {
482606
482680
  const sentenceLength = 10 + Math.floor(Math.random() * 11);
482607
482681
  let wordsInSentence = 0;
482608
482682
  for (let i = 0;i < sentenceLength && tokens < targetTokens; i++) {
482609
- const word = ONE_TOKEN_WORDS[Math.floor(Math.random() * ONE_TOKEN_WORDS.length)];
482683
+ const word = ONE_TOKEN_WORDS[Math.floor(Math.random() * ONE_TOKEN_WORDS.length)] ?? "";
482610
482684
  result += word;
482611
482685
  tokens++;
482612
482686
  wordsInSentence++;
@@ -483561,19 +483635,19 @@ var SOURCE_LABELS = {
483561
483635
  bundled: "Built-in Skills",
483562
483636
  mcp: "MCP Skills"
483563
483637
  };
483564
- function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483638
+ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill: _onInvokeSkill }) {
483565
483639
  const { width, height } = useTerminalDimensions();
483566
483640
  const inputRef = import_react21.useRef(null);
483567
483641
  const [filterText, setFilterText] = import_react21.useState("");
483568
483642
  const [selectedIndex, setSelectedIndex] = import_react21.useState(0);
483569
483643
  const [showDetails, setShowDetails] = import_react21.useState(false);
483570
483644
  const scrollOffsetRef = import_react21.useRef(0);
483571
- const panelWidth = Math.min(80, Math.max(50, Math.floor(width * 0.8)));
483572
- const panelHeight = Math.min(height - 4, 25);
483573
- const inputHeight = 1;
483574
- const separatorHeight = 1;
483575
- const detailsHeight = showDetails ? 8 : 0;
483576
- const listHeight = panelHeight - inputHeight - separatorHeight - detailsHeight - 2;
483645
+ const layout = import_react21.useMemo(() => {
483646
+ const panelWidth = Math.min(80, Math.max(50, Math.floor(width * 0.8)));
483647
+ const listHeight = Math.min(20, Math.max(3, Math.floor(height * 0.4)));
483648
+ const detailsHeight = showDetails ? 3 : 0;
483649
+ return { panelWidth, listHeight, detailsHeight };
483650
+ }, [width, height, showDetails]);
483577
483651
  const skillGroups = import_react21.useMemo(() => {
483578
483652
  const groups = new Map;
483579
483653
  for (const skill of skills) {
@@ -483648,23 +483722,8 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483648
483722
  if (newFlatIndex !== -1) {
483649
483723
  if (newFlatIndex < currentScroll) {
483650
483724
  scrollOffsetRef.current = newFlatIndex;
483651
- } else if (newFlatIndex > currentScroll + listHeight - 1) {
483652
- scrollOffsetRef.current = newFlatIndex - listHeight + 1;
483653
- }
483654
- }
483655
- return;
483656
- }
483657
- if (key.name === "return" || key.name === "enter") {
483658
- key.preventDefault();
483659
- key.stopPropagation();
483660
- if (selectedSkill) {
483661
- if (onInvokeSkill && selectedSkill.argumentHint) {
483662
- setShowDetails(true);
483663
- } else if (onInvokeSkill) {
483664
- onInvokeSkill(selectedSkill, "");
483665
- onClose();
483666
- } else {
483667
- onClose();
483725
+ } else if (newFlatIndex > currentScroll + layout.listHeight - 1) {
483726
+ scrollOffsetRef.current = newFlatIndex - layout.listHeight + 1;
483668
483727
  }
483669
483728
  }
483670
483729
  return;
@@ -483693,7 +483752,7 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483693
483752
  if (!isActive)
483694
483753
  return null;
483695
483754
  const effectiveScrollOffset = scrollOffsetRef.current;
483696
- const visibleItems = flatItems.slice(effectiveScrollOffset, effectiveScrollOffset + listHeight);
483755
+ const visibleItems = flatItems.slice(effectiveScrollOffset, effectiveScrollOffset + layout.listHeight);
483697
483756
  const renderDetails = () => {
483698
483757
  if (!showDetails || !selectedSkill)
483699
483758
  return null;
@@ -483705,18 +483764,17 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483705
483764
  selectedSkill.context && `Context: ${selectedSkill.context}`
483706
483765
  ].filter(Boolean);
483707
483766
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483708
- height: detailsHeight,
483767
+ height: layout.detailsHeight,
483709
483768
  flexDirection: "column",
483710
- marginTop: 1,
483711
483769
  children: [
483712
483770
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483713
483771
  height: 1,
483714
483772
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
483715
483773
  fg: "#6272a4",
483716
- children: "\u2500".repeat(panelWidth - 4)
483774
+ children: "\u2500".repeat(layout.panelWidth - 4)
483717
483775
  }, undefined, false, undefined, this)
483718
483776
  }, undefined, false, undefined, this),
483719
- details.map((line, idx) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483777
+ details.slice(0, 2).map((line, idx) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483720
483778
  height: 1,
483721
483779
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
483722
483780
  fg: "#8be9fd",
@@ -483737,8 +483795,7 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483737
483795
  alignItems: "center",
483738
483796
  justifyContent: "center",
483739
483797
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483740
- width: panelWidth,
483741
- height: panelHeight,
483798
+ width: layout.panelWidth,
483742
483799
  flexDirection: "column",
483743
483800
  borderStyle: "rounded",
483744
483801
  borderColor: "#6272a4",
@@ -483762,12 +483819,12 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483762
483819
  }, undefined, true, undefined, this),
483763
483820
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
483764
483821
  fg: "#6272a4",
483765
- children: "\u2191\u2193 navigate \xB7 Enter select \xB7 Tab details \xB7 Esc close"
483822
+ children: "\u2191\u2193 navigate \xB7 Tab details \xB7 Esc close"
483766
483823
  }, undefined, false, undefined, this)
483767
483824
  ]
483768
483825
  }, undefined, true, undefined, this),
483769
483826
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483770
- height: inputHeight,
483827
+ height: 1,
483771
483828
  marginTop: 1,
483772
483829
  children: [
483773
483830
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
@@ -483781,7 +483838,7 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483781
483838
  showCursor: true,
483782
483839
  height: 1,
483783
483840
  wrapMode: "none",
483784
- width: panelWidth - 10,
483841
+ width: layout.panelWidth - 10,
483785
483842
  textColor: "#f8f8f2",
483786
483843
  backgroundColor: "#44475a",
483787
483844
  onContentChange: handleContentChange
@@ -483789,15 +483846,15 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483789
483846
  ]
483790
483847
  }, undefined, true, undefined, this),
483791
483848
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483792
- height: separatorHeight,
483849
+ height: 1,
483793
483850
  marginTop: 1,
483794
483851
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
483795
483852
  fg: "#6272a4",
483796
- children: "\u2500".repeat(panelWidth - 2)
483853
+ children: "\u2500".repeat(layout.panelWidth - 2)
483797
483854
  }, undefined, false, undefined, this)
483798
483855
  }, undefined, false, undefined, this),
483799
483856
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483800
- height: listHeight,
483857
+ height: layout.listHeight,
483801
483858
  flexDirection: "column",
483802
483859
  overflow: "hidden",
483803
483860
  children: [
@@ -483824,11 +483881,8 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483824
483881
  children: [
483825
483882
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
483826
483883
  fg: "#ffb86c",
483827
- children: [
483828
- "/",
483829
- item.skill.name
483830
- ]
483831
- }, undefined, true, undefined, this),
483884
+ children: item.skill.name
483885
+ }, undefined, false, undefined, this),
483832
483886
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
483833
483887
  fg: "#6272a4",
483834
483888
  children: [
@@ -483843,7 +483897,7 @@ function SkillsMenu({ isActive, onClose, skills, onInvokeSkill }) {
483843
483897
  return null;
483844
483898
  }),
483845
483899
  flatItems.length === 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
483846
- height: listHeight,
483900
+ height: layout.listHeight,
483847
483901
  alignItems: "center",
483848
483902
  justifyContent: "center",
483849
483903
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
@@ -484172,6 +484226,124 @@ var forkManager = new ForkManager;
484172
484226
  // src/agent/session-store.ts
484173
484227
  init_mcp();
484174
484228
  init_mode();
484229
+
484230
+ // src/agent/tool-output-guard.ts
484231
+ var TOOL_LIMITS = {
484232
+ bash: { maxLines: 3000, maxBytes: 100 * 1024, truncateFrom: "tail" },
484233
+ read: { maxLines: 2000, maxBytes: 100 * 1024, truncateFrom: "head" },
484234
+ grep: { maxLines: 500, maxBytes: 50 * 1024, truncateFrom: "tail" },
484235
+ glob: { maxLines: 500, maxBytes: 20 * 1024, truncateFrom: "tail" },
484236
+ ls: { maxLines: 500, maxBytes: 20 * 1024, truncateFrom: "tail" },
484237
+ write: { maxLines: 0, maxBytes: 0, truncateFrom: "tail" },
484238
+ mcp: { maxLines: 1000, maxBytes: 100 * 1024, truncateFrom: "tail" }
484239
+ };
484240
+ var DEFAULT_LIMITS = {
484241
+ maxLines: DEFAULT_MAX_LINES,
484242
+ maxBytes: DEFAULT_MAX_BYTES,
484243
+ truncateFrom: "tail"
484244
+ };
484245
+ function extractToolName(toolName) {
484246
+ if (toolName.startsWith("mcp__")) {
484247
+ const parts = toolName.split("__");
484248
+ if (parts.length >= 3) {
484249
+ return "mcp";
484250
+ }
484251
+ }
484252
+ return toolName.split(":").pop() ?? toolName;
484253
+ }
484254
+ function truncateTextBlock(text, limits) {
484255
+ if (limits.maxLines === 0 && limits.maxBytes === 0) {
484256
+ return { text, wasTruncated: false, truncatedBy: null };
484257
+ }
484258
+ const options4 = {
484259
+ maxLines: limits.maxLines || DEFAULT_MAX_LINES,
484260
+ maxBytes: limits.maxBytes || DEFAULT_MAX_BYTES
484261
+ };
484262
+ const result = limits.truncateFrom === "head" ? truncateHead(text, options4) : truncateTail(text, options4);
484263
+ return {
484264
+ text: result.content,
484265
+ wasTruncated: result.truncated,
484266
+ truncatedBy: result.truncatedBy
484267
+ };
484268
+ }
484269
+ function truncateToolContent(content, limits) {
484270
+ const textBlocks = [];
484271
+ let hasTruncation = false;
484272
+ let lastTruncatedBy = null;
484273
+ for (const item of content) {
484274
+ if (item.type === "text") {
484275
+ const result = truncateTextBlock(item.text ?? "", limits);
484276
+ textBlocks.push(result);
484277
+ if (result.wasTruncated) {
484278
+ hasTruncation = true;
484279
+ lastTruncatedBy = result.truncatedBy;
484280
+ }
484281
+ } else if (item.type === "image") {
484282
+ textBlocks.push({ text: `[Image: ${item.data?.length ?? 0} bytes]`, wasTruncated: false, truncatedBy: null });
484283
+ }
484284
+ }
484285
+ if (!hasTruncation) {
484286
+ return { content, wasTruncated: false };
484287
+ }
484288
+ const truncatedContent = [];
484289
+ for (const block2 of textBlocks) {
484290
+ if (block2.text) {
484291
+ truncatedContent.push({ type: "text", text: block2.text });
484292
+ }
484293
+ }
484294
+ return {
484295
+ content: truncatedContent,
484296
+ wasTruncated: true,
484297
+ truncationInfo: {
484298
+ truncatedBy: lastTruncatedBy ?? "unknown",
484299
+ totalLines: 0,
484300
+ outputLines: 0,
484301
+ totalBytes: 0,
484302
+ outputBytes: 0
484303
+ }
484304
+ };
484305
+ }
484306
+ function createToolOutputGuard(customLimits) {
484307
+ const limits = { ...TOOL_LIMITS, ...customLimits };
484308
+ return async (context) => {
484309
+ const { result, toolCall } = context;
484310
+ const toolName = extractToolName(toolCall.name);
484311
+ const toolLimits = limits[toolName] ?? DEFAULT_LIMITS;
484312
+ if (!toolLimits || toolLimits.maxLines === 0 && toolLimits.maxBytes === 0) {
484313
+ return;
484314
+ }
484315
+ const { content, wasTruncated } = truncateToolContent(result.content, toolLimits);
484316
+ if (!wasTruncated) {
484317
+ return;
484318
+ }
484319
+ const truncateNote = buildTruncationNote(toolName);
484320
+ const finalContent = [...content];
484321
+ const lastTextBlock = [...finalContent].reverse().find((c2) => c2.type === "text");
484322
+ if (lastTextBlock) {
484323
+ const idx = finalContent.findIndex((c2) => c2.type === "text" && c2.text === lastTextBlock.text);
484324
+ if (idx >= 0) {
484325
+ finalContent[idx] = {
484326
+ type: "text",
484327
+ text: `${lastTextBlock.text.trimEnd()}
484328
+
484329
+ ${truncateNote}`
484330
+ };
484331
+ }
484332
+ } else {
484333
+ finalContent.push({ type: "text", text: truncateNote });
484334
+ }
484335
+ return {
484336
+ content: finalContent
484337
+ };
484338
+ };
484339
+ }
484340
+ function buildTruncationNote(toolName) {
484341
+ const toolLabel = toolName === "mcp" ? "MCP tool" : `"${toolName}"`;
484342
+ return `[Output truncated to prevent context overflow. Set a higher limit or use filters to see more of the ${toolLabel} output.]`;
484343
+ }
484344
+ var MCP_TOOL_LIMITS = TOOL_LIMITS["mcp"];
484345
+
484346
+ // src/agent/session-store.ts
484175
484347
  var CONFIG_DIR3 = path25.join(os16.homedir(), ".config", "koi");
484176
484348
  var KOI_SESSIONS_DIR2 = path25.join(CONFIG_DIR3, "sessions");
484177
484349
  var PI_AGENT_DIR2 = path25.join(CONFIG_DIR3, "pi");
@@ -484420,7 +484592,7 @@ async function createAgentSessionWithConfig(sessionManager, config4) {
484420
484592
  try {
484421
484593
  fs18.appendFileSync(logPath, logLine);
484422
484594
  } catch {}
484423
- return createAgentSession({
484595
+ const result = await createAgentSession({
484424
484596
  cwd: process.cwd(),
484425
484597
  agentDir: PI_AGENT_DIR2,
484426
484598
  authStorage: config4.authStorage,
@@ -484432,6 +484604,9 @@ async function createAgentSessionWithConfig(sessionManager, config4) {
484432
484604
  sessionManager,
484433
484605
  resourceLoader
484434
484606
  });
484607
+ const afterToolCall = createToolOutputGuard();
484608
+ result.session.agent.afterToolCall = afterToolCall;
484609
+ return result;
484435
484610
  }
484436
484611
  async function listSessions() {
484437
484612
  try {
@@ -485995,8 +486170,9 @@ function EditPendingModal({
485995
486170
  type: type3,
485996
486171
  onConfirm,
485997
486172
  onCancel,
485998
- width = 70
486173
+ width: widthProp
485999
486174
  }) {
486175
+ const { width } = useTerminalDimensions();
486000
486176
  const textareaRef = import_react24.useRef(null);
486001
486177
  import_react24.useEffect(() => {
486002
486178
  if (isActive && textareaRef.current) {
@@ -486013,6 +486189,7 @@ function EditPendingModal({
486013
486189
  if (!isActive)
486014
486190
  return null;
486015
486191
  const label = type3 === "sheer" ? "Edit Sheer" : "Edit Queued";
486192
+ const modalWidth = Math.min(widthProp ?? 70, Math.max(40, Math.floor(width * 0.85)));
486016
486193
  const handleConfirm = () => {
486017
486194
  const text = textareaRef.current?.editBuffer.getText() ?? "";
486018
486195
  if (text.trim()) {
@@ -486036,7 +486213,7 @@ function EditPendingModal({
486036
486213
  backgroundColor: "#1a1a2e",
486037
486214
  paddingX: 2,
486038
486215
  paddingY: 1,
486039
- width,
486216
+ width: modalWidth,
486040
486217
  children: [
486041
486218
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486042
486219
  alignSelf: "center",
@@ -486051,7 +486228,7 @@ function EditPendingModal({
486051
486228
  onSubmit: handleConfirm,
486052
486229
  focused: true,
486053
486230
  disabled: false,
486054
- width: width - 4
486231
+ width: modalWidth - 4
486055
486232
  }, undefined, false, undefined, this)
486056
486233
  }, undefined, false, undefined, this),
486057
486234
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
@@ -486278,6 +486455,7 @@ function Divider({
486278
486455
  }
486279
486456
  function SideBar({
486280
486457
  width = 28,
486458
+ height = 50,
486281
486459
  workingDir = "/",
486282
486460
  sessionTitle: sessionTitle2 = "New Session",
486283
486461
  modelName = "Not configured",
@@ -486296,95 +486474,137 @@ function SideBar({
486296
486474
  const hasMoreSubagents = subagents.length > visibleSubagents.length;
486297
486475
  const visibleMonitors = monitors.slice(0, 8);
486298
486476
  const hasMoreMonitors = monitors.length > visibleMonitors.length;
486299
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486477
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("scrollbox", {
486478
+ scrollY: true,
486479
+ scrollX: false,
486300
486480
  width,
486301
- flexDirection: "column",
486302
- paddingLeft: 1,
486303
- children: [
486304
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486305
- children: " "
486306
- }, undefined, false, undefined, this),
486307
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486308
- width: usableWidth,
486309
- flexDirection: "row",
486310
- justifyContent: "space-between",
486311
- children: [
486312
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486313
- attributes: createTextAttributes({ bold: true }),
486314
- fg: "#5a6a7a",
486315
- children: "Meowdream"
486316
- }, undefined, false, undefined, this),
486317
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486318
- fg: "#7a8a9a",
486319
- children: VERSION6
486320
- }, undefined, false, undefined, this)
486321
- ]
486322
- }, undefined, true, undefined, this),
486323
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486324
- children: " "
486325
- }, undefined, false, undefined, this),
486326
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Divider, {
486327
- width: usableWidth
486328
- }, undefined, false, undefined, this),
486329
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Divider, {
486330
- width: usableWidth,
486331
- char: "\xB7",
486332
- fg: "#c5cdd5"
486333
- }, undefined, false, undefined, this),
486334
- KOI_LOGO.map((line, i) => {
486335
- const color = GRADIENT_STOPS[Math.min(i, GRADIENT_STOPS.length - 1)];
486336
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486337
- fg: color,
486338
- wrapMode: "none",
486339
- truncate: true,
486340
- children: line.slice(0, usableWidth)
486341
- }, i, false, undefined, this);
486342
- }),
486343
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Divider, {
486344
- width: usableWidth,
486345
- char: "\xB7",
486346
- fg: "#c5cdd5"
486347
- }, undefined, false, undefined, this),
486348
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Divider, {
486349
- width: usableWidth
486350
- }, undefined, false, undefined, this),
486351
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486352
- children: " "
486353
- }, undefined, false, undefined, this),
486354
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486355
- attributes: createTextAttributes({ bold: true }),
486356
- fg: "#5a6a7a",
486357
- children: sessionTitle2
486358
- }, undefined, false, undefined, this),
486359
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486360
- children: " "
486361
- }, undefined, false, undefined, this),
486362
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486363
- fg: "#8a9aaa",
486364
- children: abbreviatePath(workingDir, usableWidth)
486365
- }, undefined, false, undefined, this),
486366
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486367
- children: " "
486368
- }, undefined, false, undefined, this),
486369
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486370
- attributes: createTextAttributes({ bold: true }),
486371
- fg: "#5a6a7a",
486372
- children: modelName
486373
- }, undefined, false, undefined, this),
486374
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486375
- fg: "#8a9aaa",
486376
- children: provider
486377
- }, undefined, false, undefined, this),
486378
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486379
- fg: "#8a9aaa",
486380
- children: `${contextUsage} ${tokenCount} ${cost}`
486381
- }, undefined, false, undefined, this),
486382
- (() => {
486383
- const mcpSummary = getMcpStatusSummary();
486384
- const mcpConnections = getMcpConnections();
486385
- if (mcpSummary.total === 0)
486386
- return null;
486387
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
486481
+ height,
486482
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486483
+ width,
486484
+ flexDirection: "column",
486485
+ paddingLeft: 1,
486486
+ children: [
486487
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486488
+ children: " "
486489
+ }, undefined, false, undefined, this),
486490
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486491
+ width: usableWidth,
486492
+ flexDirection: "row",
486493
+ justifyContent: "space-between",
486494
+ children: [
486495
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486496
+ attributes: createTextAttributes({ bold: true }),
486497
+ fg: "#5a6a7a",
486498
+ children: "Meowdream"
486499
+ }, undefined, false, undefined, this),
486500
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486501
+ fg: "#7a8a9a",
486502
+ children: VERSION6
486503
+ }, undefined, false, undefined, this)
486504
+ ]
486505
+ }, undefined, true, undefined, this),
486506
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486507
+ children: " "
486508
+ }, undefined, false, undefined, this),
486509
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Divider, {
486510
+ width: usableWidth
486511
+ }, undefined, false, undefined, this),
486512
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Divider, {
486513
+ width: usableWidth,
486514
+ char: "\xB7",
486515
+ fg: "#c5cdd5"
486516
+ }, undefined, false, undefined, this),
486517
+ KOI_LOGO.map((line, i) => {
486518
+ const color = GRADIENT_STOPS[Math.min(i, GRADIENT_STOPS.length - 1)];
486519
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486520
+ fg: color,
486521
+ wrapMode: "none",
486522
+ truncate: true,
486523
+ children: line.slice(0, usableWidth)
486524
+ }, i, false, undefined, this);
486525
+ }),
486526
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Divider, {
486527
+ width: usableWidth,
486528
+ char: "\xB7",
486529
+ fg: "#c5cdd5"
486530
+ }, undefined, false, undefined, this),
486531
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Divider, {
486532
+ width: usableWidth
486533
+ }, undefined, false, undefined, this),
486534
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486535
+ children: " "
486536
+ }, undefined, false, undefined, this),
486537
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486538
+ attributes: createTextAttributes({ bold: true }),
486539
+ fg: "#5a6a7a",
486540
+ children: sessionTitle2
486541
+ }, undefined, false, undefined, this),
486542
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486543
+ children: " "
486544
+ }, undefined, false, undefined, this),
486545
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486546
+ fg: "#8a9aaa",
486547
+ children: abbreviatePath(workingDir, usableWidth)
486548
+ }, undefined, false, undefined, this),
486549
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486550
+ children: " "
486551
+ }, undefined, false, undefined, this),
486552
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486553
+ attributes: createTextAttributes({ bold: true }),
486554
+ fg: "#5a6a7a",
486555
+ children: modelName
486556
+ }, undefined, false, undefined, this),
486557
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486558
+ fg: "#8a9aaa",
486559
+ children: provider
486560
+ }, undefined, false, undefined, this),
486561
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486562
+ fg: "#8a9aaa",
486563
+ children: `${contextUsage} ${tokenCount} ${cost}`
486564
+ }, undefined, false, undefined, this),
486565
+ (() => {
486566
+ const mcpSummary = getMcpStatusSummary();
486567
+ const mcpConnections = getMcpConnections();
486568
+ if (mcpSummary.total === 0)
486569
+ return null;
486570
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
486571
+ children: [
486572
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486573
+ children: " "
486574
+ }, undefined, false, undefined, this),
486575
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486576
+ attributes: createTextAttributes({ bold: true }),
486577
+ fg: "#5a6a7a",
486578
+ children: [
486579
+ "MCP (",
486580
+ mcpSummary.connected,
486581
+ "/",
486582
+ mcpSummary.total,
486583
+ ")"
486584
+ ]
486585
+ }, undefined, true, undefined, this),
486586
+ Array.from(mcpConnections.entries()).map(([name, connection]) => {
486587
+ const color = MCP_STATUS_COLORS[connection.status] ?? "#6c6c7c";
486588
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486589
+ flexDirection: "row",
486590
+ gap: 1,
486591
+ children: [
486592
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486593
+ fg: color,
486594
+ children: "\u25CF"
486595
+ }, undefined, false, undefined, this),
486596
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(FixedWidthText, {
486597
+ text: name,
486598
+ width: Math.max(1, usableWidth - 4),
486599
+ fg: "#8a9aaa"
486600
+ }, undefined, false, undefined, this)
486601
+ ]
486602
+ }, name, true, undefined, this);
486603
+ })
486604
+ ]
486605
+ }, undefined, true, undefined, this);
486606
+ })(),
486607
+ visibleSubagents.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
486388
486608
  children: [
486389
486609
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486390
486610
  children: " "
@@ -486393,15 +486613,13 @@ function SideBar({
486393
486613
  attributes: createTextAttributes({ bold: true }),
486394
486614
  fg: "#5a6a7a",
486395
486615
  children: [
486396
- "MCP (",
486397
- mcpSummary.connected,
486398
- "/",
486399
- mcpSummary.total,
486616
+ "Subagents (",
486617
+ subagents.length,
486400
486618
  ")"
486401
486619
  ]
486402
486620
  }, undefined, true, undefined, this),
486403
- Array.from(mcpConnections.entries()).map(([name, connection]) => {
486404
- const color = MCP_STATUS_COLORS[connection.status] ?? "#6c6c7c";
486621
+ visibleSubagents.map((sa) => {
486622
+ const color = SUBAGENT_STATUS_COLORS[sa.status] ?? "#fbbf24";
486405
486623
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486406
486624
  flexDirection: "row",
486407
486625
  gap: 1,
@@ -486411,133 +486629,99 @@ function SideBar({
486411
486629
  children: "\u25CF"
486412
486630
  }, undefined, false, undefined, this),
486413
486631
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(FixedWidthText, {
486414
- text: name,
486632
+ text: sa.description,
486415
486633
  width: Math.max(1, usableWidth - 4),
486416
486634
  fg: "#8a9aaa"
486417
486635
  }, undefined, false, undefined, this)
486418
486636
  ]
486419
- }, name, true, undefined, this);
486420
- })
486637
+ }, sa.id, true, undefined, this);
486638
+ }),
486639
+ hasMoreSubagents && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486640
+ fg: "#9aa5b0",
486641
+ children: `\u2026 and ${subagents.length - visibleSubagents.length} more`
486642
+ }, undefined, false, undefined, this)
486421
486643
  ]
486422
- }, undefined, true, undefined, this);
486423
- })(),
486424
- visibleSubagents.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
486425
- children: [
486426
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486427
- children: " "
486428
- }, undefined, false, undefined, this),
486429
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486430
- attributes: createTextAttributes({ bold: true }),
486431
- fg: "#5a6a7a",
486432
- children: [
486433
- "Subagents (",
486434
- subagents.length,
486435
- ")"
486436
- ]
486437
- }, undefined, true, undefined, this),
486438
- visibleSubagents.map((sa) => {
486439
- const color = SUBAGENT_STATUS_COLORS[sa.status] ?? "#fbbf24";
486440
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486441
- flexDirection: "row",
486442
- gap: 1,
486443
- children: [
486444
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486445
- fg: color,
486446
- children: "\u25CF"
486447
- }, undefined, false, undefined, this),
486448
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(FixedWidthText, {
486449
- text: sa.description,
486450
- width: Math.max(1, usableWidth - 4),
486451
- fg: "#8a9aaa"
486452
- }, undefined, false, undefined, this)
486453
- ]
486454
- }, sa.id, true, undefined, this);
486455
- }),
486456
- hasMoreSubagents && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486457
- fg: "#9aa5b0",
486458
- children: `\u2026 and ${subagents.length - visibleSubagents.length} more`
486459
- }, undefined, false, undefined, this)
486460
- ]
486461
- }, undefined, true, undefined, this),
486462
- visibleTasks.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
486463
- children: [
486464
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486465
- children: " "
486466
- }, undefined, false, undefined, this),
486467
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486468
- attributes: createTextAttributes({ bold: true }),
486469
- fg: "#5a6a7a",
486470
- children: [
486471
- "Tasks (",
486472
- tasks.length,
486473
- ")"
486474
- ]
486475
- }, undefined, true, undefined, this),
486476
- visibleTasks.map((task) => {
486477
- const color = TASK_STATUS_COLORS[task.status] ?? "#fbbf24";
486478
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486479
- flexDirection: "row",
486480
- gap: 1,
486644
+ }, undefined, true, undefined, this),
486645
+ visibleTasks.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
486646
+ children: [
486647
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486648
+ children: " "
486649
+ }, undefined, false, undefined, this),
486650
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486651
+ attributes: createTextAttributes({ bold: true }),
486652
+ fg: "#5a6a7a",
486481
486653
  children: [
486482
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486483
- fg: color,
486484
- children: "\u25CF"
486485
- }, undefined, false, undefined, this),
486486
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(FixedWidthText, {
486487
- text: task.content,
486488
- width: Math.max(1, usableWidth - 4),
486489
- fg: "#8a9aaa"
486490
- }, undefined, false, undefined, this)
486654
+ "Tasks (",
486655
+ tasks.length,
486656
+ ")"
486491
486657
  ]
486492
- }, task.id, true, undefined, this);
486493
- }),
486494
- hasMoreTasks && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486495
- fg: "#9aa5b0",
486496
- children: `\u2026 and ${tasks.length - visibleTasks.length} more`
486497
- }, undefined, false, undefined, this)
486498
- ]
486499
- }, undefined, true, undefined, this),
486500
- visibleMonitors.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
486501
- children: [
486502
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486503
- children: " "
486504
- }, undefined, false, undefined, this),
486505
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486506
- attributes: createTextAttributes({ bold: true }),
486507
- fg: "#5a6a7a",
486508
- children: [
486509
- "Monitors (",
486510
- monitors.length,
486511
- ")"
486512
- ]
486513
- }, undefined, true, undefined, this),
486514
- visibleMonitors.map((mon) => {
486515
- const color = MONITOR_STATUS_COLORS[mon.status] ?? "#fbbf24";
486516
- const displayText = mon.lastOutput ? `${mon.description}: ${mon.lastOutput.slice(0, 20)}` : mon.description;
486517
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486518
- flexDirection: "row",
486519
- gap: 1,
486658
+ }, undefined, true, undefined, this),
486659
+ visibleTasks.map((task) => {
486660
+ const color = TASK_STATUS_COLORS[task.status] ?? "#fbbf24";
486661
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486662
+ flexDirection: "row",
486663
+ gap: 1,
486664
+ children: [
486665
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486666
+ fg: color,
486667
+ children: "\u25CF"
486668
+ }, undefined, false, undefined, this),
486669
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(FixedWidthText, {
486670
+ text: task.content,
486671
+ width: Math.max(1, usableWidth - 4),
486672
+ fg: "#8a9aaa"
486673
+ }, undefined, false, undefined, this)
486674
+ ]
486675
+ }, task.id, true, undefined, this);
486676
+ }),
486677
+ hasMoreTasks && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486678
+ fg: "#9aa5b0",
486679
+ children: `\u2026 and ${tasks.length - visibleTasks.length} more`
486680
+ }, undefined, false, undefined, this)
486681
+ ]
486682
+ }, undefined, true, undefined, this),
486683
+ visibleMonitors.length > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
486684
+ children: [
486685
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486686
+ children: " "
486687
+ }, undefined, false, undefined, this),
486688
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486689
+ attributes: createTextAttributes({ bold: true }),
486690
+ fg: "#5a6a7a",
486520
486691
  children: [
486521
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486522
- fg: color,
486523
- children: "\u25CF"
486524
- }, undefined, false, undefined, this),
486525
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(FixedWidthText, {
486526
- text: displayText,
486527
- width: Math.max(1, usableWidth - 4),
486528
- fg: "#8a9aaa"
486529
- }, undefined, false, undefined, this)
486692
+ "Monitors (",
486693
+ monitors.length,
486694
+ ")"
486530
486695
  ]
486531
- }, mon.id, true, undefined, this);
486532
- }),
486533
- hasMoreMonitors && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486534
- fg: "#9aa5b0",
486535
- children: `\u2026 and ${monitors.length - visibleMonitors.length} more`
486536
- }, undefined, false, undefined, this)
486537
- ]
486538
- }, undefined, true, undefined, this)
486539
- ]
486540
- }, undefined, true, undefined, this);
486696
+ }, undefined, true, undefined, this),
486697
+ visibleMonitors.map((mon) => {
486698
+ const color = MONITOR_STATUS_COLORS[mon.status] ?? "#fbbf24";
486699
+ const displayText = mon.lastOutput ? `${mon.description}: ${mon.lastOutput.slice(0, 20)}` : mon.description;
486700
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486701
+ flexDirection: "row",
486702
+ gap: 1,
486703
+ children: [
486704
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486705
+ fg: color,
486706
+ children: "\u25CF"
486707
+ }, undefined, false, undefined, this),
486708
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(FixedWidthText, {
486709
+ text: displayText,
486710
+ width: Math.max(1, usableWidth - 4),
486711
+ fg: "#8a9aaa"
486712
+ }, undefined, false, undefined, this)
486713
+ ]
486714
+ }, mon.id, true, undefined, this);
486715
+ }),
486716
+ hasMoreMonitors && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486717
+ fg: "#9aa5b0",
486718
+ children: `\u2026 and ${monitors.length - visibleMonitors.length} more`
486719
+ }, undefined, false, undefined, this)
486720
+ ]
486721
+ }, undefined, true, undefined, this)
486722
+ ]
486723
+ }, undefined, true, undefined, this)
486724
+ }, undefined, false, undefined, this);
486541
486725
  }
486542
486726
 
486543
486727
  // src/tui/components/exit-modal.tsx
@@ -486580,6 +486764,7 @@ function Button({
486580
486764
  }, undefined, false, undefined, this);
486581
486765
  }
486582
486766
  function ExitModal({ isActive, onConfirm, onCancel }) {
486767
+ const { width } = useTerminalDimensions();
486583
486768
  useKeyboard((key) => {
486584
486769
  if (!isActive)
486585
486770
  return;
@@ -486591,6 +486776,7 @@ function ExitModal({ isActive, onConfirm, onCancel }) {
486591
486776
  });
486592
486777
  if (!isActive)
486593
486778
  return null;
486779
+ const modalWidth = Math.min(35, Math.max(20, Math.floor(width * 0.4)));
486594
486780
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486595
486781
  position: "absolute",
486596
486782
  top: 0,
@@ -486608,6 +486794,7 @@ function ExitModal({ isActive, onConfirm, onCancel }) {
486608
486794
  paddingY: 1,
486609
486795
  flexDirection: "column",
486610
486796
  alignItems: "center",
486797
+ width: modalWidth,
486611
486798
  children: [
486612
486799
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486613
486800
  attributes: createTextAttributes({ bold: true }),
@@ -486678,7 +486865,7 @@ function CommandPanel({ isActive, onClose, commands }) {
486678
486865
  }
486679
486866
  }, [isActive]);
486680
486867
  const query2 = filterText;
486681
- const { flatItems, cmdCount } = import_react29.useMemo(() => {
486868
+ const { flatItems } = import_react29.useMemo(() => {
486682
486869
  let filtered = commands;
486683
486870
  if (query2) {
486684
486871
  const q3 = query2.toLowerCase();
@@ -486705,7 +486892,7 @@ function CommandPanel({ isActive, onClose, commands }) {
486705
486892
  cmdIdx++;
486706
486893
  }
486707
486894
  }
486708
- return { flatItems: items3, cmdCount: cmdIdx };
486895
+ return { flatItems: items3 };
486709
486896
  }, [commands, query2]);
486710
486897
  const effectiveScrollOffset = scrollOffsetRef.current;
486711
486898
  useKeyboard((key) => {
@@ -486726,8 +486913,25 @@ function CommandPanel({ isActive, onClose, commands }) {
486726
486913
  if (key.name === "up" || key.name === "down") {
486727
486914
  key.preventDefault();
486728
486915
  key.stopPropagation();
486729
- const newIndex = key.name === "up" ? Math.max(0, selectedCmdIndex - 1) : Math.min(cmdCount - 1, selectedCmdIndex + 1);
486730
- const newFlatIndex = flatItems.findIndex((i) => i.type === "command" && i.cmdIndex === newIndex);
486916
+ const enabledIndices = flatItems.filter((i) => i.type === "command" && !i.cmd?.disabled && i.cmdIndex !== undefined).map((i) => i.cmdIndex);
486917
+ if (enabledIndices.length === 0)
486918
+ return;
486919
+ const currentIdx = enabledIndices.indexOf(selectedCmdIndex);
486920
+ let targetIndex;
486921
+ if (key.name === "up") {
486922
+ if (currentIdx <= 0) {
486923
+ targetIndex = enabledIndices[enabledIndices.length - 1];
486924
+ } else {
486925
+ targetIndex = enabledIndices[currentIdx - 1];
486926
+ }
486927
+ } else {
486928
+ if (currentIdx < 0 || currentIdx >= enabledIndices.length - 1) {
486929
+ targetIndex = enabledIndices[0];
486930
+ } else {
486931
+ targetIndex = enabledIndices[currentIdx + 1];
486932
+ }
486933
+ }
486934
+ const newFlatIndex = flatItems.findIndex((i) => i.type === "command" && i.cmdIndex === targetIndex);
486731
486935
  const currentScroll = scrollOffsetRef.current;
486732
486936
  let newScrollOffset = currentScroll;
486733
486937
  if (newFlatIndex !== -1) {
@@ -486737,16 +486941,8 @@ function CommandPanel({ isActive, onClose, commands }) {
486737
486941
  newScrollOffset = newFlatIndex - listHeight + 1;
486738
486942
  }
486739
486943
  }
486740
- console.log("[CommandPanel] Key press:", {
486741
- key: key.name,
486742
- newIndex,
486743
- newFlatIndex,
486744
- currentScroll,
486745
- listHeight,
486746
- newScrollOffset
486747
- });
486748
486944
  scrollOffsetRef.current = newScrollOffset;
486749
- setSelectedCmdIndex(newIndex);
486945
+ setSelectedCmdIndex(targetIndex);
486750
486946
  return;
486751
486947
  }
486752
486948
  });
@@ -486758,7 +486954,7 @@ function CommandPanel({ isActive, onClose, commands }) {
486758
486954
  };
486759
486955
  const handleSubmit = () => {
486760
486956
  const selectedItem = flatItems.find((i) => i.type === "command" && i.cmdIndex === selectedCmdIndex);
486761
- if (selectedItem?.cmd) {
486957
+ if (selectedItem?.cmd && !selectedItem.cmd.disabled) {
486762
486958
  onClose();
486763
486959
  selectedItem.cmd.action();
486764
486960
  }
@@ -486824,14 +487020,15 @@ function CommandPanel({ isActive, onClose, commands }) {
486824
487020
  }, undefined, false, undefined, this)
486825
487021
  }, `h-${item.section}-${flatIndex}`, false, undefined, this);
486826
487022
  }
486827
- const isSelected = item.cmdIndex === selectedCmdIndex;
487023
+ const isDisabled = item.cmd?.disabled;
487024
+ const isSelected = !isDisabled && item.cmdIndex === selectedCmdIndex;
486828
487025
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486829
487026
  height: 1,
486830
487027
  backgroundColor: isSelected ? "#44475a" : undefined,
486831
487028
  paddingLeft: 2,
486832
487029
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
486833
- fg: isSelected ? "#ff79c6" : "#f8f8f2",
486834
- children: `${item.cmd.id} ${item.cmd.label}`
487030
+ fg: isDisabled ? "#666666" : isSelected ? "#ff79c6" : "#f8f8f2",
487031
+ children: `${item.cmd.id} ${isDisabled ? item.cmd.label + " (busy)" : item.cmd.label}`
486835
487032
  }, undefined, false, undefined, this)
486836
487033
  }, `c-${item.cmd.id}-${flatIndex}`, false, undefined, this);
486837
487034
  }),
@@ -486852,6 +487049,7 @@ function CommandPanel({ isActive, onClose, commands }) {
486852
487049
  // src/tui/components/rename-modal.tsx
486853
487050
  var import_react31 = __toESM(require_react(), 1);
486854
487051
  function RenameModal({ isActive, currentTitle, onConfirm, onCancel }) {
487052
+ const { width } = useTerminalDimensions();
486855
487053
  const inputRef = import_react31.useRef(null);
486856
487054
  const [value2, setValue] = import_react31.useState(currentTitle);
486857
487055
  import_react31.useEffect(() => {
@@ -486886,6 +487084,7 @@ function RenameModal({ isActive, currentTitle, onConfirm, onCancel }) {
486886
487084
  };
486887
487085
  if (!isActive)
486888
487086
  return null;
487087
+ const modalWidth = Math.min(50, Math.max(30, Math.floor(width * 0.6)));
486889
487088
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486890
487089
  position: "absolute",
486891
487090
  top: 0,
@@ -486896,7 +487095,7 @@ function RenameModal({ isActive, currentTitle, onConfirm, onCancel }) {
486896
487095
  alignItems: "center",
486897
487096
  justifyContent: "center",
486898
487097
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
486899
- width: 50,
487098
+ width: modalWidth,
486900
487099
  flexDirection: "column",
486901
487100
  borderStyle: "rounded",
486902
487101
  borderColor: "#4a4a5a",
@@ -486976,8 +487175,25 @@ function ConnectModal({ isActive, onClose }) {
486976
487175
  const [verifyResult, setVerifyResult] = import_react33.useState(null);
486977
487176
  const [spinnerFrame, setSpinnerFrame] = import_react33.useState(0);
486978
487177
  const [scrollOffset, setScrollOffset] = import_react33.useState(0);
487178
+ const [filterText, setFilterText] = import_react33.useState("");
486979
487179
  const inputRef = import_react33.useRef(null);
486980
- const listHeight = Math.min(10, Math.floor(height * 0.35));
487180
+ const searchRef = import_react33.useRef(null);
487181
+ const layout = import_react33.useMemo(() => ({
487182
+ listHeight: Math.min(10, Math.max(3, Math.floor(height * 0.35)))
487183
+ }), [height]);
487184
+ const query2 = filterText;
487185
+ const filteredProviders = import_react33.useMemo(() => {
487186
+ if (!query2)
487187
+ return providers;
487188
+ const q3 = query2.toLowerCase();
487189
+ return providers.filter((p) => p.toLowerCase().includes(q3));
487190
+ }, [providers, query2]);
487191
+ const handleSearchChange = () => {
487192
+ const text = searchRef.current?.editBuffer.getText() ?? "";
487193
+ setFilterText(text);
487194
+ setSelectedProviderIndex(0);
487195
+ setScrollOffset(0);
487196
+ };
486981
487197
  import_react33.useEffect(() => {
486982
487198
  if (isActive) {
486983
487199
  setStep("provider");
@@ -486987,6 +487203,12 @@ function ConnectModal({ isActive, onClose }) {
486987
487203
  setVerifyResult(null);
486988
487204
  setSpinnerFrame(0);
486989
487205
  setScrollOffset(0);
487206
+ setFilterText("");
487207
+ const ta = searchRef.current;
487208
+ if (ta) {
487209
+ ta.editBuffer.replaceText("");
487210
+ ta.focus();
487211
+ }
486990
487212
  }
486991
487213
  }, [isActive]);
486992
487214
  import_react33.useEffect(() => {
@@ -487049,10 +487271,15 @@ function ConnectModal({ isActive, onClose }) {
487049
487271
  import_react33.useEffect(() => {
487050
487272
  if (selectedProviderIndex < scrollOffset) {
487051
487273
  setScrollOffset(selectedProviderIndex);
487052
- } else if (selectedProviderIndex >= scrollOffset + listHeight) {
487053
- setScrollOffset(selectedProviderIndex - listHeight + 1);
487274
+ } else if (selectedProviderIndex >= scrollOffset + layout.listHeight) {
487275
+ setScrollOffset(selectedProviderIndex - layout.listHeight + 1);
487276
+ }
487277
+ }, [selectedProviderIndex, layout.listHeight, scrollOffset]);
487278
+ import_react33.useEffect(() => {
487279
+ if (selectedProviderIndex >= filteredProviders.length && filteredProviders.length > 0) {
487280
+ setSelectedProviderIndex(filteredProviders.length - 1);
487054
487281
  }
487055
- }, [selectedProviderIndex, listHeight, scrollOffset]);
487282
+ }, [filteredProviders.length, selectedProviderIndex]);
487056
487283
  const handleSelectProvider = (provider) => {
487057
487284
  setSelectedProvider(provider);
487058
487285
  if (isProviderConfigured(provider)) {
@@ -487094,11 +487321,11 @@ function ConnectModal({ isActive, onClose }) {
487094
487321
  return;
487095
487322
  }
487096
487323
  if (key.name === "down") {
487097
- setSelectedProviderIndex((prev) => Math.max(0, Math.min(providers.length - 1, prev + 1)));
487324
+ setSelectedProviderIndex((prev) => Math.max(0, Math.min(filteredProviders.length - 1, prev + 1)));
487098
487325
  return;
487099
487326
  }
487100
487327
  if (key.name === "return") {
487101
- const provider = providers[selectedProviderIndex];
487328
+ const provider = filteredProviders[selectedProviderIndex];
487102
487329
  if (provider) {
487103
487330
  handleSelectProvider(provider);
487104
487331
  }
@@ -487143,7 +487370,7 @@ function ConnectModal({ isActive, onClose }) {
487143
487370
  const existingConfig = selectedProvider ? getProviderConfig(selectedProvider) : undefined;
487144
487371
  if (!isActive)
487145
487372
  return null;
487146
- const visibleProviders = providers.slice(scrollOffset, scrollOffset + listHeight);
487373
+ const visibleProviders = filteredProviders.slice(scrollOffset, scrollOffset + layout.listHeight);
487147
487374
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487148
487375
  position: "absolute",
487149
487376
  top: 0,
@@ -487170,45 +487397,83 @@ function ConnectModal({ isActive, onClose }) {
487170
487397
  children: "Select Provider"
487171
487398
  }, undefined, false, undefined, this),
487172
487399
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487173
- height: listHeight,
487400
+ height: 1,
487401
+ marginTop: 1,
487402
+ backgroundColor: "#16213e",
487403
+ paddingX: 1,
487404
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("textarea", {
487405
+ ref: searchRef,
487406
+ initialValue: "",
487407
+ focused: isActive,
487408
+ showCursor: true,
487409
+ height: 1,
487410
+ wrapMode: "none",
487411
+ textColor: "#f8f8f2",
487412
+ backgroundColor: "#16213e",
487413
+ onContentChange: handleSearchChange
487414
+ }, undefined, false, undefined, this)
487415
+ }, undefined, false, undefined, this),
487416
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487417
+ height: 1,
487418
+ marginTop: 1,
487419
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487420
+ fg: "#4a4a5a",
487421
+ children: "\u2500".repeat(56)
487422
+ }, undefined, false, undefined, this)
487423
+ }, undefined, false, undefined, this),
487424
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487425
+ height: layout.listHeight,
487174
487426
  flexDirection: "column",
487175
487427
  overflow: "hidden",
487176
487428
  marginTop: 1,
487177
- children: visibleProviders.map((p, i) => {
487178
- const actualIndex = scrollOffset + i;
487179
- const configured = isProviderConfigured(p);
487180
- return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487429
+ children: [
487430
+ filteredProviders.length === 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487181
487431
  height: 1,
487182
- backgroundColor: actualIndex === selectedProviderIndex ? "#44475a" : undefined,
487183
- paddingLeft: 1,
487184
- flexDirection: "row",
487185
- onMouseUp: (e) => {
487186
- e.stopPropagation();
487187
- handleSelectProvider(p);
487188
- },
487189
- children: [
487190
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487191
- fg: actualIndex === selectedProviderIndex ? "#ff79c6" : "#f8f8f2",
487192
- children: [
487193
- configured ? "\u25CF " : " ",
487194
- p
487195
- ]
487196
- }, undefined, true, undefined, this),
487197
- configured && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487198
- fg: "#00ff99",
487199
- marginLeft: 1,
487200
- children: "configured"
487201
- }, undefined, false, undefined, this)
487202
- ]
487203
- }, p, true, undefined, this);
487204
- })
487205
- }, undefined, false, undefined, this),
487432
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487433
+ fg: "#6c6c7c",
487434
+ children: [
487435
+ 'No providers match "',
487436
+ filterText,
487437
+ '"'
487438
+ ]
487439
+ }, undefined, true, undefined, this)
487440
+ }, undefined, false, undefined, this),
487441
+ visibleProviders.map((p, i) => {
487442
+ const actualIndex = scrollOffset + i;
487443
+ const configured = isProviderConfigured(p);
487444
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487445
+ height: 1,
487446
+ backgroundColor: actualIndex === selectedProviderIndex ? "#44475a" : undefined,
487447
+ paddingLeft: 1,
487448
+ flexDirection: "row",
487449
+ onMouseUp: (e) => {
487450
+ e.stopPropagation();
487451
+ handleSelectProvider(p);
487452
+ },
487453
+ children: [
487454
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487455
+ fg: actualIndex === selectedProviderIndex ? "#ff79c6" : "#f8f8f2",
487456
+ children: [
487457
+ configured ? "\u25CF " : " ",
487458
+ p
487459
+ ]
487460
+ }, undefined, true, undefined, this),
487461
+ configured && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487462
+ fg: "#00ff99",
487463
+ marginLeft: 1,
487464
+ children: "configured"
487465
+ }, undefined, false, undefined, this)
487466
+ ]
487467
+ }, p, true, undefined, this);
487468
+ })
487469
+ ]
487470
+ }, undefined, true, undefined, this),
487206
487471
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487207
487472
  marginTop: 1,
487208
487473
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487209
487474
  fg: "#6c6c7c",
487210
487475
  attributes: createTextAttributes({ dim: true }),
487211
- children: "\u2191\u2193 Navigate Enter Select Esc Cancel"
487476
+ children: "\u2191\u2193 Navigate Enter Select Esc Cancel Type to search"
487212
487477
  }, undefined, false, undefined, this)
487213
487478
  }, undefined, false, undefined, this)
487214
487479
  ]
@@ -487411,41 +487676,85 @@ function ModelModal({
487411
487676
  const configuredProviders = getConfiguredProviders();
487412
487677
  const [selectedModelIndex, setSelectedModelIndex] = import_react35.useState(0);
487413
487678
  const [activeTab, setActiveTab] = import_react35.useState("primary");
487679
+ const [filterText, setFilterText] = import_react35.useState("");
487414
487680
  const scrollOffsetRef = import_react35.useRef(0);
487415
- const listHeight = Math.min(12, Math.floor(height * 0.4));
487681
+ const inputRef = import_react35.useRef(null);
487682
+ const layout = import_react35.useMemo(() => ({
487683
+ listHeight: Math.min(12, Math.max(3, Math.floor(height * 0.4)))
487684
+ }), [height]);
487416
487685
  const primaryModel = getCurrentModel();
487417
487686
  const auxiliaryModel2 = getAuxiliaryModel();
487418
487687
  import_react35.useLayoutEffect(() => {
487419
487688
  if (isActive) {
487420
487689
  setSelectedModelIndex(0);
487421
487690
  setActiveTab("primary");
487691
+ setFilterText("");
487422
487692
  scrollOffsetRef.current = 0;
487693
+ const ta = inputRef.current;
487694
+ if (ta) {
487695
+ ta.editBuffer.replaceText("");
487696
+ ta.focus();
487697
+ }
487423
487698
  }
487424
487699
  }, [isActive]);
487700
+ const query2 = filterText;
487425
487701
  const { flatItems, modelCount } = import_react35.useMemo(() => {
487426
- const items3 = [];
487427
- let mIdx = 0;
487702
+ const allModels = [];
487428
487703
  for (const provider of configuredProviders) {
487429
- items3.push({ type: "header", provider });
487430
487704
  const models2 = getProviderModels(provider);
487431
487705
  for (const model of models2) {
487432
- items3.push({
487433
- type: "model",
487706
+ allModels.push({
487434
487707
  provider,
487435
487708
  modelId: model.id,
487436
- modelName: model.name || model.id,
487709
+ modelName: model.name || model.id
487710
+ });
487711
+ }
487712
+ }
487713
+ let filteredModels = allModels;
487714
+ if (query2) {
487715
+ const q3 = query2.toLowerCase();
487716
+ filteredModels = allModels.map((m2) => {
487717
+ const providerMatch = m2.provider.toLowerCase().includes(q3);
487718
+ const modelIdMatch = m2.modelId.toLowerCase().includes(q3);
487719
+ const modelNameMatch = m2.modelName.toLowerCase().includes(q3);
487720
+ const priority = !providerMatch && !modelIdMatch && !modelNameMatch ? 3 : providerMatch ? 0 : modelIdMatch ? 1 : 2;
487721
+ return { model: m2, priority };
487722
+ }).filter((item) => item.priority < 3).sort((a, b2) => a.priority - b2.priority).map((item) => item.model);
487723
+ }
487724
+ const grouped = new Map;
487725
+ for (const m2 of filteredModels) {
487726
+ if (!grouped.has(m2.provider))
487727
+ grouped.set(m2.provider, []);
487728
+ grouped.get(m2.provider).push(m2);
487729
+ }
487730
+ const items3 = [];
487731
+ let mIdx = 0;
487732
+ for (const [provider, models2] of grouped) {
487733
+ items3.push({ type: "header", provider });
487734
+ for (const m2 of models2) {
487735
+ items3.push({
487736
+ type: "model",
487737
+ provider: m2.provider,
487738
+ modelId: m2.modelId,
487739
+ modelName: m2.modelName,
487437
487740
  modelIndex: mIdx
487438
487741
  });
487439
487742
  mIdx++;
487440
487743
  }
487441
487744
  }
487442
487745
  return { flatItems: items3, modelCount: mIdx };
487443
- }, [configuredProviders]);
487746
+ }, [configuredProviders, query2]);
487444
487747
  import_react35.useEffect(() => {
487445
487748
  if (selectedModelIndex >= modelCount && modelCount > 0) {
487446
487749
  setSelectedModelIndex(modelCount - 1);
487447
487750
  }
487448
487751
  }, [modelCount, selectedModelIndex]);
487752
+ const handleContentChange = () => {
487753
+ const text = inputRef.current?.editBuffer.getText() ?? "";
487754
+ setFilterText(text);
487755
+ setSelectedModelIndex(0);
487756
+ scrollOffsetRef.current = 0;
487757
+ };
487449
487758
  const effectiveScrollOffset = scrollOffsetRef.current;
487450
487759
  useKeyboard((key) => {
487451
487760
  if (!isActive)
@@ -487465,8 +487774,8 @@ function ModelModal({
487465
487774
  if (newFlatIndex !== -1) {
487466
487775
  if (newFlatIndex < scrollOffsetRef.current) {
487467
487776
  newScrollOffset = newFlatIndex;
487468
- } else if (newFlatIndex > scrollOffsetRef.current + listHeight - 1) {
487469
- newScrollOffset = newFlatIndex - listHeight + 1;
487777
+ } else if (newFlatIndex > scrollOffsetRef.current + layout.listHeight - 1) {
487778
+ newScrollOffset = newFlatIndex - layout.listHeight + 1;
487470
487779
  }
487471
487780
  }
487472
487781
  scrollOffsetRef.current = newScrollOffset;
@@ -487494,7 +487803,7 @@ function ModelModal({
487494
487803
  });
487495
487804
  if (!isActive)
487496
487805
  return null;
487497
- const visibleItems = flatItems.slice(effectiveScrollOffset, effectiveScrollOffset + listHeight);
487806
+ const visibleItems = flatItems.slice(effectiveScrollOffset, effectiveScrollOffset + layout.listHeight);
487498
487807
  const isCurrent = (provider, modelId) => {
487499
487808
  const target2 = activeTab === "primary" ? primaryModel : auxiliaryModel2;
487500
487809
  return target2?.provider === provider && target2?.modelId === modelId;
@@ -487575,7 +487884,32 @@ function ModelModal({
487575
487884
  ]
487576
487885
  }, undefined, true, undefined, this),
487577
487886
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487578
- height: listHeight,
487887
+ height: 1,
487888
+ marginTop: 1,
487889
+ backgroundColor: "#16213e",
487890
+ paddingX: 1,
487891
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("textarea", {
487892
+ ref: inputRef,
487893
+ initialValue: "",
487894
+ focused: isActive,
487895
+ showCursor: true,
487896
+ height: 1,
487897
+ wrapMode: "none",
487898
+ textColor: "#f8f8f2",
487899
+ backgroundColor: "#16213e",
487900
+ onContentChange: handleContentChange
487901
+ }, undefined, false, undefined, this)
487902
+ }, undefined, false, undefined, this),
487903
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487904
+ height: 1,
487905
+ marginTop: 1,
487906
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487907
+ fg: "#4a4a5a",
487908
+ children: "\u2500".repeat(56)
487909
+ }, undefined, false, undefined, this)
487910
+ }, undefined, false, undefined, this),
487911
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487912
+ height: layout.listHeight,
487579
487913
  flexDirection: "column",
487580
487914
  overflow: "hidden",
487581
487915
  marginTop: 1,
@@ -487587,6 +487921,17 @@ function ModelModal({
487587
487921
  children: "No providers configured. Use /connect to add one."
487588
487922
  }, undefined, false, undefined, this)
487589
487923
  }, undefined, false, undefined, this),
487924
+ configuredProviders.length > 0 && modelCount === 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487925
+ height: 1,
487926
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487927
+ fg: "#6c6c7c",
487928
+ children: [
487929
+ 'No models match "',
487930
+ filterText,
487931
+ '"'
487932
+ ]
487933
+ }, undefined, true, undefined, this)
487934
+ }, undefined, false, undefined, this),
487590
487935
  visibleItems.map((item, idx) => {
487591
487936
  const flatIndex = effectiveScrollOffset + idx;
487592
487937
  if (item.type === "header") {
@@ -487632,7 +487977,7 @@ function ModelModal({
487632
487977
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487633
487978
  fg: "#6c6c7c",
487634
487979
  attributes: createTextAttributes({ dim: true }),
487635
- children: "Tab Switch"
487980
+ children: "Type to search Tab Switch"
487636
487981
  }, undefined, false, undefined, this)
487637
487982
  ]
487638
487983
  }, undefined, true, undefined, this)
@@ -487675,8 +488020,10 @@ function SessionModal({
487675
488020
  const { width, height } = useTerminalDimensions();
487676
488021
  const [selectedIndex, setSelectedIndex] = import_react37.useState(0);
487677
488022
  const [scrollOffset, setScrollOffset] = import_react37.useState(0);
487678
- const panelWidth = Math.min(70, Math.max(50, Math.floor(width * 0.7)));
487679
- const listHeight = Math.min(14, Math.floor(height * 0.5));
488023
+ const layout = import_react37.useMemo(() => ({
488024
+ panelWidth: Math.min(70, Math.max(50, Math.floor(width * 0.7))),
488025
+ listHeight: Math.min(14, Math.max(3, Math.floor(height * 0.5)))
488026
+ }), [width, height]);
487680
488027
  import_react37.useEffect(() => {
487681
488028
  if (isActive) {
487682
488029
  setSelectedIndex(0);
@@ -487700,10 +488047,10 @@ function SessionModal({
487700
488047
  return;
487701
488048
  if (safeIndex < scrollOffset) {
487702
488049
  setScrollOffset(safeIndex);
487703
- } else if (safeIndex >= scrollOffset + listHeight) {
487704
- setScrollOffset(safeIndex - listHeight + 1);
488050
+ } else if (safeIndex >= scrollOffset + layout.listHeight) {
488051
+ setScrollOffset(safeIndex - layout.listHeight + 1);
487705
488052
  }
487706
- }, [safeIndex, listHeight, scrollOffset]);
488053
+ }, [safeIndex, layout.listHeight, scrollOffset]);
487707
488054
  useKeyboard((key) => {
487708
488055
  if (!isActive || keyboardDisabled)
487709
488056
  return;
@@ -487740,7 +488087,7 @@ function SessionModal({
487740
488087
  });
487741
488088
  if (!isActive)
487742
488089
  return null;
487743
- const visibleSessions = sessions.slice(scrollOffset, scrollOffset + listHeight);
488090
+ const visibleSessions = sessions.slice(scrollOffset, scrollOffset + layout.listHeight);
487744
488091
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487745
488092
  position: "absolute",
487746
488093
  top: 0,
@@ -487751,7 +488098,7 @@ function SessionModal({
487751
488098
  alignItems: "center",
487752
488099
  justifyContent: "center",
487753
488100
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487754
- width: panelWidth,
488101
+ width: layout.panelWidth,
487755
488102
  flexDirection: "column",
487756
488103
  borderStyle: "rounded",
487757
488104
  borderColor: "#4a4a5a",
@@ -487776,7 +488123,7 @@ function SessionModal({
487776
488123
  ]
487777
488124
  }, undefined, true, undefined, this),
487778
488125
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487779
- height: listHeight,
488126
+ height: layout.listHeight,
487780
488127
  flexDirection: "column",
487781
488128
  overflow: "hidden",
487782
488129
  marginTop: 1,
@@ -487792,8 +488139,9 @@ function SessionModal({
487792
488139
  const flatIndex = scrollOffset + idx;
487793
488140
  const isSelected = flatIndex === safeIndex;
487794
488141
  const isCurrent = s.id === currentSessionId;
487795
- const safeTitle = String(s.title ?? "Untitled Session");
488142
+ const safeTitle = s.title ?? "Untitled Session";
487796
488143
  const safeMessageCount = typeof s.messageCount === "number" ? s.messageCount : 0;
488144
+ const messageCountDisplay = safeMessageCount > 0 ? safeMessageCount.toString() : "0";
487797
488145
  const safeUpdatedAt = s.updatedAt instanceof Date && !isNaN(s.updatedAt.getTime()) ? s.updatedAt : new Date;
487798
488146
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487799
488147
  height: 1,
@@ -487807,7 +488155,7 @@ function SessionModal({
487807
488155
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487808
488156
  fg: isSelected ? "#ff79c6" : isCurrent ? "#00f5ff" : "#f8f8f2",
487809
488157
  attributes: createTextAttributes({ bold: isCurrent }),
487810
- width: Math.max(1, panelWidth - 24),
488158
+ width: Math.max(1, layout.panelWidth - 24),
487811
488159
  truncate: true,
487812
488160
  children: [
487813
488161
  isCurrent ? "\u25CF " : " ",
@@ -487822,7 +488170,7 @@ function SessionModal({
487822
488170
  fg: "#6c6c7c",
487823
488171
  attributes: createTextAttributes({ dim: true }),
487824
488172
  children: [
487825
- String(safeMessageCount),
488173
+ messageCountDisplay,
487826
488174
  "msg"
487827
488175
  ]
487828
488176
  }, undefined, true, undefined, this),
@@ -487909,6 +488257,7 @@ function ConfirmModal({
487909
488257
  onConfirm,
487910
488258
  onCancel
487911
488259
  }) {
488260
+ const { width } = useTerminalDimensions();
487912
488261
  useKeyboard((key) => {
487913
488262
  if (!isActive)
487914
488263
  return;
@@ -487920,13 +488269,14 @@ function ConfirmModal({
487920
488269
  });
487921
488270
  if (!isActive)
487922
488271
  return null;
488272
+ const modalWidth = Math.min(40, Math.max(25, Math.floor(width * 0.5)));
487923
488273
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
487924
488274
  position: "absolute",
487925
488275
  top: 0,
487926
488276
  left: 0,
487927
488277
  width: "100%",
487928
488278
  height: "100%",
487929
- backgroundColor: "#00000080",
488279
+ backgroundColor: "#00000090",
487930
488280
  alignItems: "center",
487931
488281
  justifyContent: "center",
487932
488282
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
@@ -487937,6 +488287,7 @@ function ConfirmModal({
487937
488287
  paddingY: 1,
487938
488288
  flexDirection: "column",
487939
488289
  alignItems: "center",
488290
+ width: modalWidth,
487940
488291
  children: [
487941
488292
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
487942
488293
  attributes: createTextAttributes({ bold: true }),
@@ -488050,6 +488401,7 @@ function ProgressBar({ completed, total }) {
488050
488401
  function ConnectingModal({ isActive, progress }) {
488051
488402
  if (!isActive)
488052
488403
  return null;
488404
+ const { width, height } = useTerminalDimensions();
488053
488405
  const defaultProgress = progress ?? {
488054
488406
  total: 0,
488055
488407
  completed: 0,
@@ -488059,6 +488411,8 @@ function ConnectingModal({ isActive, progress }) {
488059
488411
  const { total, completed, currentServer, status, error: error55 } = defaultProgress;
488060
488412
  const isComplete = completed >= total && total > 0;
488061
488413
  const percentage = total > 0 ? Math.round(completed / total * 100) : 0;
488414
+ const modalWidth = Math.min(60, Math.max(40, Math.floor(width * 0.7)));
488415
+ const modalHeight = Math.min(height - 4, 12);
488062
488416
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
488063
488417
  position: "absolute",
488064
488418
  top: 0,
@@ -488075,8 +488429,8 @@ function ConnectingModal({ isActive, progress }) {
488075
488429
  paddingX: 3,
488076
488430
  paddingY: 2,
488077
488431
  flexDirection: "column",
488078
- minWidth: 50,
488079
- maxWidth: 60,
488432
+ width: modalWidth,
488433
+ height: modalHeight,
488080
488434
  children: [
488081
488435
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
488082
488436
  flexDirection: "row",
@@ -488175,7 +488529,7 @@ function ConnectingModal({ isActive, progress }) {
488175
488529
  }
488176
488530
 
488177
488531
  // src/tui/components/fork-modal.tsx
488178
- var import_react42 = __toESM(require_react(), 1);
488532
+ var import_react43 = __toESM(require_react(), 1);
488179
488533
  function isMessageEntry(entry) {
488180
488534
  return typeof entry === "object" && entry !== null && "type" in entry && entry["type"] === "message" && "message" in entry && typeof entry["message"] === "object" && entry["message"] !== null;
488181
488535
  }
@@ -488278,13 +488632,15 @@ function ForkModal({
488278
488632
  onFork
488279
488633
  }) {
488280
488634
  const { width, height } = useTerminalDimensions();
488281
- const [selectedIndex, setSelectedIndex] = import_react42.useState(0);
488282
- const [scrollOffset, setScrollOffset] = import_react42.useState(0);
488283
- const [rows, setRows] = import_react42.useState([]);
488284
- const panelWidth = Math.min(80, Math.max(52, Math.floor(width * 0.8)));
488285
- const listHeight = Math.min(16, Math.floor(height * 0.5));
488286
- const contentWidth = Math.max(1, panelWidth - 4);
488287
- import_react42.useEffect(() => {
488635
+ const [selectedIndex, setSelectedIndex] = import_react43.useState(0);
488636
+ const [scrollOffset, setScrollOffset] = import_react43.useState(0);
488637
+ const [rows, setRows] = import_react43.useState([]);
488638
+ const layout = import_react43.useMemo(() => ({
488639
+ panelWidth: Math.min(80, Math.max(52, Math.floor(width * 0.8))),
488640
+ listHeight: Math.min(16, Math.max(3, Math.floor(height * 0.5))),
488641
+ contentWidth: Math.max(1, Math.min(80, Math.max(52, Math.floor(width * 0.8))) - 4)
488642
+ }), [width, height]);
488643
+ import_react43.useEffect(() => {
488288
488644
  if (!isActive || !session) {
488289
488645
  setRows([]);
488290
488646
  return;
@@ -488299,22 +488655,22 @@ function ForkModal({
488299
488655
  setSelectedIndex(0);
488300
488656
  }
488301
488657
  }, [isActive, session]);
488302
- const selectableRows = import_react42.useMemo(() => rows.filter((r) => r.isUserMessage), [rows]);
488303
- const safeIndex = import_react42.useMemo(() => {
488658
+ const selectableRows = import_react43.useMemo(() => rows.filter((r) => r.isUserMessage), [rows]);
488659
+ const safeIndex = import_react43.useMemo(() => {
488304
488660
  if (selectableRows.length === 0)
488305
488661
  return -1;
488306
488662
  return Math.max(0, Math.min(selectedIndex, selectableRows.length - 1));
488307
488663
  }, [selectedIndex, selectableRows.length]);
488308
488664
  const selectedRow = safeIndex >= 0 ? selectableRows[safeIndex] : null;
488309
- import_react42.useEffect(() => {
488665
+ import_react43.useEffect(() => {
488310
488666
  if (!selectedRow) {
488311
488667
  setScrollOffset(0);
488312
488668
  return;
488313
488669
  }
488314
488670
  const flatIndex = selectedRow.index;
488315
- const maxOffset = Math.max(0, rows.length - listHeight);
488671
+ const maxOffset = Math.max(0, rows.length - layout.listHeight);
488316
488672
  setScrollOffset(Math.max(0, Math.min(flatIndex - 4, maxOffset)));
488317
- }, [selectedRow, listHeight, rows.length]);
488673
+ }, [selectedRow, layout.listHeight, rows.length]);
488318
488674
  useKeyboard((key) => {
488319
488675
  if (!isActive)
488320
488676
  return;
@@ -488339,7 +488695,7 @@ function ForkModal({
488339
488695
  });
488340
488696
  if (!isActive)
488341
488697
  return null;
488342
- const visibleRows = rows.slice(scrollOffset, scrollOffset + listHeight);
488698
+ const visibleRows = rows.slice(scrollOffset, scrollOffset + layout.listHeight);
488343
488699
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
488344
488700
  position: "absolute",
488345
488701
  top: 0,
@@ -488351,7 +488707,7 @@ function ForkModal({
488351
488707
  justifyContent: "center",
488352
488708
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
488353
488709
  alignSelf: "center",
488354
- width: panelWidth,
488710
+ width: layout.panelWidth,
488355
488711
  flexDirection: "column",
488356
488712
  borderStyle: "rounded",
488357
488713
  borderColor: "#4a4a5a",
@@ -488371,7 +488727,7 @@ function ForkModal({
488371
488727
  children: "Select a user message to branch from:"
488372
488728
  }, undefined, false, undefined, this),
488373
488729
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
488374
- height: listHeight,
488730
+ height: layout.listHeight,
488375
488731
  flexDirection: "column",
488376
488732
  overflow: "hidden",
488377
488733
  marginTop: 1,
@@ -488385,8 +488741,8 @@ function ForkModal({
488385
488741
  }, undefined, false, undefined, this),
488386
488742
  visibleRows.map((row) => {
488387
488743
  const isSelected = selectedRow?.index === row.index;
488388
- const prefix = getVisiblePrefix(row, contentWidth);
488389
- const availableWidth = Math.max(1, contentWidth - prefix.length);
488744
+ const prefix = getVisiblePrefix(row, layout.contentWidth);
488745
+ const availableWidth = Math.max(1, layout.contentWidth - prefix.length);
488390
488746
  const displayText = row.displayText.length > availableWidth ? row.displayText.slice(0, availableWidth - 1) + "\u2026" : row.displayText;
488391
488747
  const fgColor = row.isUserMessage ? isSelected ? "#ff79c6" : "#f8f8f2" : "#6c6c7c";
488392
488748
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
@@ -488433,7 +488789,7 @@ function ForkModal({
488433
488789
  }
488434
488790
 
488435
488791
  // src/tui/components/image-preview-modal.tsx
488436
- var import_react44 = __toESM(require_react(), 1);
488792
+ var import_react45 = __toESM(require_react(), 1);
488437
488793
  function ImagePreviewContent({ rows }) {
488438
488794
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
488439
488795
  flexDirection: "column",
@@ -488453,8 +488809,8 @@ function ImagePreviewModal({
488453
488809
  terminalWidth,
488454
488810
  terminalHeight
488455
488811
  }) {
488456
- const [rows, setRows] = import_react44.useState(null);
488457
- const [loading, setLoading] = import_react44.useState(false);
488812
+ const [rows, setRows] = import_react45.useState(null);
488813
+ const [loading, setLoading] = import_react45.useState(false);
488458
488814
  useKeyboard((key) => {
488459
488815
  if (!isActive)
488460
488816
  return;
@@ -488466,7 +488822,7 @@ function ImagePreviewModal({
488466
488822
  const modalH = Math.max(10, Math.floor(terminalHeight * 0.8));
488467
488823
  const imgMaxW = Math.max(10, modalW - 4);
488468
488824
  const imgMaxH = Math.max(4, modalH - 4);
488469
- import_react44.useEffect(() => {
488825
+ import_react45.useEffect(() => {
488470
488826
  if (!isActive) {
488471
488827
  setRows(null);
488472
488828
  setLoading(false);
@@ -488562,35 +488918,394 @@ function ImagePreviewModal({
488562
488918
  }, undefined, false, undefined, this);
488563
488919
  }
488564
488920
 
488921
+ // src/tui/components/external-editor-modal.tsx
488922
+ var import_react47 = __toESM(require_react(), 1);
488923
+ function ExternalEditorModal({
488924
+ isActive,
488925
+ onClose,
488926
+ currentPath,
488927
+ onSave
488928
+ }) {
488929
+ const { width, height } = useTerminalDimensions();
488930
+ const inputRef = import_react47.useRef(null);
488931
+ const [editorPath, setEditorPath] = import_react47.useState(currentPath ?? "");
488932
+ const layout = import_react47.useMemo(() => ({
488933
+ panelWidth: Math.min(60, Math.max(40, Math.floor(width * 0.7))),
488934
+ maxSuggestions: Math.max(2, Math.floor(height * 0.25))
488935
+ }), [width, height]);
488936
+ import_react47.useEffect(() => {
488937
+ if (isActive) {
488938
+ setEditorPath(currentPath ?? "");
488939
+ setTimeout(() => {
488940
+ const ta = inputRef.current;
488941
+ if (ta) {
488942
+ ta.editBuffer.replaceText(currentPath ?? "");
488943
+ ta.focus();
488944
+ }
488945
+ }, 10);
488946
+ }
488947
+ }, [isActive, currentPath]);
488948
+ useKeyboard((key) => {
488949
+ if (!isActive)
488950
+ return;
488951
+ if (key.name === "escape") {
488952
+ key.preventDefault();
488953
+ key.stopPropagation();
488954
+ onClose();
488955
+ return;
488956
+ }
488957
+ if (key.name === "return") {
488958
+ key.preventDefault();
488959
+ key.stopPropagation();
488960
+ const path7 = editorPath.trim();
488961
+ if (path7) {
488962
+ onSave(path7);
488963
+ }
488964
+ return;
488965
+ }
488966
+ });
488967
+ const handleContentChange = () => {
488968
+ const text = inputRef.current?.editBuffer.getText() ?? "";
488969
+ setEditorPath(text);
488970
+ };
488971
+ if (!isActive)
488972
+ return null;
488973
+ const suggestedEditors = [
488974
+ { cmd: "code --wait", desc: "VS Code" },
488975
+ { cmd: "vim", desc: "Vim" },
488976
+ { cmd: "nano", desc: "Nano" },
488977
+ { cmd: "emacs", desc: "Emacs" },
488978
+ { cmd: "subl -w", desc: "Sublime Text" },
488979
+ { cmd: "hx", desc: "Helix" }
488980
+ ];
488981
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
488982
+ position: "absolute",
488983
+ top: 0,
488984
+ left: 0,
488985
+ width: "100%",
488986
+ height: "100%",
488987
+ backgroundColor: "#00000080",
488988
+ alignItems: "center",
488989
+ justifyContent: "center",
488990
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
488991
+ width: layout.panelWidth,
488992
+ flexDirection: "column",
488993
+ borderStyle: "rounded",
488994
+ borderColor: "#4a4a5a",
488995
+ backgroundColor: "#1a1a2e",
488996
+ paddingX: 2,
488997
+ paddingY: 1,
488998
+ children: [
488999
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489000
+ attributes: createTextAttributes({ bold: true }),
489001
+ fg: "#ff79c6",
489002
+ children: "External Editor Configuration"
489003
+ }, undefined, false, undefined, this),
489004
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489005
+ marginTop: 1,
489006
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489007
+ fg: "#6c6c7c",
489008
+ children: "Enter the command to launch your preferred editor."
489009
+ }, undefined, false, undefined, this)
489010
+ }, undefined, false, undefined, this),
489011
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489012
+ marginTop: 1,
489013
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489014
+ fg: "#6c6c7c",
489015
+ children: "The editor should wait for the file to be closed before returning."
489016
+ }, undefined, false, undefined, this)
489017
+ }, undefined, false, undefined, this),
489018
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489019
+ marginTop: 1,
489020
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489021
+ fg: "#f8f8f2",
489022
+ children: "Editor command:"
489023
+ }, undefined, false, undefined, this)
489024
+ }, undefined, false, undefined, this),
489025
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489026
+ height: 1,
489027
+ marginTop: 1,
489028
+ backgroundColor: "#16213e",
489029
+ paddingX: 1,
489030
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("textarea", {
489031
+ ref: inputRef,
489032
+ initialValue: "",
489033
+ focused: isActive,
489034
+ showCursor: true,
489035
+ height: 1,
489036
+ wrapMode: "none",
489037
+ textColor: "#f8f8f2",
489038
+ backgroundColor: "#16213e",
489039
+ onContentChange: handleContentChange
489040
+ }, undefined, false, undefined, this)
489041
+ }, undefined, false, undefined, this),
489042
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489043
+ marginTop: 1,
489044
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489045
+ fg: "#6c6c7c",
489046
+ attributes: createTextAttributes({ dim: true }),
489047
+ children: "Suggestions:"
489048
+ }, undefined, false, undefined, this)
489049
+ }, undefined, false, undefined, this),
489050
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("scrollbox", {
489051
+ height: layout.maxSuggestions,
489052
+ flexDirection: "column",
489053
+ overflow: "hidden",
489054
+ children: suggestedEditors.slice(0, layout.maxSuggestions).map((editor) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489055
+ height: 1,
489056
+ flexDirection: "row",
489057
+ children: [
489058
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489059
+ fg: "#50fa7b",
489060
+ children: editor.cmd
489061
+ }, undefined, false, undefined, this),
489062
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489063
+ fg: "#6c6c7c",
489064
+ marginLeft: 2,
489065
+ children: editor.desc
489066
+ }, undefined, false, undefined, this)
489067
+ ]
489068
+ }, editor.cmd, true, undefined, this))
489069
+ }, undefined, false, undefined, this),
489070
+ currentPath && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489071
+ marginTop: 1,
489072
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489073
+ fg: "#f1fa8c",
489074
+ children: [
489075
+ "Current: ",
489076
+ currentPath
489077
+ ]
489078
+ }, undefined, true, undefined, this)
489079
+ }, undefined, false, undefined, this),
489080
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489081
+ marginTop: 2,
489082
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489083
+ fg: "#6c6c7c",
489084
+ attributes: createTextAttributes({ dim: true }),
489085
+ children: "Enter Confirm Esc Cancel"
489086
+ }, undefined, false, undefined, this)
489087
+ }, undefined, false, undefined, this),
489088
+ currentPath && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489089
+ marginTop: 1,
489090
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489091
+ fg: "#ff5555",
489092
+ onMouseUp: (e) => {
489093
+ e.stopPropagation();
489094
+ onSave("");
489095
+ },
489096
+ children: "Click here to clear the editor setting"
489097
+ }, undefined, false, undefined, this)
489098
+ }, undefined, false, undefined, this)
489099
+ ]
489100
+ }, undefined, true, undefined, this)
489101
+ }, undefined, false, undefined, this);
489102
+ }
489103
+
489104
+ // src/tui/components/alert-modal.tsx
489105
+ var import_react49 = __toESM(require_react(), 1);
489106
+ function Button3({
489107
+ label,
489108
+ bgColor,
489109
+ hoverBgColor,
489110
+ isActive,
489111
+ onClick
489112
+ }) {
489113
+ const [hover, setHover] = import_react49.useState(false);
489114
+ const currentBg = hover ? hoverBgColor : bgColor;
489115
+ const handleMouseUp = (e) => {
489116
+ if (isActive) {
489117
+ e.stopPropagation();
489118
+ onClick();
489119
+ }
489120
+ };
489121
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489122
+ paddingX: 2,
489123
+ paddingY: 0,
489124
+ backgroundColor: currentBg,
489125
+ onMouseUp: handleMouseUp,
489126
+ onMouseOver: () => isActive && setHover(true),
489127
+ onMouseOut: () => isActive && setHover(false),
489128
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489129
+ fg: "white",
489130
+ attributes: createTextAttributes({ bold: true }),
489131
+ children: label
489132
+ }, undefined, false, undefined, this)
489133
+ }, undefined, false, undefined, this);
489134
+ }
489135
+ function AlertModal({
489136
+ isActive,
489137
+ title,
489138
+ message,
489139
+ onClose
489140
+ }) {
489141
+ const { width } = useTerminalDimensions();
489142
+ useKeyboard((key) => {
489143
+ if (!isActive)
489144
+ return;
489145
+ if (key.name === "return" || key.name === "escape" || key.name === "o") {
489146
+ onClose();
489147
+ }
489148
+ });
489149
+ if (!isActive)
489150
+ return null;
489151
+ const messageLines = message.split(`
489152
+ `);
489153
+ const maxLineLength = Math.max(...messageLines.map((l) => l.length));
489154
+ const modalWidth = Math.min(70, Math.max(40, Math.min(maxLineLength + 10, Math.floor(width * 0.7))));
489155
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489156
+ position: "absolute",
489157
+ top: 0,
489158
+ left: 0,
489159
+ width: "100%",
489160
+ height: "100%",
489161
+ backgroundColor: "#00000090",
489162
+ alignItems: "center",
489163
+ justifyContent: "center",
489164
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489165
+ borderStyle: "rounded",
489166
+ borderColor: "#4a4a5a",
489167
+ backgroundColor: "#1a1a2e",
489168
+ paddingX: 2,
489169
+ paddingY: 1,
489170
+ flexDirection: "column",
489171
+ alignItems: "center",
489172
+ width: modalWidth,
489173
+ children: [
489174
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489175
+ attributes: createTextAttributes({ bold: true }),
489176
+ fg: "#fb7185",
489177
+ children: title
489178
+ }, undefined, false, undefined, this),
489179
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489180
+ marginTop: 1,
489181
+ flexDirection: "column",
489182
+ children: messageLines.map((line, i) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489183
+ fg: "#f8f8f2",
489184
+ wrapMode: "none",
489185
+ children: line
489186
+ }, i, false, undefined, this))
489187
+ }, undefined, false, undefined, this),
489188
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489189
+ marginTop: 2,
489190
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Button3, {
489191
+ label: " OK ",
489192
+ bgColor: "#2dd4bf",
489193
+ hoverBgColor: "#5eead4",
489194
+ isActive,
489195
+ onClick: onClose
489196
+ }, undefined, false, undefined, this)
489197
+ }, undefined, false, undefined, this),
489198
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
489199
+ marginTop: 1,
489200
+ fg: "#6c6c7c",
489201
+ attributes: createTextAttributes({ dim: true }),
489202
+ children: "Enter/O/ESC to close"
489203
+ }, undefined, false, undefined, this)
489204
+ ]
489205
+ }, undefined, true, undefined, this)
489206
+ }, undefined, false, undefined, this);
489207
+ }
489208
+
489209
+ // src/tui/components/external-editor.ts
489210
+ import { spawn as spawn15 } from "child_process";
489211
+ import fs22 from "fs";
489212
+ import path27 from "path";
489213
+ import os18 from "os";
489214
+ function openExternalEditor(renderer, editorPath, initialContent, onComplete) {
489215
+ const tmpDir = os18.tmpdir();
489216
+ const tmpFile = path27.join(tmpDir, `koi-prompt-${Date.now()}.txt`);
489217
+ let rendererResumed = false;
489218
+ const resumeRenderer = () => {
489219
+ if (!rendererResumed) {
489220
+ rendererResumed = true;
489221
+ try {
489222
+ renderer.resume();
489223
+ } catch {}
489224
+ }
489225
+ };
489226
+ const cleanupTempFile = () => {
489227
+ try {
489228
+ fs22.unlinkSync(tmpFile);
489229
+ } catch {}
489230
+ };
489231
+ const complete2 = (result, error55) => {
489232
+ resumeRenderer();
489233
+ cleanupTempFile();
489234
+ onComplete(result, error55);
489235
+ };
489236
+ try {
489237
+ fs22.writeFileSync(tmpFile, initialContent, "utf-8");
489238
+ renderer.suspend();
489239
+ const parts = editorPath.trim().split(/\s+/);
489240
+ const cmd = parts[0];
489241
+ const args = parts.slice(1);
489242
+ const child = spawn15(cmd, [...args, tmpFile], {
489243
+ stdio: "inherit",
489244
+ env: { ...process.env }
489245
+ });
489246
+ const timeout = setTimeout(() => {
489247
+ try {
489248
+ child.kill("SIGTERM");
489249
+ } catch {}
489250
+ }, 120000);
489251
+ child.on("close", (code2) => {
489252
+ clearTimeout(timeout);
489253
+ if (code2 === 0) {
489254
+ try {
489255
+ const result = fs22.readFileSync(tmpFile, "utf-8");
489256
+ complete2(result);
489257
+ } catch {
489258
+ complete2(null, { message: "Failed to read edited file" });
489259
+ }
489260
+ } else {
489261
+ complete2(null, {
489262
+ code: code2 ?? undefined,
489263
+ message: `Editor exited with code ${code2}`
489264
+ });
489265
+ }
489266
+ });
489267
+ child.on("error", (err) => {
489268
+ clearTimeout(timeout);
489269
+ complete2(null, {
489270
+ message: `Failed to launch editor: ${err.message}. Make sure "${cmd}" is installed and in your PATH.`
489271
+ });
489272
+ });
489273
+ } catch (err) {
489274
+ complete2(null, {
489275
+ message: `Failed to start external editor: ${err instanceof Error ? err.message : String(err)}`
489276
+ });
489277
+ }
489278
+ }
489279
+
488565
489280
  // src/tui/components/mcp/MCPSettings.tsx
488566
- var import_react46 = __toESM(require_react(), 1);
489281
+ var import_react51 = __toESM(require_react(), 1);
488567
489282
  init_config2();
488568
489283
  init_connection_manager();
488569
489284
  function MCPSettings({ isActive, onClose, onMcpChange }) {
488570
489285
  const { width } = useTerminalDimensions();
488571
- const [view, setView] = import_react46.useState("list");
488572
- const [selectedIndex, setSelectedIndex] = import_react46.useState(0);
488573
- const [servers, setServers] = import_react46.useState([]);
488574
- const [message, setMessage] = import_react46.useState(null);
488575
- const [editName, setEditName] = import_react46.useState("");
488576
- const [editType, setEditType] = import_react46.useState("stdio");
488577
- const [editCommand, setEditCommand] = import_react46.useState("");
488578
- const [editArgs, setEditArgs] = import_react46.useState("");
488579
- const [editUrl, setEditUrl] = import_react46.useState("");
488580
- const [editHeaders, setEditHeaders] = import_react46.useState("");
488581
- const nameRef = import_react46.useRef(null);
488582
- const commandRef = import_react46.useRef(null);
488583
- const argsRef = import_react46.useRef(null);
488584
- const urlRef = import_react46.useRef(null);
488585
- const headersRef = import_react46.useRef(null);
489286
+ const [view, setView] = import_react51.useState("list");
489287
+ const [selectedIndex, setSelectedIndex] = import_react51.useState(0);
489288
+ const [servers, setServers] = import_react51.useState([]);
489289
+ const [message, setMessage] = import_react51.useState(null);
489290
+ const [editName, setEditName] = import_react51.useState("");
489291
+ const [editType, setEditType] = import_react51.useState("stdio");
489292
+ const [editCommand, setEditCommand] = import_react51.useState("");
489293
+ const [editArgs, setEditArgs] = import_react51.useState("");
489294
+ const [editUrl, setEditUrl] = import_react51.useState("");
489295
+ const [editHeaders, setEditHeaders] = import_react51.useState("");
489296
+ const nameRef = import_react51.useRef(null);
489297
+ const commandRef = import_react51.useRef(null);
489298
+ const argsRef = import_react51.useRef(null);
489299
+ const urlRef = import_react51.useRef(null);
489300
+ const headersRef = import_react51.useRef(null);
488586
489301
  const panelWidth = Math.min(75, Math.max(50, Math.floor(width * 0.8)));
488587
- const refreshServers = import_react46.useCallback(() => {
489302
+ const refreshServers = import_react51.useCallback(() => {
488588
489303
  loadMcpConfigs();
488589
489304
  const configs = getAllMcpConfigs();
488590
489305
  setServers(Array.from(configs.keys()));
488591
489306
  setSelectedIndex(0);
488592
489307
  }, []);
488593
- import_react46.useEffect(() => {
489308
+ import_react51.useEffect(() => {
488594
489309
  if (isActive) {
488595
489310
  refreshServers();
488596
489311
  setView("list");
@@ -489144,13 +489859,13 @@ function CustomPromptContent({
489144
489859
  width,
489145
489860
  height
489146
489861
  }) {
489147
- const taRef = import_react48.useRef(null);
489862
+ const taRef = import_react53.useRef(null);
489148
489863
  const handleSubmit = () => {
489149
489864
  resolve4(taRef.current?.editBuffer.getText() ?? "");
489150
489865
  };
489151
489866
  const contentWidth = Math.min(70, Math.max(20, width - 8));
489152
489867
  const questionLines = wrapText(question, contentWidth - 4, 0);
489153
- const keyBindings = import_react48.useMemo(() => [{ name: "return", action: "submit" }], []);
489868
+ const keyBindings = import_react53.useMemo(() => [{ name: "return", action: "submit" }], []);
489154
489869
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
489155
489870
  flexDirection: "column",
489156
489871
  alignSelf: "center",
@@ -489222,41 +489937,45 @@ function CustomPromptContent({
489222
489937
  ]
489223
489938
  }, undefined, true, undefined, this);
489224
489939
  }
489225
- function App({ onExit }) {
489940
+ function App({ renderer, onExit }) {
489226
489941
  const { width, height } = useTerminalDimensions();
489227
- const [showExitModal, setShowExitModal] = import_react48.useState(false);
489228
- const [showCommandPanel, setShowCommandPanel] = import_react48.useState(false);
489229
- const [showRenameModal, setShowRenameModal] = import_react48.useState(false);
489230
- const [showConnectModal, setShowConnectModal] = import_react48.useState(false);
489231
- const [showModelModal, setShowModelModal] = import_react48.useState(false);
489232
- const [showSessionModal, setShowSessionModal] = import_react48.useState(false);
489233
- const [showForkModal, setShowForkModal] = import_react48.useState(false);
489234
- const [showDeleteConfirm, setShowDeleteConfirm] = import_react48.useState(false);
489235
- const [sessionToDelete, setSessionToDelete] = import_react48.useState(null);
489236
- const [showImageModal, setShowImageModal] = import_react48.useState(false);
489237
- const [imageModalUrl, setImageModalUrl] = import_react48.useState("");
489238
- const [showEditPendingModal, setShowEditPendingModal] = import_react48.useState(false);
489239
- const [editPendingType, setEditPendingType] = import_react48.useState(null);
489240
- const [editPendingIndex, setEditPendingIndex] = import_react48.useState(-1);
489241
- const [editPendingText, setEditPendingText] = import_react48.useState("");
489242
- const [currentModel2, setCurrentModelState] = import_react48.useState(getCurrentModel);
489243
- const [, setAuxiliaryModelState] = import_react48.useState(getAuxiliaryModel);
489244
- const [sidebarContextUsage, setSidebarContextUsage] = import_react48.useState("0%");
489245
- const [sidebarTokenCount, setSidebarTokenCount] = import_react48.useState("(0)");
489246
- const [sidebarCost, setSidebarCost] = import_react48.useState("$0.00");
489247
- const [tasks, setTasks] = import_react48.useState([]);
489248
- const [subagents, setSubagents] = import_react48.useState([]);
489249
- const [monitors, setMonitors] = import_react48.useState([]);
489250
- const [yoloMode2, setYoloMode2] = import_react48.useState(false);
489251
- const [agentMode, setAgentMode2] = import_react48.useState(getAgentMode());
489252
- const [showMCPSettings, setShowMCPSettings] = import_react48.useState(false);
489253
- const [showSkillsModal, setShowSkillsModal] = import_react48.useState(false);
489254
- const [skills, setSkills] = import_react48.useState([]);
489255
- import_react48.useEffect(() => {
489942
+ const [showExitModal, setShowExitModal] = import_react53.useState(false);
489943
+ const [showCommandPanel, setShowCommandPanel] = import_react53.useState(false);
489944
+ const [showRenameModal, setShowRenameModal] = import_react53.useState(false);
489945
+ const [showConnectModal, setShowConnectModal] = import_react53.useState(false);
489946
+ const [showModelModal, setShowModelModal] = import_react53.useState(false);
489947
+ const [showSessionModal, setShowSessionModal] = import_react53.useState(false);
489948
+ const [showForkModal, setShowForkModal] = import_react53.useState(false);
489949
+ const [showDeleteConfirm, setShowDeleteConfirm] = import_react53.useState(false);
489950
+ const [sessionToDelete, setSessionToDelete] = import_react53.useState(null);
489951
+ const [showImageModal, setShowImageModal] = import_react53.useState(false);
489952
+ const [imageModalUrl, setImageModalUrl] = import_react53.useState("");
489953
+ const [showEditPendingModal, setShowEditPendingModal] = import_react53.useState(false);
489954
+ const [editPendingType, setEditPendingType] = import_react53.useState(null);
489955
+ const [editPendingIndex, setEditPendingIndex] = import_react53.useState(-1);
489956
+ const [editPendingText, setEditPendingText] = import_react53.useState("");
489957
+ const [currentModel2, setCurrentModelState] = import_react53.useState(getCurrentModel);
489958
+ const [, setAuxiliaryModelState] = import_react53.useState(getAuxiliaryModel);
489959
+ const [sidebarContextUsage, setSidebarContextUsage] = import_react53.useState("0%");
489960
+ const [sidebarTokenCount, setSidebarTokenCount] = import_react53.useState("(0)");
489961
+ const [sidebarCost, setSidebarCost] = import_react53.useState("$0.00");
489962
+ const [tasks, setTasks] = import_react53.useState([]);
489963
+ const [subagents, setSubagents] = import_react53.useState([]);
489964
+ const [monitors, setMonitors] = import_react53.useState([]);
489965
+ const [yoloMode2, setYoloMode2] = import_react53.useState(false);
489966
+ const [agentMode, setAgentMode2] = import_react53.useState(getAgentMode());
489967
+ const [showMCPSettings, setShowMCPSettings] = import_react53.useState(false);
489968
+ const [showSkillsModal, setShowSkillsModal] = import_react53.useState(false);
489969
+ const [skills, setSkills] = import_react53.useState([]);
489970
+ const [showExternalEditorModal, setShowExternalEditorModal] = import_react53.useState(false);
489971
+ const [externalEditorBusy, setExternalEditorBusy] = import_react53.useState(false);
489972
+ const [externalEditorResult, setExternalEditorResult] = import_react53.useState(undefined);
489973
+ const [externalEditorError, setExternalEditorError] = import_react53.useState(null);
489974
+ import_react53.useEffect(() => {
489256
489975
  setYoloMode(yoloMode2);
489257
489976
  }, [yoloMode2]);
489258
489977
  const dialog = useDialog();
489259
- import_react48.useEffect(() => {
489978
+ import_react53.useEffect(() => {
489260
489979
  (async () => {
489261
489980
  initBundledSkills();
489262
489981
  await loadAllSkills(process.cwd());
@@ -489296,7 +490015,7 @@ function App({ onExit }) {
489296
490015
  addPlanMessage,
489297
490016
  syncAgentMode
489298
490017
  } = useKoiAgent();
489299
- const handleInvokeSkill = import_react48.useCallback(async (skill, args) => {
490018
+ const handleInvokeSkill = import_react53.useCallback(async (skill, args) => {
489300
490019
  const content = await invokeSkill(skill, args, session);
489301
490020
  const skillPrompt = extractTextFromContent(content);
489302
490021
  if (skillPrompt) {
@@ -489304,34 +490023,34 @@ function App({ onExit }) {
489304
490023
  }
489305
490024
  setShowSkillsModal(false);
489306
490025
  }, [session, prompt]);
489307
- const applyAgentMode = import_react48.useCallback((mode) => {
490026
+ const applyAgentMode = import_react53.useCallback((mode) => {
489308
490027
  setAgentMode(mode);
489309
490028
  setAgentMode2(mode);
489310
490029
  syncAgentMode(mode);
489311
490030
  }, [syncAgentMode]);
489312
- const handleModeSwitch = import_react48.useCallback(() => {
490031
+ const handleModeSwitch = import_react53.useCallback(() => {
489313
490032
  const next2 = cycleAgentMode();
489314
490033
  applyAgentMode(next2);
489315
490034
  }, [applyAgentMode]);
489316
- import_react48.useEffect(() => {
490035
+ import_react53.useEffect(() => {
489317
490036
  return subscribeModeChanges(() => {
489318
490037
  const mode = getAgentMode();
489319
490038
  setAgentMode2(mode);
489320
490039
  });
489321
490040
  }, []);
489322
- import_react48.useEffect(() => {
490041
+ import_react53.useEffect(() => {
489323
490042
  if (!session)
489324
490043
  return;
489325
490044
  session.setActiveToolsByName(getActiveToolNamesForMode(agentMode));
489326
490045
  injectModeIntoSystemPrompt(session, agentMode);
489327
490046
  }, [agentMode, session]);
489328
- import_react48.useEffect(() => {
490047
+ import_react53.useEffect(() => {
489329
490048
  const unsubscribe = subagentRegistry.subscribe(() => {
489330
490049
  setSubagents(subagentRegistry.getAll());
489331
490050
  });
489332
490051
  return unsubscribe;
489333
490052
  }, []);
489334
- import_react48.useEffect(() => {
490053
+ import_react53.useEffect(() => {
489335
490054
  const unsubscribe = monitorRegistry.subscribe(() => {
489336
490055
  if (currentSessionId) {
489337
490056
  setMonitors(monitorRegistry.getBySession(currentSessionId));
@@ -489342,7 +490061,7 @@ function App({ onExit }) {
489342
490061
  }
489343
490062
  return unsubscribe;
489344
490063
  }, [currentSessionId]);
489345
- import_react48.useEffect(() => {
490064
+ import_react53.useEffect(() => {
489346
490065
  const update2 = () => {
489347
490066
  if (!session) {
489348
490067
  setSidebarContextUsage("0%");
@@ -489378,13 +490097,13 @@ function App({ onExit }) {
489378
490097
  const interval = setInterval(update2, 2000);
489379
490098
  return () => clearInterval(interval);
489380
490099
  }, [session, currentSessionId]);
489381
- const processingPermissionRef = import_react48.useRef(false);
489382
- const permissionResolveRef = import_react48.useRef(null);
489383
- const [permissionModalOpen, setPermissionModalOpen] = import_react48.useState(false);
489384
- const planApprovalResolveRef = import_react48.useRef(null);
489385
- const questionResolveRef = import_react48.useRef(null);
489386
- const questionOptionsRef = import_react48.useRef([]);
489387
- const processPermissionQueue = import_react48.useCallback(async () => {
490100
+ const processingPermissionRef = import_react53.useRef(false);
490101
+ const permissionResolveRef = import_react53.useRef(null);
490102
+ const [permissionModalOpen, setPermissionModalOpen] = import_react53.useState(false);
490103
+ const planApprovalResolveRef = import_react53.useRef(null);
490104
+ const questionResolveRef = import_react53.useRef(null);
490105
+ const questionOptionsRef = import_react53.useRef([]);
490106
+ const processPermissionQueue = import_react53.useCallback(async () => {
489388
490107
  if (processingPermissionRef.current)
489389
490108
  return;
489390
490109
  const queue4 = getPermissionQueue();
@@ -489504,14 +490223,14 @@ function App({ onExit }) {
489504
490223
  processPermissionQueue();
489505
490224
  }, 0);
489506
490225
  }, [dialog, width, height]);
489507
- import_react48.useEffect(() => {
490226
+ import_react53.useEffect(() => {
489508
490227
  const unsubscribe = subscribePermissions(() => {
489509
490228
  processPermissionQueue();
489510
490229
  });
489511
490230
  return unsubscribe;
489512
490231
  }, [processPermissionQueue]);
489513
- const processingQuestionRef = import_react48.useRef(false);
489514
- import_react48.useEffect(() => {
490232
+ const processingQuestionRef = import_react53.useRef(false);
490233
+ import_react53.useEffect(() => {
489515
490234
  const unsubscribe = subscribeQuestions(async () => {
489516
490235
  if (processingQuestionRef.current)
489517
490236
  return;
@@ -489624,8 +490343,8 @@ function App({ onExit }) {
489624
490343
  });
489625
490344
  return unsubscribe;
489626
490345
  }, [dialog, width, height]);
489627
- const processingPlanApprovalRef = import_react48.useRef(false);
489628
- import_react48.useEffect(() => {
490346
+ const processingPlanApprovalRef = import_react53.useRef(false);
490347
+ import_react53.useEffect(() => {
489629
490348
  const unsubscribe = subscribePlanApprovals(async () => {
489630
490349
  if (processingPlanApprovalRef.current)
489631
490350
  return;
@@ -489773,11 +490492,11 @@ function App({ onExit }) {
489773
490492
  const pendingCount = steeringMessages.length + followUpMessages.length;
489774
490493
  const pendingHeight = pendingCount > 0 ? Math.min(pendingCount, 3) + (pendingCount > 3 ? 1 : 0) : 0;
489775
490494
  const chatPanelHeight = Math.max(1, height - (error55 ? 1 : 0) - 5 - 1 - pendingHeight);
489776
- const chatPanelRef = import_react48.useRef(null);
489777
- const inputBoxRef = import_react48.useRef(null);
489778
- const visibleMessages = import_react48.useMemo(() => messages.filter((m2) => !(m2.type === "user" && isInternalNotification(m2.content))), [messages]);
489779
- const anyModalOpen = showExitModal || showCommandPanel || showRenameModal || showConnectModal || showModelModal || showSessionModal || showForkModal || permissionModalOpen || showDeleteConfirm || showImageModal || showEditPendingModal || showMCPSettings || showSkillsModal;
489780
- const handleSubmit = import_react48.useCallback((text) => {
490495
+ const chatPanelRef = import_react53.useRef(null);
490496
+ const inputBoxRef = import_react53.useRef(null);
490497
+ const visibleMessages = import_react53.useMemo(() => messages.filter((m2) => !(m2.type === "user" && isInternalNotification(m2.content))), [messages]);
490498
+ const anyModalOpen = showExitModal || showCommandPanel || showRenameModal || showConnectModal || showModelModal || showSessionModal || showForkModal || permissionModalOpen || showDeleteConfirm || showImageModal || showEditPendingModal || showMCPSettings || showSkillsModal || showExternalEditorModal || externalEditorBusy || externalEditorError !== null;
490499
+ const handleSubmit = import_react53.useCallback((text) => {
489781
490500
  if (!text.trim() || !isReady)
489782
490501
  return;
489783
490502
  if (text.trim() === "/plan") {
@@ -489785,7 +490504,8 @@ function App({ onExit }) {
489785
490504
  return;
489786
490505
  }
489787
490506
  if (text.trim() === "/exit" || text.trim() === "/quit") {
489788
- setShowExitModal(true);
490507
+ saveCurrentState();
490508
+ onExit();
489789
490509
  return;
489790
490510
  }
489791
490511
  const skillInvocation = detectSkillInvocation(text);
@@ -489805,7 +490525,7 @@ function App({ onExit }) {
489805
490525
  prompt(text);
489806
490526
  }
489807
490527
  }, [isReady, isStreaming, steer, prompt, applyAgentMode, session]);
489808
- const handleQueueSubmit = import_react48.useCallback((text) => {
490528
+ const handleQueueSubmit = import_react53.useCallback((text) => {
489809
490529
  if (!text.trim() || !isReady)
489810
490530
  return;
489811
490531
  if (isStreaming) {
@@ -489814,51 +490534,51 @@ function App({ onExit }) {
489814
490534
  prompt(text);
489815
490535
  }
489816
490536
  }, [isReady, isStreaming, followUp, prompt]);
489817
- const handleRename = import_react48.useCallback((newTitle) => {
490537
+ const handleRename = import_react53.useCallback((newTitle) => {
489818
490538
  setSessionTitle2(newTitle);
489819
490539
  setShowRenameModal(false);
489820
490540
  }, [setSessionTitle2]);
489821
- const modelInfo = import_react48.useMemo(() => {
490541
+ const modelInfo = import_react53.useMemo(() => {
489822
490542
  if (!currentModel2) {
489823
490543
  return { modelName: "Not configured", provider: "Use /model to select" };
489824
490544
  }
489825
490545
  return { modelName: currentModel2.modelId, provider: `via ${currentModel2.provider}` };
489826
490546
  }, [currentModel2]);
489827
- const handleNewSession = import_react48.useCallback(async () => {
490547
+ const handleNewSession = import_react53.useCallback(async () => {
489828
490548
  await newSession();
489829
490549
  setShowSessionModal(false);
489830
490550
  }, [newSession]);
489831
- const handleSwitchSession = import_react48.useCallback(async (filePath) => {
490551
+ const handleSwitchSession = import_react53.useCallback(async (filePath) => {
489832
490552
  await switchSession(filePath);
489833
490553
  setShowSessionModal(false);
489834
490554
  }, [switchSession]);
489835
- const handleFork = import_react48.useCallback(async (entryId) => {
490555
+ const handleFork = import_react53.useCallback(async (entryId) => {
489836
490556
  await forkSession(entryId);
489837
490557
  setShowForkModal(false);
489838
490558
  }, [forkSession]);
489839
- const handleDeleteRequest = import_react48.useCallback((sessionId) => {
490559
+ const handleDeleteRequest = import_react53.useCallback((sessionId) => {
489840
490560
  const meta4 = sessionList.find((s) => s.id === sessionId);
489841
490561
  if (!meta4)
489842
490562
  return;
489843
490563
  setSessionToDelete(meta4);
489844
490564
  setShowDeleteConfirm(true);
489845
490565
  }, [sessionList]);
489846
- const handleConfirmDelete = import_react48.useCallback(async () => {
490566
+ const handleConfirmDelete = import_react53.useCallback(async () => {
489847
490567
  if (!sessionToDelete)
489848
490568
  return;
489849
490569
  await deleteSession2(sessionToDelete.id);
489850
490570
  setShowDeleteConfirm(false);
489851
490571
  setSessionToDelete(null);
489852
490572
  }, [sessionToDelete, deleteSession2]);
489853
- const handleCancelDelete = import_react48.useCallback(() => {
490573
+ const handleCancelDelete = import_react53.useCallback(() => {
489854
490574
  setShowDeleteConfirm(false);
489855
490575
  setSessionToDelete(null);
489856
490576
  }, []);
489857
- const handleImageClick = import_react48.useCallback((url4) => {
490577
+ const handleImageClick = import_react53.useCallback((url4) => {
489858
490578
  setImageModalUrl(url4);
489859
490579
  setShowImageModal(true);
489860
490580
  }, []);
489861
- const handleEditPending = import_react48.useCallback((type3, index2) => {
490581
+ const handleEditPending = import_react53.useCallback((type3, index2) => {
489862
490582
  const text = type3 === "sheer" ? steeringMessages[index2] : followUpMessages[index2];
489863
490583
  if (text === undefined)
489864
490584
  return;
@@ -489867,7 +490587,7 @@ function App({ onExit }) {
489867
490587
  setEditPendingText(text);
489868
490588
  setShowEditPendingModal(true);
489869
490589
  }, [steeringMessages, followUpMessages]);
489870
- const handleConfirmEditPending = import_react48.useCallback((text) => {
490590
+ const handleConfirmEditPending = import_react53.useCallback((text) => {
489871
490591
  if (!editPendingType || editPendingIndex < 0)
489872
490592
  return;
489873
490593
  removePendingMessage(editPendingType, editPendingIndex);
@@ -489878,11 +490598,11 @@ function App({ onExit }) {
489878
490598
  }
489879
490599
  setShowEditPendingModal(false);
489880
490600
  }, [editPendingType, editPendingIndex, removePendingMessage, steer, followUp]);
489881
- const handleCloseImageModal = import_react48.useCallback(() => {
490601
+ const handleCloseImageModal = import_react53.useCallback(() => {
489882
490602
  setShowImageModal(false);
489883
490603
  setImageModalUrl("");
489884
490604
  }, []);
489885
- const skillCommands = import_react48.useMemo(() => {
490605
+ const skillCommands = import_react53.useMemo(() => {
489886
490606
  return skills.filter((skill) => skill.userInvocable && !skill.isHidden).map((skill) => ({
489887
490607
  id: `/${skill.name}`,
489888
490608
  label: skill.description || skill.name,
@@ -489896,28 +490616,35 @@ function App({ onExit }) {
489896
490616
  }
489897
490617
  }));
489898
490618
  }, [skills, session, prompt]);
489899
- const commands = import_react48.useMemo(() => [
489900
- { id: "/new", label: "Start a new session", section: "Session", action: () => void handleNewSession() },
489901
- { id: "/fork", label: "Fork current session", section: "Session", action: () => setShowForkModal(true) },
490619
+ const commands = import_react53.useMemo(() => [
490620
+ { id: "/new", label: "Start a new session", section: "Session", action: () => void handleNewSession(), disabled: isStreaming },
490621
+ { id: "/fork", label: "Fork current session", section: "Session", action: () => setShowForkModal(true), disabled: isStreaming },
489902
490622
  { id: "/sessions", label: "Browse sessions", section: "Session", action: async () => {
489903
490623
  await refreshSessionList();
489904
490624
  setShowSessionModal(true);
489905
- } },
490625
+ }, disabled: isStreaming },
489906
490626
  { id: "/compact", label: "Compact current session", section: "Session", action: () => {
489907
490627
  session?.compact().catch(() => {});
489908
- } },
490628
+ }, disabled: isStreaming },
489909
490629
  { id: "/rename", label: "Rename session", section: "Session", action: () => setShowRenameModal(true) },
489910
- { id: "/exit", label: "Exit Koi", section: "Session", action: () => setShowExitModal(true) },
489911
- { id: "/quit", label: "Exit Koi (alias)", section: "Session", action: () => setShowExitModal(true) },
490630
+ { id: "/exit", label: "Exit Koi", section: "Session", action: () => {
490631
+ saveCurrentState();
490632
+ onExit();
490633
+ } },
490634
+ { id: "/quit", label: "Exit Koi (alias)", section: "Session", action: () => {
490635
+ saveCurrentState();
490636
+ onExit();
490637
+ } },
489912
490638
  { id: "/yolo", label: "Toggle YOLO mode (auto-approve all permissions)", section: "Mode", action: () => setYoloMode2((prev) => !prev) },
489913
490639
  { id: "/mode", label: `Cycle agent mode (${agentMode})`, section: "Mode", action: () => handleModeSwitch() },
489914
- { id: "/plan", label: "Switch to plan mode (read-only, no file modifications)", section: "Mode", action: () => applyAgentMode("plan") },
490640
+ { id: "/plan", label: "Switch to plan mode (read-only, no file modifications)", section: "Mode", action: () => applyAgentMode("plan"), disabled: isStreaming },
489915
490641
  { id: "/connect", label: "Connect to a provider", section: "Model", action: () => setShowConnectModal(true) },
489916
- { id: "/model", label: "Select a model", section: "Model", action: () => setShowModelModal(true) },
490642
+ { id: "/model", label: "Select a model", section: "Model", action: () => setShowModelModal(true), disabled: isStreaming },
489917
490643
  { id: "/mcp", label: "Open MCP settings", section: "Extensions", action: () => setShowMCPSettings(true) },
489918
490644
  { id: "/skills", label: "List and manage skills", section: "Extensions", action: () => setShowSkillsModal(true) },
490645
+ { id: "/editor", label: "Set external editor for prompts", section: "Settings", action: () => setShowExternalEditorModal(true) },
489919
490646
  ...skillCommands
489920
- ], [session, handleNewSession, refreshSessionList, agentMode, handleModeSwitch, applyAgentMode, skillCommands]);
490647
+ ], [isStreaming, session, handleNewSession, refreshSessionList, agentMode, handleModeSwitch, applyAgentMode, skillCommands]);
489921
490648
  useKeyboard((key) => {
489922
490649
  if (anyModalOpen && !permissionModalOpen)
489923
490650
  return;
@@ -489968,18 +490695,24 @@ function App({ onExit }) {
489968
490695
  return;
489969
490696
  }
489970
490697
  if (key.ctrl && key.name === "s") {
489971
- (async () => {
489972
- await refreshSessionList();
489973
- setShowSessionModal(true);
489974
- })();
490698
+ if (!isStreaming) {
490699
+ (async () => {
490700
+ await refreshSessionList();
490701
+ setShowSessionModal(true);
490702
+ })();
490703
+ }
489975
490704
  return;
489976
490705
  }
489977
490706
  if (key.ctrl && key.name === "f") {
489978
- setShowForkModal(true);
490707
+ if (!isStreaming) {
490708
+ setShowForkModal(true);
490709
+ }
489979
490710
  return;
489980
490711
  }
489981
490712
  if (key.name === "tab" && key.shift) {
489982
- handleModeSwitch();
490713
+ if (!isStreaming) {
490714
+ handleModeSwitch();
490715
+ }
489983
490716
  return;
489984
490717
  }
489985
490718
  if (key.ctrl && key.name === "o") {
@@ -489991,6 +490724,26 @@ function App({ onExit }) {
489991
490724
  }
489992
490725
  return;
489993
490726
  }
490727
+ if (key.ctrl && key.name === "g") {
490728
+ if (!isStreaming && !externalEditorBusy) {
490729
+ const editorPath = getExternalEditor();
490730
+ if (editorPath) {
490731
+ const inputText = inputBoxRef.current?.getText?.() ?? "";
490732
+ openExternalEditor(renderer, editorPath, inputText, (result, error56) => {
490733
+ if (result !== null && result.trim()) {
490734
+ setExternalEditorResult(result);
490735
+ } else if (error56) {
490736
+ setExternalEditorError(error56.message);
490737
+ }
490738
+ setExternalEditorBusy(false);
490739
+ });
490740
+ setExternalEditorBusy(true);
490741
+ } else {
490742
+ setShowExternalEditorModal(true);
490743
+ }
490744
+ }
490745
+ return;
490746
+ }
489994
490747
  if (key.name === "pageup") {
489995
490748
  chatPanelRef.current?.scrollUp?.();
489996
490749
  return;
@@ -490000,10 +490753,10 @@ function App({ onExit }) {
490000
490753
  return;
490001
490754
  }
490002
490755
  });
490003
- const handleSlashEmpty = import_react48.useCallback(() => {
490756
+ const handleSlashEmpty = import_react53.useCallback(() => {
490004
490757
  setShowCommandPanel(true);
490005
490758
  }, []);
490006
- const handleSelectPrimary = import_react48.useCallback((model) => {
490759
+ const handleSelectPrimary = import_react53.useCallback((model) => {
490007
490760
  setCurrentModelState(model);
490008
490761
  setCurrentModel(model);
490009
490762
  setShowModelModal(false);
@@ -490013,7 +490766,7 @@ function App({ onExit }) {
490013
490766
  session.setModel(piModel).catch(() => {});
490014
490767
  }
490015
490768
  }, [session]);
490016
- const handleSelectAuxiliary = import_react48.useCallback((model) => {
490769
+ const handleSelectAuxiliary = import_react53.useCallback((model) => {
490017
490770
  setAuxiliaryModelState(model);
490018
490771
  setAuxiliaryModel(model);
490019
490772
  setShowModelModal(false);
@@ -490064,11 +490817,14 @@ function App({ onExit }) {
490064
490817
  onQueueSubmit: handleQueueSubmit,
490065
490818
  onSlashEmpty: handleSlashEmpty,
490066
490819
  focused: !anyModalOpen,
490067
- disabled: !isReady,
490820
+ disabled: !isReady || externalEditorBusy,
490821
+ disabledHint: externalEditorBusy ? "External editor in use..." : undefined,
490068
490822
  width: leftWidth,
490069
490823
  mode: agentMode,
490070
490824
  isBusy: isStreaming,
490071
- onModeSwitch: handleModeSwitch
490825
+ onModeSwitch: handleModeSwitch,
490826
+ externalEditorResult,
490827
+ onExternalEditorResultUsed: () => setExternalEditorResult(undefined)
490072
490828
  }, undefined, false, undefined, this),
490073
490829
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(InfoBar, {
490074
490830
  width: leftWidth,
@@ -490094,6 +490850,7 @@ function App({ onExit }) {
490094
490850
  }, undefined, false, undefined, this),
490095
490851
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SideBar, {
490096
490852
  width: SIDEBAR_WIDTH,
490853
+ height,
490097
490854
  workingDir: process.cwd(),
490098
490855
  sessionTitle: sessionTitle2,
490099
490856
  modelName: modelInfo.modelName,
@@ -490194,6 +490951,21 @@ function App({ onExit }) {
490194
490951
  onClose: () => setShowSkillsModal(false),
490195
490952
  skills,
490196
490953
  onInvokeSkill: handleInvokeSkill
490954
+ }, undefined, false, undefined, this),
490955
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ExternalEditorModal, {
490956
+ isActive: showExternalEditorModal,
490957
+ onClose: () => setShowExternalEditorModal(false),
490958
+ currentPath: getExternalEditor(),
490959
+ onSave: (path7) => {
490960
+ setExternalEditor(path7 || null);
490961
+ setShowExternalEditorModal(false);
490962
+ }
490963
+ }, undefined, false, undefined, this),
490964
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AlertModal, {
490965
+ isActive: externalEditorError !== null,
490966
+ title: "External Editor Error",
490967
+ message: externalEditorError ?? "",
490968
+ onClose: () => setExternalEditorError(null)
490197
490969
  }, undefined, false, undefined, this)
490198
490970
  ]
490199
490971
  }, undefined, true, undefined, this);
@@ -490205,6 +490977,7 @@ async function main3() {
490205
490977
  const renderer = await createCliRenderer({ exitOnCtrlC: false });
490206
490978
  createRoot(renderer).render(/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(DialogProvider, {
490207
490979
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(App, {
490980
+ renderer,
490208
490981
  onExit: () => {
490209
490982
  renderer.destroy();
490210
490983
  process.exit(0);