@phenx-inc/ctlsurf 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/out/headless/index.mjs +115 -5
  2. package/out/headless/index.mjs.map +3 -3
  3. package/out/main/index.js +316 -8
  4. package/out/preload/index.js +9 -0
  5. package/out/renderer/assets/{cssMode-Bmzy9hKX.js → cssMode-D7r66wvO.js} +3 -3
  6. package/out/renderer/assets/{freemarker2-CDAsExTa.js → freemarker2-jSWJIjPG.js} +1 -1
  7. package/out/renderer/assets/{handlebars-CvpBYG1L.js → handlebars-BnFyz5RH.js} +1 -1
  8. package/out/renderer/assets/{html-CNhEQqr8.js → html-Bl5UFavp.js} +1 -1
  9. package/out/renderer/assets/{htmlMode-DD7YPgga.js → htmlMode-D4VByBT6.js} +3 -3
  10. package/out/renderer/assets/{index-CGHeDwZa.css → index-Cc_TqOCL.css} +318 -0
  11. package/out/renderer/assets/{index-BDlCPzCG.js → index-DxhW1emk.js} +558 -85
  12. package/out/renderer/assets/{javascript-CsGLsswK.js → javascript-CMN1OmwG.js} +2 -2
  13. package/out/renderer/assets/{jsonMode-CTuGgRvC.js → jsonMode-CJ5AYSPV.js} +3 -3
  14. package/out/renderer/assets/{liquid-BrrN4uul.js → liquid-BGMIDTEm.js} +1 -1
  15. package/out/renderer/assets/{lspLanguageFeatures-Bhdn2Eqy.js → lspLanguageFeatures-DGifVYB5.js} +1 -1
  16. package/out/renderer/assets/{mdx-DJo-gax4.js → mdx-B2Z7R10Q.js} +1 -1
  17. package/out/renderer/assets/{python-DYHeg5b2.js → python-LMDMSSdn.js} +1 -1
  18. package/out/renderer/assets/{razor-Cqcks14K.js → razor-BZSrziPu.js} +1 -1
  19. package/out/renderer/assets/{tsMode-YTLgCayh.js → tsMode-DF9m6_3n.js} +1 -1
  20. package/out/renderer/assets/{typescript-oxBn_Vcw.js → typescript-rZeCMf0a.js} +1 -1
  21. package/out/renderer/assets/{xml-BEqGMCTc.js → xml-fNSZ-7so.js} +1 -1
  22. package/out/renderer/assets/{yaml-BGSrk5HC.js → yaml-1wxIuvzh.js} +1 -1
  23. package/out/renderer/index.html +2 -2
  24. package/package.json +1 -1
  25. package/src/main/agents.ts +9 -1
  26. package/src/main/ctlsurfApi.ts +7 -0
  27. package/src/main/index.ts +62 -4
  28. package/src/main/orchestrator.ts +112 -4
  29. package/src/main/ssh.ts +348 -0
  30. package/src/preload/index.ts +26 -0
  31. package/src/renderer/App.tsx +25 -1
  32. package/src/renderer/components/AgentPicker.tsx +18 -0
  33. package/src/renderer/components/ProjectPicker.tsx +121 -0
  34. package/src/renderer/components/RemoteConnect.tsx +315 -0
  35. package/src/renderer/components/SpeakControls.tsx +8 -2
  36. package/src/renderer/components/TerminalPanel.tsx +134 -23
  37. package/src/renderer/components/TicketPanel.tsx +67 -26
  38. package/src/renderer/styles.css +318 -0
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-Bmzy9hKX.js","./lspLanguageFeatures-Bhdn2Eqy.js","./htmlMode-DD7YPgga.js","./jsonMode-CTuGgRvC.js","./javascript-CsGLsswK.js","./typescript-oxBn_Vcw.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-D7r66wvO.js","./lspLanguageFeatures-DGifVYB5.js","./htmlMode-D4VByBT6.js","./jsonMode-CJ5AYSPV.js","./javascript-CMN1OmwG.js","./typescript-rZeCMf0a.js"])))=>i.map(i=>d[i]);
2
2
  function getDefaultExportFromCjs(x) {
3
3
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
4
4
  }
@@ -18851,14 +18851,34 @@ function handleTerminalLink(_event, uri) {
18851
18851
  const _terminals = /* @__PURE__ */ new Map();
18852
18852
  function isAtBottom(terminal) {
18853
18853
  const buf = terminal.buffer.active;
18854
- return buf.viewportY >= buf.baseY;
18854
+ return buf.baseY - buf.viewportY <= 2;
18855
18855
  }
18856
- function scrollIfPinned(tabId, terminal) {
18857
- const state = _terminals.get(tabId);
18858
- if (state?.pinnedToBottom) {
18859
- terminal.scrollToBottom();
18856
+ function notifyPin(state) {
18857
+ state.pinListeners.forEach((fn) => fn(state.pinnedToBottom));
18858
+ }
18859
+ function setPinned(state, pinned) {
18860
+ if (pinned) state.anchorLine = null;
18861
+ if (state.pinnedToBottom === pinned) return;
18862
+ state.pinnedToBottom = pinned;
18863
+ notifyPin(state);
18864
+ }
18865
+ function reassert(state) {
18866
+ const { terminal } = state;
18867
+ state.holding = true;
18868
+ try {
18869
+ if (state.pinnedToBottom) {
18870
+ terminal.scrollToBottom();
18871
+ } else if (state.anchorLine != null && terminal.buffer.active.viewportY !== state.anchorLine) {
18872
+ terminal.scrollToLine(state.anchorLine);
18873
+ }
18874
+ } finally {
18875
+ state.holding = false;
18860
18876
  }
18861
18877
  }
18878
+ function markGesture(tabId) {
18879
+ const s = _terminals.get(tabId);
18880
+ if (s) s.userScrollPending = true;
18881
+ }
18862
18882
  function getOrCreateTerminal(tabId, onExit) {
18863
18883
  const existing = _terminals.get(tabId);
18864
18884
  if (existing) {
@@ -18902,6 +18922,9 @@ function getOrCreateTerminal(tabId, onExit) {
18902
18922
  terminal.loadAddon(new addonWebLinksExports.WebLinksAddon(handleTerminalLink));
18903
18923
  terminal.attachCustomKeyEventHandler((e) => {
18904
18924
  if (e.type !== "keydown") return true;
18925
+ if (e.key === "PageUp" || e.key === "PageDown" || e.key === "Home" || e.key === "End") {
18926
+ markGesture(tabId);
18927
+ }
18905
18928
  const isCombo = (e.metaKey || e.ctrlKey) && e.shiftKey && e.code === "KeyE";
18906
18929
  if (!isCombo) return true;
18907
18930
  e.preventDefault();
@@ -18914,20 +18937,34 @@ function getOrCreateTerminal(tabId, onExit) {
18914
18937
  unsubData: null,
18915
18938
  unsubExit: null,
18916
18939
  spawnedAgentId: null,
18917
- pinnedToBottom: true
18940
+ pinnedToBottom: true,
18941
+ anchorLine: null,
18942
+ userScrollPending: false,
18943
+ holding: false,
18944
+ pinListeners: /* @__PURE__ */ new Set()
18918
18945
  };
18919
18946
  terminal.onScroll(() => {
18920
- state.pinnedToBottom = isAtBottom(terminal);
18947
+ if (state.holding) return;
18948
+ if (!state.userScrollPending) {
18949
+ reassert(state);
18950
+ return;
18951
+ }
18952
+ state.userScrollPending = false;
18953
+ if (isAtBottom(terminal)) {
18954
+ setPinned(state, true);
18955
+ } else {
18956
+ state.anchorLine = terminal.buffer.active.viewportY;
18957
+ setPinned(state, false);
18958
+ }
18921
18959
  });
18922
18960
  terminal.onData((data) => {
18923
- state.pinnedToBottom = true;
18961
+ setPinned(state, true);
18924
18962
  terminal.scrollToBottom();
18925
18963
  window.worker.writePty(tabId, data);
18926
18964
  });
18927
18965
  state.unsubData = window.worker.onPtyData((eventTabId, data) => {
18928
18966
  if (eventTabId === tabId) {
18929
- terminal.write(data);
18930
- scrollIfPinned(tabId, terminal);
18967
+ terminal.write(data, () => reassert(state));
18931
18968
  }
18932
18969
  });
18933
18970
  state.unsubExit = window.worker.onPtyExit((eventTabId, code) => {
@@ -18955,6 +18992,14 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
18955
18992
  const containerRef = reactExports.useRef(null);
18956
18993
  const [menu, setMenu] = reactExports.useState(null);
18957
18994
  const [toast, setToast] = reactExports.useState(null);
18995
+ const [followBottom, setFollowBottom] = reactExports.useState(true);
18996
+ const jumpToLatest = () => {
18997
+ const s = _terminals.get(tabId);
18998
+ if (!s) return;
18999
+ setPinned(s, true);
19000
+ s.terminal.scrollToBottom();
19001
+ s.terminal.focus();
19002
+ };
18958
19003
  const handleContextMenu = (e) => {
18959
19004
  const sel = _terminals.get(tabId)?.terminal.getSelection() ?? "";
18960
19005
  if (!convertToEmailTable(sel)) return;
@@ -19006,7 +19051,10 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19006
19051
  terminal.open(containerRef.current);
19007
19052
  }
19008
19053
  fitAddon.fit();
19009
- scrollIfPinned(tabId, terminal);
19054
+ const attached = _terminals.get(tabId);
19055
+ if (attached) reassert(attached);
19056
+ const onWheel = () => markGesture(tabId);
19057
+ terminal.element?.addEventListener("wheel", onWheel, { passive: true });
19010
19058
  let resizeTimeout;
19011
19059
  const handleResize = () => {
19012
19060
  clearTimeout(resizeTimeout);
@@ -19018,7 +19066,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19018
19066
  state.fitAddon.fit();
19019
19067
  const { cols, rows } = state.terminal;
19020
19068
  if (cols < 10 || rows < 5) return;
19021
- scrollIfPinned(tabId, state.terminal);
19069
+ reassert(state);
19022
19070
  window.worker.resizePty(tabId, cols, rows);
19023
19071
  }, 150);
19024
19072
  };
@@ -19029,6 +19077,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19029
19077
  clearTimeout(resizeTimeout);
19030
19078
  window.removeEventListener("resize", handleResize);
19031
19079
  observer.disconnect();
19080
+ terminal.element?.removeEventListener("wheel", onWheel);
19032
19081
  };
19033
19082
  }, [tabId, onExit]);
19034
19083
  reactExports.useEffect(() => {
@@ -19042,7 +19091,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19042
19091
  const s = _terminals.get(tabId);
19043
19092
  if (s) {
19044
19093
  s.fitAddon.fit();
19045
- s.pinnedToBottom = true;
19094
+ setPinned(s, true);
19046
19095
  s.terminal.scrollToBottom();
19047
19096
  const { cols, rows } = s.terminal;
19048
19097
  window.worker.resizePty(tabId, cols, rows);
@@ -19059,7 +19108,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19059
19108
  if (state) {
19060
19109
  state.fitAddon.fit();
19061
19110
  state.terminal.focus();
19062
- scrollIfPinned(tabId, state.terminal);
19111
+ reassert(state);
19063
19112
  const { cols, rows } = state.terminal;
19064
19113
  window.worker.resizePty(tabId, cols, rows);
19065
19114
  }
@@ -19067,8 +19116,33 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19067
19116
  });
19068
19117
  }
19069
19118
  }, [isActive, tabId]);
19119
+ reactExports.useEffect(() => {
19120
+ const s = _terminals.get(tabId);
19121
+ if (!s) return;
19122
+ setFollowBottom(s.pinnedToBottom);
19123
+ const fn = (p) => setFollowBottom(p);
19124
+ s.pinListeners.add(fn);
19125
+ return () => {
19126
+ s.pinListeners.delete(fn);
19127
+ };
19128
+ }, [tabId]);
19070
19129
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19071
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "terminal-container", ref: containerRef, onContextMenu: handleContextMenu }),
19130
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "terminal-wrap", children: [
19131
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "terminal-container", ref: containerRef, onContextMenu: handleContextMenu }),
19132
+ !followBottom && /* @__PURE__ */ jsxRuntimeExports.jsx(
19133
+ "button",
19134
+ {
19135
+ type: "button",
19136
+ className: "term-jump-latest",
19137
+ onMouseDown: (e) => {
19138
+ e.preventDefault();
19139
+ jumpToLatest();
19140
+ },
19141
+ title: "Jump to latest output",
19142
+ children: "↓ Latest"
19143
+ }
19144
+ )
19145
+ ] }),
19072
19146
  menu && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "term-context-menu", style: { position: "fixed", left: menu.x, top: menu.y }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onMouseDown: (e) => {
19073
19147
  e.preventDefault();
19074
19148
  runConvert();
@@ -20022,7 +20096,7 @@ function FloatingMic({ onTranscript, onHide }) {
20022
20096
  ] });
20023
20097
  }
20024
20098
  const SAMPLE = "This is how spoken agent replies will sound.";
20025
- function SpeakControls() {
20099
+ function SpeakControls({ remote = false }) {
20026
20100
  const [enabled, setEnabled] = reactExports.useState(false);
20027
20101
  const [engine, setEngineState] = reactExports.useState(getEngine);
20028
20102
  const [voices, setVoices] = reactExports.useState([]);
@@ -20114,7 +20188,7 @@ function SpeakControls() {
20114
20188
  speech.stop();
20115
20189
  speech.enqueue(SAMPLE);
20116
20190
  }, []);
20117
- const title = enabled ? "Spoken replies on — click to mute" : "Speak agent replies (off)";
20191
+ const title = remote ? "Spoken replies aren't available for remote SSH sessions — the agent's transcript lives on the remote machine" : enabled ? "Spoken replies on — click to mute" : "Speak agent replies (off)";
20118
20192
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "speak-controls", ref: wrapRef, children: [
20119
20193
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
20120
20194
  "button",
@@ -20123,6 +20197,7 @@ function SpeakControls() {
20123
20197
  className: `titlebar-btn titlebar-icon-btn speak-btn ${enabled ? "active" : ""}`,
20124
20198
  onClick: toggle,
20125
20199
  title,
20200
+ disabled: remote,
20126
20201
  "aria-label": "Toggle spoken agent replies",
20127
20202
  "aria-pressed": enabled,
20128
20203
  children: [
@@ -207841,7 +207916,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
207841
207916
  modeConfigurationDefault$2
207842
207917
  );
207843
207918
  function getMode$3() {
207844
- return __vitePreload(() => import("./cssMode-Bmzy9hKX.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
207919
+ return __vitePreload(() => import("./cssMode-D7r66wvO.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
207845
207920
  }
207846
207921
  languages.onLanguage("less", () => {
207847
207922
  getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
@@ -207946,7 +208021,7 @@ const razorLanguageService = registerHTMLLanguageService(
207946
208021
  );
207947
208022
  const razorDefaults = razorLanguageService.defaults;
207948
208023
  function getMode$2() {
207949
- return __vitePreload(() => import("./htmlMode-DD7YPgga.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
208024
+ return __vitePreload(() => import("./htmlMode-D4VByBT6.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
207950
208025
  }
207951
208026
  function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
207952
208027
  const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
@@ -208030,7 +208105,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
208030
208105
  );
208031
208106
  const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
208032
208107
  function getMode$1() {
208033
- return __vitePreload(() => import("./jsonMode-CTuGgRvC.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
208108
+ return __vitePreload(() => import("./jsonMode-CJ5AYSPV.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
208034
208109
  }
208035
208110
  languages.register({
208036
208111
  id: "json",
@@ -208276,7 +208351,7 @@ const getJavaScriptWorker = () => {
208276
208351
  return getMode().then((mode) => mode.getJavaScriptWorker());
208277
208352
  };
208278
208353
  function getMode() {
208279
- return __vitePreload(() => import("./tsMode-YTLgCayh.js"), true ? [] : void 0, import.meta.url);
208354
+ return __vitePreload(() => import("./tsMode-DF9m6_3n.js"), true ? [] : void 0, import.meta.url);
208280
208355
  }
208281
208356
  languages.onLanguage("typescript", () => {
208282
208357
  return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
@@ -208471,49 +208546,49 @@ registerLanguage({
208471
208546
  extensions: [".ftl", ".ftlh", ".ftlx"],
208472
208547
  aliases: ["FreeMarker2", "Apache FreeMarker2"],
208473
208548
  loader: () => {
208474
- return __vitePreload(() => import("./freemarker2-CDAsExTa.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208549
+ return __vitePreload(() => import("./freemarker2-jSWJIjPG.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208475
208550
  }
208476
208551
  });
208477
208552
  registerLanguage({
208478
208553
  id: "freemarker2.tag-angle.interpolation-dollar",
208479
208554
  aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
208480
208555
  loader: () => {
208481
- return __vitePreload(() => import("./freemarker2-CDAsExTa.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
208556
+ return __vitePreload(() => import("./freemarker2-jSWJIjPG.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
208482
208557
  }
208483
208558
  });
208484
208559
  registerLanguage({
208485
208560
  id: "freemarker2.tag-bracket.interpolation-dollar",
208486
208561
  aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
208487
208562
  loader: () => {
208488
- return __vitePreload(() => import("./freemarker2-CDAsExTa.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
208563
+ return __vitePreload(() => import("./freemarker2-jSWJIjPG.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
208489
208564
  }
208490
208565
  });
208491
208566
  registerLanguage({
208492
208567
  id: "freemarker2.tag-angle.interpolation-bracket",
208493
208568
  aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
208494
208569
  loader: () => {
208495
- return __vitePreload(() => import("./freemarker2-CDAsExTa.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
208570
+ return __vitePreload(() => import("./freemarker2-jSWJIjPG.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
208496
208571
  }
208497
208572
  });
208498
208573
  registerLanguage({
208499
208574
  id: "freemarker2.tag-bracket.interpolation-bracket",
208500
208575
  aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
208501
208576
  loader: () => {
208502
- return __vitePreload(() => import("./freemarker2-CDAsExTa.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
208577
+ return __vitePreload(() => import("./freemarker2-jSWJIjPG.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
208503
208578
  }
208504
208579
  });
208505
208580
  registerLanguage({
208506
208581
  id: "freemarker2.tag-auto.interpolation-dollar",
208507
208582
  aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
208508
208583
  loader: () => {
208509
- return __vitePreload(() => import("./freemarker2-CDAsExTa.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208584
+ return __vitePreload(() => import("./freemarker2-jSWJIjPG.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208510
208585
  }
208511
208586
  });
208512
208587
  registerLanguage({
208513
208588
  id: "freemarker2.tag-auto.interpolation-bracket",
208514
208589
  aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
208515
208590
  loader: () => {
208516
- return __vitePreload(() => import("./freemarker2-CDAsExTa.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
208591
+ return __vitePreload(() => import("./freemarker2-jSWJIjPG.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
208517
208592
  }
208518
208593
  });
208519
208594
  registerLanguage({
@@ -208534,7 +208609,7 @@ registerLanguage({
208534
208609
  extensions: [".handlebars", ".hbs"],
208535
208610
  aliases: ["Handlebars", "handlebars", "hbs"],
208536
208611
  mimetypes: ["text/x-handlebars-template"],
208537
- loader: () => __vitePreload(() => import("./handlebars-CvpBYG1L.js"), true ? [] : void 0, import.meta.url)
208612
+ loader: () => __vitePreload(() => import("./handlebars-BnFyz5RH.js"), true ? [] : void 0, import.meta.url)
208538
208613
  });
208539
208614
  registerLanguage({
208540
208615
  id: "hcl",
@@ -208547,7 +208622,7 @@ registerLanguage({
208547
208622
  extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
208548
208623
  aliases: ["HTML", "htm", "html", "xhtml"],
208549
208624
  mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
208550
- loader: () => __vitePreload(() => import("./html-CNhEQqr8.js"), true ? [] : void 0, import.meta.url)
208625
+ loader: () => __vitePreload(() => import("./html-Bl5UFavp.js"), true ? [] : void 0, import.meta.url)
208551
208626
  });
208552
208627
  registerLanguage({
208553
208628
  id: "ini",
@@ -208570,7 +208645,7 @@ registerLanguage({
208570
208645
  filenames: ["jakefile"],
208571
208646
  aliases: ["JavaScript", "javascript", "js"],
208572
208647
  mimetypes: ["text/javascript"],
208573
- loader: () => __vitePreload(() => import("./javascript-CsGLsswK.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
208648
+ loader: () => __vitePreload(() => import("./javascript-CMN1OmwG.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
208574
208649
  });
208575
208650
  registerLanguage({
208576
208651
  id: "julia",
@@ -208609,7 +208684,7 @@ registerLanguage({
208609
208684
  extensions: [".liquid", ".html.liquid"],
208610
208685
  aliases: ["Liquid", "liquid"],
208611
208686
  mimetypes: ["application/liquid"],
208612
- loader: () => __vitePreload(() => import("./liquid-BrrN4uul.js"), true ? [] : void 0, import.meta.url)
208687
+ loader: () => __vitePreload(() => import("./liquid-BGMIDTEm.js"), true ? [] : void 0, import.meta.url)
208613
208688
  });
208614
208689
  registerLanguage({
208615
208690
  id: "m3",
@@ -208627,7 +208702,7 @@ registerLanguage({
208627
208702
  id: "mdx",
208628
208703
  extensions: [".mdx"],
208629
208704
  aliases: ["MDX", "mdx"],
208630
- loader: () => __vitePreload(() => import("./mdx-DJo-gax4.js"), true ? [] : void 0, import.meta.url)
208705
+ loader: () => __vitePreload(() => import("./mdx-B2Z7R10Q.js"), true ? [] : void 0, import.meta.url)
208631
208706
  });
208632
208707
  registerLanguage({
208633
208708
  id: "mips",
@@ -208726,7 +208801,7 @@ registerLanguage({
208726
208801
  extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
208727
208802
  aliases: ["Python", "py"],
208728
208803
  firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
208729
- loader: () => __vitePreload(() => import("./python-DYHeg5b2.js"), true ? [] : void 0, import.meta.url)
208804
+ loader: () => __vitePreload(() => import("./python-LMDMSSdn.js"), true ? [] : void 0, import.meta.url)
208730
208805
  });
208731
208806
  registerLanguage({
208732
208807
  id: "qsharp",
@@ -208745,7 +208820,7 @@ registerLanguage({
208745
208820
  extensions: [".cshtml"],
208746
208821
  aliases: ["Razor", "razor"],
208747
208822
  mimetypes: ["text/x-cshtml"],
208748
- loader: () => __vitePreload(() => import("./razor-Cqcks14K.js"), true ? [] : void 0, import.meta.url)
208823
+ loader: () => __vitePreload(() => import("./razor-BZSrziPu.js"), true ? [] : void 0, import.meta.url)
208749
208824
  });
208750
208825
  registerLanguage({
208751
208826
  id: "redis",
@@ -208878,7 +208953,7 @@ registerLanguage({
208878
208953
  aliases: ["TypeScript", "ts", "typescript"],
208879
208954
  mimetypes: ["text/typescript"],
208880
208955
  loader: () => {
208881
- return __vitePreload(() => import("./typescript-oxBn_Vcw.js"), true ? [] : void 0, import.meta.url);
208956
+ return __vitePreload(() => import("./typescript-rZeCMf0a.js"), true ? [] : void 0, import.meta.url);
208882
208957
  }
208883
208958
  });
208884
208959
  registerLanguage({
@@ -208923,14 +208998,14 @@ registerLanguage({
208923
208998
  firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
208924
208999
  aliases: ["XML", "xml"],
208925
209000
  mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
208926
- loader: () => __vitePreload(() => import("./xml-BEqGMCTc.js"), true ? [] : void 0, import.meta.url)
209001
+ loader: () => __vitePreload(() => import("./xml-fNSZ-7so.js"), true ? [] : void 0, import.meta.url)
208927
209002
  });
208928
209003
  registerLanguage({
208929
209004
  id: "yaml",
208930
209005
  extensions: [".yaml", ".yml"],
208931
209006
  aliases: ["YAML", "yaml", "YML", "yml"],
208932
209007
  mimetypes: ["application/x-yaml", "text/x-yaml"],
208933
- loader: () => __vitePreload(() => import("./yaml-BGSrk5HC.js"), true ? [] : void 0, import.meta.url)
209008
+ loader: () => __vitePreload(() => import("./yaml-1wxIuvzh.js"), true ? [] : void 0, import.meta.url)
208934
209009
  });
208935
209010
  var __defProp = Object.defineProperty;
208936
209011
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -212419,10 +212494,276 @@ function EditorPanel({ cwd: cwd2 }) {
212419
212494
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "editor-placeholder", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Select a file to edit" }) }) })
212420
212495
  ] });
212421
212496
  }
212497
+ const TMUX_PREFIX = "ctlsurf_";
212498
+ function nextSessionName(sessions) {
212499
+ const used = new Set(
212500
+ sessions.filter((s) => s.startsWith(TMUX_PREFIX)).map((s) => parseInt(s.slice(TMUX_PREFIX.length), 10)).filter((n22) => Number.isInteger(n22) && n22 > 0)
212501
+ );
212502
+ let n2 = 1;
212503
+ while (used.has(n2)) n2++;
212504
+ return `${TMUX_PREFIX}${n2}`;
212505
+ }
212506
+ function RemoteConnect({ onConnect, onBack }) {
212507
+ const [step, setStep] = reactExports.useState("hosts");
212508
+ const [configHosts, setConfigHosts] = reactExports.useState([]);
212509
+ const [recents, setRecents] = reactExports.useState([]);
212510
+ const [manual, setManual] = reactExports.useState("");
212511
+ const [agentCmd, setAgentCmd] = reactExports.useState("claude");
212512
+ const [target, setTarget] = reactExports.useState("");
212513
+ const [probe, setProbe] = reactExports.useState(null);
212514
+ const [errorMsg, setErrorMsg] = reactExports.useState("");
212515
+ const [password, setPassword] = reactExports.useState("");
212516
+ reactExports.useEffect(() => {
212517
+ let cancelled = false;
212518
+ window.worker.sshListHosts().then((r) => {
212519
+ if (cancelled) return;
212520
+ setConfigHosts(r.configHosts || []);
212521
+ setRecents(r.recents || []);
212522
+ }).catch(() => {
212523
+ });
212524
+ return () => {
212525
+ cancelled = true;
212526
+ };
212527
+ }, []);
212528
+ const startProbe = async (t) => {
212529
+ const dest = t.trim();
212530
+ if (!dest) return;
212531
+ setTarget(dest);
212532
+ setStep("probing");
212533
+ try {
212534
+ const result = await window.worker.sshProbe(dest);
212535
+ setProbe(result);
212536
+ if (result.reachable || result.authRequired) {
212537
+ setStep("action");
212538
+ } else {
212539
+ setErrorMsg(result.error || "Could not connect");
212540
+ setStep("error");
212541
+ }
212542
+ } catch (err) {
212543
+ setErrorMsg(err?.message || "Probe failed");
212544
+ setStep("error");
212545
+ }
212546
+ };
212547
+ const connect = async (action) => {
212548
+ try {
212549
+ const agent = await window.worker.sshBuildAgent(target, action, agentCmd);
212550
+ if (password) {
212551
+ await window.worker.sshSetPassword(target, password).catch(() => {
212552
+ });
212553
+ }
212554
+ setPassword("");
212555
+ window.worker.sshAddRecent(target).catch(() => {
212556
+ });
212557
+ onConnect(agent);
212558
+ } catch (err) {
212559
+ setErrorMsg(err?.message || "Failed to build connection");
212560
+ setStep("error");
212561
+ }
212562
+ };
212563
+ const passwordField = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-pw", children: [
212564
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
212565
+ "input",
212566
+ {
212567
+ className: "remote-input",
212568
+ type: "password",
212569
+ placeholder: "Password (optional — blank prompts in terminal)",
212570
+ value: password,
212571
+ onChange: (e) => setPassword(e.target.value),
212572
+ autoCapitalize: "off",
212573
+ autoCorrect: "off",
212574
+ spellCheck: false
212575
+ }
212576
+ ),
212577
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-pw-hint", children: "Used once for this connection — never stored." })
212578
+ ] });
212579
+ const agentLabel = agentCmd === "claude" ? "Claude Code" : "shell";
212580
+ const agentToggle = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-toggle", children: [
212581
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "remote-toggle-label", children: "Run:" }),
212582
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
212583
+ "button",
212584
+ {
212585
+ className: `remote-toggle-btn${agentCmd === "claude" ? " is-active" : ""}`,
212586
+ onClick: () => setAgentCmd("claude"),
212587
+ children: "Claude Code"
212588
+ }
212589
+ ),
212590
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
212591
+ "button",
212592
+ {
212593
+ className: `remote-toggle-btn${agentCmd === "" ? " is-active" : ""}`,
212594
+ onClick: () => setAgentCmd(""),
212595
+ children: "Shell"
212596
+ }
212597
+ )
212598
+ ] });
212599
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
212600
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
212601
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: "CONNECT TO REMOTE" }),
212602
+ step === "hosts" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212603
+ agentToggle,
212604
+ recents.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212605
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "RECENT" }),
212606
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-list", children: recents.map((r) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "agent-picker-item", onClick: () => startProbe(r), children: [
212607
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: r }),
212608
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "reconnect" })
212609
+ ] }, r)) })
212610
+ ] }),
212611
+ configHosts.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212612
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "~/.ssh/config" }),
212613
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-list remote-host-scroll", children: configHosts.map((h2) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "agent-picker-item", onClick: () => startProbe(h2.alias), children: [
212614
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: h2.alias }),
212615
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "agent-picker-desc", children: [
212616
+ h2.user ? `${h2.user}@` : "",
212617
+ h2.hostName || ""
212618
+ ] })
212619
+ ] }, h2.alias)) })
212620
+ ] }),
212621
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "OR TYPE A HOST" }),
212622
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212623
+ "form",
212624
+ {
212625
+ className: "remote-manual",
212626
+ onSubmit: (e) => {
212627
+ e.preventDefault();
212628
+ startProbe(manual);
212629
+ },
212630
+ children: [
212631
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
212632
+ "input",
212633
+ {
212634
+ className: "remote-input",
212635
+ type: "text",
212636
+ placeholder: "user@host",
212637
+ value: manual,
212638
+ onChange: (e) => setManual(e.target.value),
212639
+ autoCapitalize: "off",
212640
+ autoCorrect: "off",
212641
+ spellCheck: false
212642
+ }
212643
+ ),
212644
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-btn", type: "submit", disabled: !manual.trim(), children: "Connect" })
212645
+ ]
212646
+ }
212647
+ ),
212648
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", onClick: onBack, children: "← back to agents" })
212649
+ ] }),
212650
+ step === "probing" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-status", children: [
212651
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-spinner" }),
212652
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
212653
+ "Connecting to ",
212654
+ /* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: target }),
212655
+ "…"
212656
+ ] }),
212657
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-status-sub", children: "Checking for tmux sessions" })
212658
+ ] }),
212659
+ step === "action" && probe && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212660
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-target-line", children: [
212661
+ /* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: target }),
212662
+ " · run ",
212663
+ agentLabel
212664
+ ] }),
212665
+ probe.authRequired && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212666
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: "This host needs a password. Enter it below, or leave blank to type it at ssh's prompt in the terminal. Either way it is never stored." }),
212667
+ passwordField
212668
+ ] }),
212669
+ probe.reachable && probe.sessions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212670
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "REATTACH" }),
212671
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-list", children: [...probe.sessions].sort((a, b) => a.startsWith(TMUX_PREFIX) === b.startsWith(TMUX_PREFIX) ? 0 : a.startsWith(TMUX_PREFIX) ? -1 : 1).map((s) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
212672
+ "button",
212673
+ {
212674
+ className: "agent-picker-item",
212675
+ onClick: () => connect({ kind: "attach", session: s }),
212676
+ children: [
212677
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: s }),
212678
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "attach running tmux session" })
212679
+ ]
212680
+ },
212681
+ s
212682
+ )) })
212683
+ ] }),
212684
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "START" }),
212685
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-list", children: [
212686
+ (probe.reachable && probe.tmuxInstalled || probe.authRequired) && /* @__PURE__ */ jsxRuntimeExports.jsxs(
212687
+ "button",
212688
+ {
212689
+ className: "agent-picker-item",
212690
+ onClick: () => {
212691
+ const session = probe.reachable ? nextSessionName(probe.sessions) : `${TMUX_PREFIX}1`;
212692
+ connect(probe.reachable ? { kind: "new-tmux", session } : { kind: "auto", session });
212693
+ },
212694
+ children: [
212695
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "agent-picker-name", children: [
212696
+ "New tmux session (",
212697
+ probe.reachable ? nextSessionName(probe.sessions) : `${TMUX_PREFIX}1`,
212698
+ ")"
212699
+ ] }),
212700
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "persists across disconnects (recommended)" })
212701
+ ]
212702
+ }
212703
+ ),
212704
+ probe.reachable && !probe.tmuxInstalled && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: "tmux isn't installed on this host — running without it." }),
212705
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212706
+ "button",
212707
+ {
212708
+ className: "agent-picker-item",
212709
+ onClick: () => connect({ kind: "no-tmux" }),
212710
+ children: [
212711
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "Run without tmux" }),
212712
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "agent-picker-desc", children: [
212713
+ agentLabel,
212714
+ " directly — won't survive a dropped connection"
212715
+ ] })
212716
+ ]
212717
+ }
212718
+ )
212719
+ ] }),
212720
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", onClick: () => setStep("hosts"), children: "← choose another host" })
212721
+ ] }),
212722
+ step === "error" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212723
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-error", children: [
212724
+ "Couldn't connect to ",
212725
+ /* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: target })
212726
+ ] }),
212727
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-status-sub", children: errorMsg }),
212728
+ passwordField,
212729
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "TRY ANYWAY (INTERACTIVE)" }),
212730
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-list", children: [
212731
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212732
+ "button",
212733
+ {
212734
+ className: "agent-picker-item",
212735
+ onClick: () => connect({ kind: "auto", session: `${TMUX_PREFIX}1` }),
212736
+ children: [
212737
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "Connect with tmux" }),
212738
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "ssh will prompt if a password / key passphrase is needed" })
212739
+ ]
212740
+ }
212741
+ ),
212742
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212743
+ "button",
212744
+ {
212745
+ className: "agent-picker-item",
212746
+ onClick: () => connect({ kind: "no-tmux" }),
212747
+ children: [
212748
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "Connect without tmux" }),
212749
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "agent-picker-desc", children: [
212750
+ "run ",
212751
+ agentLabel,
212752
+ " directly"
212753
+ ] })
212754
+ ]
212755
+ }
212756
+ )
212757
+ ] }),
212758
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", onClick: () => setStep("hosts"), children: "← choose another host" })
212759
+ ] })
212760
+ ] }) });
212761
+ }
212422
212762
  function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
212423
212763
  const displayPath = cwd2.replace(/^\/Users\/[^/]+/, "~");
212424
212764
  const [logChat, setLogChat] = reactExports.useState(false);
212425
212765
  const [loaded, setLoaded] = reactExports.useState(false);
212766
+ const [remoteMode, setRemoteMode] = reactExports.useState(false);
212426
212767
  reactExports.useEffect(() => {
212427
212768
  let cancelled = false;
212428
212769
  window.worker.getLogChat().then((r) => {
@@ -212451,6 +212792,17 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
212451
212792
  }
212452
212793
  onSelect(agent);
212453
212794
  };
212795
+ if (remoteMode) {
212796
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
212797
+ RemoteConnect,
212798
+ {
212799
+ onConnect: (agent) => {
212800
+ handleSelect(agent);
212801
+ },
212802
+ onBack: () => setRemoteMode(false)
212803
+ }
212804
+ );
212805
+ }
212454
212806
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
212455
212807
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
212456
212808
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-cwd", onClick: onChangeCwd, title: "Click to change directory", children: [
@@ -212459,18 +212811,31 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
212459
212811
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-cwd-change", children: "change" })
212460
212812
  ] }),
212461
212813
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: "SELECT AGENT" }),
212462
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-list", children: agents.map((a) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
212463
- "button",
212464
- {
212465
- className: "agent-picker-item",
212466
- onClick: () => handleSelect(a),
212467
- children: [
212468
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: a.name }),
212469
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: a.description })
212470
- ]
212471
- },
212472
- a.id
212473
- )) }),
212814
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-list", children: [
212815
+ agents.map((a) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
212816
+ "button",
212817
+ {
212818
+ className: "agent-picker-item",
212819
+ onClick: () => handleSelect(a),
212820
+ children: [
212821
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: a.name }),
212822
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: a.description })
212823
+ ]
212824
+ },
212825
+ a.id
212826
+ )),
212827
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212828
+ "button",
212829
+ {
212830
+ className: "agent-picker-item",
212831
+ onClick: () => setRemoteMode(true),
212832
+ children: [
212833
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "🌐 Remote (SSH)" }),
212834
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "Run an agent on a remote machine" })
212835
+ ]
212836
+ }
212837
+ )
212838
+ ] }),
212474
212839
  /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "agent-picker-option", onClick: (e) => e.stopPropagation(), children: [
212475
212840
  /* @__PURE__ */ jsxRuntimeExports.jsx(
212476
212841
  "input",
@@ -212485,6 +212850,84 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
212485
212850
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-hint", children: "Select an agent to start" })
212486
212851
  ] }) });
212487
212852
  }
212853
+ function ProjectPicker({ onDone }) {
212854
+ const [state, setState2] = reactExports.useState("loading");
212855
+ const [projects, setProjects] = reactExports.useState([]);
212856
+ const [errorMsg, setErrorMsg] = reactExports.useState("");
212857
+ const [binding, setBinding] = reactExports.useState(false);
212858
+ const [filter, setFilter] = reactExports.useState("");
212859
+ reactExports.useEffect(() => {
212860
+ let cancelled = false;
212861
+ window.worker.listProjects().then((r) => {
212862
+ if (cancelled) return;
212863
+ if (r.ok) {
212864
+ setProjects(r.projects || []);
212865
+ setState2("ready");
212866
+ } else {
212867
+ setErrorMsg(r.error || "Could not load projects");
212868
+ setState2("error");
212869
+ }
212870
+ }).catch((err) => {
212871
+ if (cancelled) return;
212872
+ setErrorMsg(err?.message || "Could not load projects");
212873
+ setState2("error");
212874
+ });
212875
+ return () => {
212876
+ cancelled = true;
212877
+ };
212878
+ }, []);
212879
+ const pick = async (p) => {
212880
+ setBinding(true);
212881
+ try {
212882
+ await window.worker.bindProject(p.root_path);
212883
+ } catch {
212884
+ }
212885
+ onDone();
212886
+ };
212887
+ const shown = filter.trim() ? projects.filter((p) => p.name.toLowerCase().includes(filter.toLowerCase()) || p.root_path.toLowerCase().includes(filter.toLowerCase())) : projects;
212888
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
212889
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
212890
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: "SELECT CTLSURF PROJECT" }),
212891
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-target-line", children: "Bind this remote session to a project" }),
212892
+ state === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-status", children: [
212893
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-spinner" }),
212894
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading projects…" })
212895
+ ] }),
212896
+ state === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: errorMsg }) }),
212897
+ state === "ready" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212898
+ projects.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: "No ctlsurf projects found. You can skip and bind one later." }),
212899
+ projects.length > 6 && /* @__PURE__ */ jsxRuntimeExports.jsx(
212900
+ "input",
212901
+ {
212902
+ className: "remote-input",
212903
+ style: { width: "100%", boxSizing: "border-box", margin: "4px 0 8px" },
212904
+ type: "text",
212905
+ placeholder: "Filter projects…",
212906
+ value: filter,
212907
+ onChange: (e) => setFilter(e.target.value),
212908
+ autoCapitalize: "off",
212909
+ autoCorrect: "off",
212910
+ spellCheck: false
212911
+ }
212912
+ ),
212913
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-list remote-host-scroll", children: shown.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
212914
+ "button",
212915
+ {
212916
+ className: "agent-picker-item project-item",
212917
+ disabled: binding,
212918
+ onClick: () => pick(p),
212919
+ title: p.root_path,
212920
+ children: [
212921
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: p.name }),
212922
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: p.root_path })
212923
+ ]
212924
+ },
212925
+ p.id
212926
+ )) })
212927
+ ] }),
212928
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", disabled: binding, onClick: onDone, children: "Skip — no project" })
212929
+ ] }) });
212930
+ }
212488
212931
  function removePane(node, paneId) {
212489
212932
  if (node.type === "leaf") {
212490
212933
  return node.paneId === paneId ? null : node;
@@ -213094,12 +213537,36 @@ function Segmented({ options, value, onChange }) {
213094
213537
  );
213095
213538
  }) });
213096
213539
  }
213540
+ function TicketCard({ ticket, onClick }) {
213541
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ticket-card", onClick, children: [
213542
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-bar", style: { background: colorOf(STATUS_OPTIONS, ticket.status) } }),
213543
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-main", children: [
213544
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-title", children: ticket.title || "Untitled" }),
213545
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-meta", children: [
213546
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ticket-card-status", style: { color: colorOf(STATUS_OPTIONS, ticket.status) }, children: [
213547
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-seg-dot", style: { background: colorOf(STATUS_OPTIONS, ticket.status) } }),
213548
+ ticket.status
213549
+ ] }),
213550
+ ticket.created && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-date", children: formatDate(ticket.created) })
213551
+ ] })
213552
+ ] }),
213553
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
213554
+ "span",
213555
+ {
213556
+ className: "ticket-card-pri",
213557
+ style: { color: colorOf(PRIORITY_OPTIONS, ticket.priority), borderColor: `${colorOf(PRIORITY_OPTIONS, ticket.priority)}66` },
213558
+ children: ticket.priority
213559
+ }
213560
+ )
213561
+ ] });
213562
+ }
213097
213563
  function TicketPane() {
213098
213564
  const [project, setProject] = reactExports.useState(null);
213099
213565
  const [view, setView] = reactExports.useState("list");
213100
213566
  const [tickets, setTickets] = reactExports.useState([]);
213101
213567
  const [listLoading, setListLoading] = reactExports.useState(false);
213102
213568
  const [listError, setListError] = reactExports.useState(null);
213569
+ const [showDone, setShowDone] = reactExports.useState(false);
213103
213570
  const [editingId, setEditingId] = reactExports.useState(null);
213104
213571
  const [title, setTitle] = reactExports.useState("");
213105
213572
  const [description, setDescription] = reactExports.useState("");
@@ -213189,17 +213656,22 @@ function TicketPane() {
213189
213656
  window.addEventListener("keydown", onKey);
213190
213657
  return () => window.removeEventListener("keydown", onKey);
213191
213658
  }, [view, handleSave]);
213659
+ const activeTickets = tickets.filter((t) => t.status !== "Done");
213660
+ const doneTickets = tickets.filter((t) => t.status === "Done");
213192
213661
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-pane", role: "region", "aria-label": "Tickets", children: [
213193
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-drawer-header", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-heading", children: [
213194
- view === "form" ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-drawer-back", onClick: () => setView("list"), title: "Back (Esc)", children: "‹" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-badge", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, { size: 16 }) }),
213195
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-headtext", children: [
213196
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-title", children: view === "form" ? editingId ? "Edit ticket" : "New ticket" : "Tickets" }),
213197
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-subtitle", children: project ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213198
- "in ",
213199
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: project })
213200
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-warn", children: "no ctlsurf project for this tab" }) })
213201
- ] })
213202
- ] }) }),
213662
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-header", children: [
213663
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-heading", children: [
213664
+ view === "form" ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-drawer-back", onClick: () => setView("list"), title: "Back (Esc)", children: "‹" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-badge", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, { size: 16 }) }),
213665
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-headtext", children: [
213666
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-title", children: view === "form" ? editingId ? "Edit ticket" : "New ticket" : "Tickets" }),
213667
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-subtitle", children: project ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213668
+ "in ",
213669
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: project })
213670
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-warn", children: "no ctlsurf project for this tab" }) })
213671
+ ] })
213672
+ ] }),
213673
+ view === "list" && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-drawer-new", onClick: openNewForm, title: "New ticket", children: "+ New" })
213674
+ ] }),
213203
213675
  view === "list" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213204
213676
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-list-body", children: [
213205
213677
  listLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-list-msg", children: "Loading tickets…" }),
@@ -213212,32 +213684,27 @@ function TicketPane() {
213212
213684
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-title", children: "No tickets yet" }),
213213
213685
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-hint", children: "Log the first one for this project." })
213214
213686
  ] }),
213215
- !listLoading && tickets.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ticket-card", onClick: () => openEditForm(t), children: [
213216
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-bar", style: { background: colorOf(STATUS_OPTIONS, t.status) } }),
213217
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-main", children: [
213218
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-title", children: t.title || "Untitled" }),
213219
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-meta", children: [
213220
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ticket-card-status", style: { color: colorOf(STATUS_OPTIONS, t.status) }, children: [
213221
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-seg-dot", style: { background: colorOf(STATUS_OPTIONS, t.status) } }),
213222
- t.status
213223
- ] }),
213224
- t.created && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-date", children: formatDate(t.created) })
213225
- ] })
213226
- ] }),
213227
- /* @__PURE__ */ jsxRuntimeExports.jsx(
213228
- "span",
213687
+ !listLoading && activeTickets.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsx(TicketCard, { ticket: t, onClick: () => openEditForm(t) }, t.id)),
213688
+ !listLoading && activeTickets.length === 0 && doneTickets.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-list-msg", children: "Nothing open — all caught up." }),
213689
+ !listLoading && doneTickets.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213690
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
213691
+ "button",
213229
213692
  {
213230
- className: "ticket-card-pri",
213231
- style: { color: colorOf(PRIORITY_OPTIONS, t.priority), borderColor: `${colorOf(PRIORITY_OPTIONS, t.priority)}66` },
213232
- children: t.priority
213693
+ type: "button",
213694
+ className: "ticket-done-toggle",
213695
+ onClick: () => setShowDone((v2) => !v2),
213696
+ "aria-expanded": showDone,
213697
+ children: [
213698
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `ticket-done-caret ${showDone ? "open" : ""}`, "aria-hidden": "true", children: "▸" }),
213699
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-done-label", children: "Done" }),
213700
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-done-count", children: doneTickets.length })
213701
+ ]
213233
213702
  }
213234
- )
213235
- ] }, t.id))
213703
+ ),
213704
+ showDone && doneTickets.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsx(TicketCard, { ticket: t, onClick: () => openEditForm(t) }, t.id))
213705
+ ] })
213236
213706
  ] }),
213237
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-footer", children: [
213238
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-hint", children: tickets.length > 0 && `${tickets.length} ticket${tickets.length === 1 ? "" : "s"}` }),
213239
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-btn-primary", onClick: openNewForm, children: "+ New ticket" })
213240
- ] })
213707
+ tickets.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-drawer-footer", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-hint", children: doneTickets.length > 0 ? `${activeTickets.length} open · ${doneTickets.length} done` : `${tickets.length} ticket${tickets.length === 1 ? "" : "s"}` }) })
213241
213708
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213242
213709
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-body", children: [
213243
213710
  /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "ticket-field", children: [
@@ -213346,6 +213813,7 @@ function App() {
213346
213813
  }, []);
213347
213814
  const [pickerTargetTabId, setPickerTargetTabId] = reactExports.useState(tabs[0].id);
213348
213815
  const [showAgentPicker, setShowAgentPicker] = reactExports.useState(true);
213816
+ const [showProjectPicker, setShowProjectPicker] = reactExports.useState(false);
213349
213817
  const visiblePaneIds = findPaneIds(layout2);
213350
213818
  reactExports.useEffect(() => {
213351
213819
  async function init2() {
@@ -213478,6 +213946,9 @@ function App() {
213478
213946
  }
213479
213947
  setShowAgentPicker(false);
213480
213948
  setPickerTargetTabId(null);
213949
+ if (agent.id.startsWith("remote-")) {
213950
+ setShowProjectPicker(true);
213951
+ }
213481
213952
  }, [pickerTargetTabId, tabs]);
213482
213953
  const closeTab = reactExports.useCallback(async (tabId) => {
213483
213954
  await window.worker.killPty(tabId);
@@ -213578,6 +214049,7 @@ function App() {
213578
214049
  window.addEventListener("ctlsurf-open-url", onOpen);
213579
214050
  return () => window.removeEventListener("ctlsurf-open-url", onOpen);
213580
214051
  }, [layout2, togglePane]);
214052
+ const activeAgentIsRemote = !!tabs.find((t) => t.id === activeTabId)?.agent?.id?.startsWith("remote-");
213581
214053
  const panes = [
213582
214054
  { id: "editor", label: "Editor", content: /* @__PURE__ */ jsxRuntimeExports.jsx(EditorPanel, { cwd: cwd2 }) },
213583
214055
  {
@@ -213723,7 +214195,7 @@ function App() {
213723
214195
  )
213724
214196
  }
213725
214197
  ),
213726
- /* @__PURE__ */ jsxRuntimeExports.jsx(SpeakControls, {}),
214198
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SpeakControls, { remote: activeAgentIsRemote }),
213727
214199
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
213728
214200
  agents.map((a) => {
213729
214201
  const activeTab = tabs.find((t) => t.id === activeTabId);
@@ -213777,6 +214249,7 @@ function App() {
213777
214249
  }
213778
214250
  }
213779
214251
  ),
214252
+ showProjectPicker && /* @__PURE__ */ jsxRuntimeExports.jsx(ProjectPicker, { onDone: () => setShowProjectPicker(false) }),
213780
214253
  showFloatingMic && /* @__PURE__ */ jsxRuntimeExports.jsx(
213781
214254
  FloatingMic,
213782
214255
  {