@phenx-inc/ctlsurf 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/out/headless/index.mjs +111 -5
  2. package/out/headless/index.mjs.map +3 -3
  3. package/out/main/index.js +357 -28
  4. package/out/preload/index.js +9 -0
  5. package/out/renderer/assets/{cssMode-BbCVOTaJ.js → cssMode-R8fTFLae.js} +3 -3
  6. package/out/renderer/assets/{freemarker2-DryLywQv.js → freemarker2-CyjWzWoP.js} +1 -1
  7. package/out/renderer/assets/{handlebars-CdUMtSdr.js → handlebars-CrhCGIRM.js} +1 -1
  8. package/out/renderer/assets/{html-Cwkf2GMH.js → html-Cc3dPNZr.js} +1 -1
  9. package/out/renderer/assets/{htmlMode-C7PzhXxH.js → htmlMode-BWtG6ywk.js} +3 -3
  10. package/out/renderer/assets/{index-BIceGcz3.css → index-BL9njXIL.css} +206 -46
  11. package/out/renderer/assets/{index-Dg1vMspf.js → index-kkG56aKv.js} +529 -156
  12. package/out/renderer/assets/{javascript-Cy7ROXJl.js → javascript-C8T_SdLC.js} +2 -2
  13. package/out/renderer/assets/{jsonMode-BtZtt3QW.js → jsonMode-D1zR4VVU.js} +3 -3
  14. package/out/renderer/assets/{liquid-xReAQeYs.js → liquid-COSMe9Uf.js} +1 -1
  15. package/out/renderer/assets/{lspLanguageFeatures-Dmx0L0Bw.js → lspLanguageFeatures-CXGfrtzl.js} +1 -1
  16. package/out/renderer/assets/{mdx-YMNFO1Zz.js → mdx-RfS44T-L.js} +1 -1
  17. package/out/renderer/assets/{python-BExaDVxv.js → python-DFSWKHUu.js} +1 -1
  18. package/out/renderer/assets/{razor-HL8ANmQ4.js → razor-DtH2_uvo.js} +1 -1
  19. package/out/renderer/assets/{tsMode-Dqd8vdLE.js → tsMode-BYIQ-S9Z.js} +1 -1
  20. package/out/renderer/assets/{typescript-Dysz1PTX.js → typescript-HhQUr1Rb.js} +1 -1
  21. package/out/renderer/assets/{xml-BHp5Z9nc.js → xml-KuhgdKfD.js} +1 -1
  22. package/out/renderer/assets/{yaml-CPDHxVup.js → yaml-W_kzCFH7.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 +118 -34
  28. package/src/main/orchestrator.ts +105 -4
  29. package/src/main/ssh.ts +348 -0
  30. package/src/preload/index.ts +26 -0
  31. package/src/renderer/App.tsx +29 -7
  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/TicketPanel.tsx +20 -25
  36. package/src/renderer/styles.css +206 -46
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-BbCVOTaJ.js","./lspLanguageFeatures-Dmx0L0Bw.js","./htmlMode-C7PzhXxH.js","./jsonMode-BtZtt3QW.js","./javascript-Cy7ROXJl.js","./typescript-Dysz1PTX.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-R8fTFLae.js","./lspLanguageFeatures-CXGfrtzl.js","./htmlMode-BWtG6ywk.js","./jsonMode-D1zR4VVU.js","./javascript-C8T_SdLC.js","./typescript-HhQUr1Rb.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
  }
@@ -207841,7 +207841,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
207841
207841
  modeConfigurationDefault$2
207842
207842
  );
207843
207843
  function getMode$3() {
207844
- return __vitePreload(() => import("./cssMode-BbCVOTaJ.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
207844
+ return __vitePreload(() => import("./cssMode-R8fTFLae.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
207845
207845
  }
207846
207846
  languages.onLanguage("less", () => {
207847
207847
  getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
@@ -207946,7 +207946,7 @@ const razorLanguageService = registerHTMLLanguageService(
207946
207946
  );
207947
207947
  const razorDefaults = razorLanguageService.defaults;
207948
207948
  function getMode$2() {
207949
- return __vitePreload(() => import("./htmlMode-C7PzhXxH.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
207949
+ return __vitePreload(() => import("./htmlMode-BWtG6ywk.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
207950
207950
  }
207951
207951
  function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
207952
207952
  const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
@@ -208030,7 +208030,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
208030
208030
  );
208031
208031
  const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
208032
208032
  function getMode$1() {
208033
- return __vitePreload(() => import("./jsonMode-BtZtt3QW.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
208033
+ return __vitePreload(() => import("./jsonMode-D1zR4VVU.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
208034
208034
  }
208035
208035
  languages.register({
208036
208036
  id: "json",
@@ -208276,7 +208276,7 @@ const getJavaScriptWorker = () => {
208276
208276
  return getMode().then((mode) => mode.getJavaScriptWorker());
208277
208277
  };
208278
208278
  function getMode() {
208279
- return __vitePreload(() => import("./tsMode-Dqd8vdLE.js"), true ? [] : void 0, import.meta.url);
208279
+ return __vitePreload(() => import("./tsMode-BYIQ-S9Z.js"), true ? [] : void 0, import.meta.url);
208280
208280
  }
208281
208281
  languages.onLanguage("typescript", () => {
208282
208282
  return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
@@ -208471,49 +208471,49 @@ registerLanguage({
208471
208471
  extensions: [".ftl", ".ftlh", ".ftlx"],
208472
208472
  aliases: ["FreeMarker2", "Apache FreeMarker2"],
208473
208473
  loader: () => {
208474
- return __vitePreload(() => import("./freemarker2-DryLywQv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208474
+ return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208475
208475
  }
208476
208476
  });
208477
208477
  registerLanguage({
208478
208478
  id: "freemarker2.tag-angle.interpolation-dollar",
208479
208479
  aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
208480
208480
  loader: () => {
208481
- return __vitePreload(() => import("./freemarker2-DryLywQv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
208481
+ return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
208482
208482
  }
208483
208483
  });
208484
208484
  registerLanguage({
208485
208485
  id: "freemarker2.tag-bracket.interpolation-dollar",
208486
208486
  aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
208487
208487
  loader: () => {
208488
- return __vitePreload(() => import("./freemarker2-DryLywQv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
208488
+ return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
208489
208489
  }
208490
208490
  });
208491
208491
  registerLanguage({
208492
208492
  id: "freemarker2.tag-angle.interpolation-bracket",
208493
208493
  aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
208494
208494
  loader: () => {
208495
- return __vitePreload(() => import("./freemarker2-DryLywQv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
208495
+ return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
208496
208496
  }
208497
208497
  });
208498
208498
  registerLanguage({
208499
208499
  id: "freemarker2.tag-bracket.interpolation-bracket",
208500
208500
  aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
208501
208501
  loader: () => {
208502
- return __vitePreload(() => import("./freemarker2-DryLywQv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
208502
+ return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
208503
208503
  }
208504
208504
  });
208505
208505
  registerLanguage({
208506
208506
  id: "freemarker2.tag-auto.interpolation-dollar",
208507
208507
  aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
208508
208508
  loader: () => {
208509
- return __vitePreload(() => import("./freemarker2-DryLywQv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208509
+ return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208510
208510
  }
208511
208511
  });
208512
208512
  registerLanguage({
208513
208513
  id: "freemarker2.tag-auto.interpolation-bracket",
208514
208514
  aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
208515
208515
  loader: () => {
208516
- return __vitePreload(() => import("./freemarker2-DryLywQv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
208516
+ return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
208517
208517
  }
208518
208518
  });
208519
208519
  registerLanguage({
@@ -208534,7 +208534,7 @@ registerLanguage({
208534
208534
  extensions: [".handlebars", ".hbs"],
208535
208535
  aliases: ["Handlebars", "handlebars", "hbs"],
208536
208536
  mimetypes: ["text/x-handlebars-template"],
208537
- loader: () => __vitePreload(() => import("./handlebars-CdUMtSdr.js"), true ? [] : void 0, import.meta.url)
208537
+ loader: () => __vitePreload(() => import("./handlebars-CrhCGIRM.js"), true ? [] : void 0, import.meta.url)
208538
208538
  });
208539
208539
  registerLanguage({
208540
208540
  id: "hcl",
@@ -208547,7 +208547,7 @@ registerLanguage({
208547
208547
  extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
208548
208548
  aliases: ["HTML", "htm", "html", "xhtml"],
208549
208549
  mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
208550
- loader: () => __vitePreload(() => import("./html-Cwkf2GMH.js"), true ? [] : void 0, import.meta.url)
208550
+ loader: () => __vitePreload(() => import("./html-Cc3dPNZr.js"), true ? [] : void 0, import.meta.url)
208551
208551
  });
208552
208552
  registerLanguage({
208553
208553
  id: "ini",
@@ -208570,7 +208570,7 @@ registerLanguage({
208570
208570
  filenames: ["jakefile"],
208571
208571
  aliases: ["JavaScript", "javascript", "js"],
208572
208572
  mimetypes: ["text/javascript"],
208573
- loader: () => __vitePreload(() => import("./javascript-Cy7ROXJl.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
208573
+ loader: () => __vitePreload(() => import("./javascript-C8T_SdLC.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
208574
208574
  });
208575
208575
  registerLanguage({
208576
208576
  id: "julia",
@@ -208609,7 +208609,7 @@ registerLanguage({
208609
208609
  extensions: [".liquid", ".html.liquid"],
208610
208610
  aliases: ["Liquid", "liquid"],
208611
208611
  mimetypes: ["application/liquid"],
208612
- loader: () => __vitePreload(() => import("./liquid-xReAQeYs.js"), true ? [] : void 0, import.meta.url)
208612
+ loader: () => __vitePreload(() => import("./liquid-COSMe9Uf.js"), true ? [] : void 0, import.meta.url)
208613
208613
  });
208614
208614
  registerLanguage({
208615
208615
  id: "m3",
@@ -208627,7 +208627,7 @@ registerLanguage({
208627
208627
  id: "mdx",
208628
208628
  extensions: [".mdx"],
208629
208629
  aliases: ["MDX", "mdx"],
208630
- loader: () => __vitePreload(() => import("./mdx-YMNFO1Zz.js"), true ? [] : void 0, import.meta.url)
208630
+ loader: () => __vitePreload(() => import("./mdx-RfS44T-L.js"), true ? [] : void 0, import.meta.url)
208631
208631
  });
208632
208632
  registerLanguage({
208633
208633
  id: "mips",
@@ -208726,7 +208726,7 @@ registerLanguage({
208726
208726
  extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
208727
208727
  aliases: ["Python", "py"],
208728
208728
  firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
208729
- loader: () => __vitePreload(() => import("./python-BExaDVxv.js"), true ? [] : void 0, import.meta.url)
208729
+ loader: () => __vitePreload(() => import("./python-DFSWKHUu.js"), true ? [] : void 0, import.meta.url)
208730
208730
  });
208731
208731
  registerLanguage({
208732
208732
  id: "qsharp",
@@ -208745,7 +208745,7 @@ registerLanguage({
208745
208745
  extensions: [".cshtml"],
208746
208746
  aliases: ["Razor", "razor"],
208747
208747
  mimetypes: ["text/x-cshtml"],
208748
- loader: () => __vitePreload(() => import("./razor-HL8ANmQ4.js"), true ? [] : void 0, import.meta.url)
208748
+ loader: () => __vitePreload(() => import("./razor-DtH2_uvo.js"), true ? [] : void 0, import.meta.url)
208749
208749
  });
208750
208750
  registerLanguage({
208751
208751
  id: "redis",
@@ -208878,7 +208878,7 @@ registerLanguage({
208878
208878
  aliases: ["TypeScript", "ts", "typescript"],
208879
208879
  mimetypes: ["text/typescript"],
208880
208880
  loader: () => {
208881
- return __vitePreload(() => import("./typescript-Dysz1PTX.js"), true ? [] : void 0, import.meta.url);
208881
+ return __vitePreload(() => import("./typescript-HhQUr1Rb.js"), true ? [] : void 0, import.meta.url);
208882
208882
  }
208883
208883
  });
208884
208884
  registerLanguage({
@@ -208923,14 +208923,14 @@ registerLanguage({
208923
208923
  firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
208924
208924
  aliases: ["XML", "xml"],
208925
208925
  mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
208926
- loader: () => __vitePreload(() => import("./xml-BHp5Z9nc.js"), true ? [] : void 0, import.meta.url)
208926
+ loader: () => __vitePreload(() => import("./xml-KuhgdKfD.js"), true ? [] : void 0, import.meta.url)
208927
208927
  });
208928
208928
  registerLanguage({
208929
208929
  id: "yaml",
208930
208930
  extensions: [".yaml", ".yml"],
208931
208931
  aliases: ["YAML", "yaml", "YML", "yml"],
208932
208932
  mimetypes: ["application/x-yaml", "text/x-yaml"],
208933
- loader: () => __vitePreload(() => import("./yaml-CPDHxVup.js"), true ? [] : void 0, import.meta.url)
208933
+ loader: () => __vitePreload(() => import("./yaml-W_kzCFH7.js"), true ? [] : void 0, import.meta.url)
208934
208934
  });
208935
208935
  var __defProp = Object.defineProperty;
208936
208936
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -212419,10 +212419,276 @@ function EditorPanel({ cwd: cwd2 }) {
212419
212419
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "editor-placeholder", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Select a file to edit" }) }) })
212420
212420
  ] });
212421
212421
  }
212422
+ const TMUX_PREFIX = "ctlsurf_";
212423
+ function nextSessionName(sessions) {
212424
+ const used = new Set(
212425
+ sessions.filter((s) => s.startsWith(TMUX_PREFIX)).map((s) => parseInt(s.slice(TMUX_PREFIX.length), 10)).filter((n22) => Number.isInteger(n22) && n22 > 0)
212426
+ );
212427
+ let n2 = 1;
212428
+ while (used.has(n2)) n2++;
212429
+ return `${TMUX_PREFIX}${n2}`;
212430
+ }
212431
+ function RemoteConnect({ onConnect, onBack }) {
212432
+ const [step, setStep] = reactExports.useState("hosts");
212433
+ const [configHosts, setConfigHosts] = reactExports.useState([]);
212434
+ const [recents, setRecents] = reactExports.useState([]);
212435
+ const [manual, setManual] = reactExports.useState("");
212436
+ const [agentCmd, setAgentCmd] = reactExports.useState("claude");
212437
+ const [target, setTarget] = reactExports.useState("");
212438
+ const [probe, setProbe] = reactExports.useState(null);
212439
+ const [errorMsg, setErrorMsg] = reactExports.useState("");
212440
+ const [password, setPassword] = reactExports.useState("");
212441
+ reactExports.useEffect(() => {
212442
+ let cancelled = false;
212443
+ window.worker.sshListHosts().then((r) => {
212444
+ if (cancelled) return;
212445
+ setConfigHosts(r.configHosts || []);
212446
+ setRecents(r.recents || []);
212447
+ }).catch(() => {
212448
+ });
212449
+ return () => {
212450
+ cancelled = true;
212451
+ };
212452
+ }, []);
212453
+ const startProbe = async (t) => {
212454
+ const dest = t.trim();
212455
+ if (!dest) return;
212456
+ setTarget(dest);
212457
+ setStep("probing");
212458
+ try {
212459
+ const result = await window.worker.sshProbe(dest);
212460
+ setProbe(result);
212461
+ if (result.reachable || result.authRequired) {
212462
+ setStep("action");
212463
+ } else {
212464
+ setErrorMsg(result.error || "Could not connect");
212465
+ setStep("error");
212466
+ }
212467
+ } catch (err) {
212468
+ setErrorMsg(err?.message || "Probe failed");
212469
+ setStep("error");
212470
+ }
212471
+ };
212472
+ const connect = async (action) => {
212473
+ try {
212474
+ const agent = await window.worker.sshBuildAgent(target, action, agentCmd);
212475
+ if (password) {
212476
+ await window.worker.sshSetPassword(target, password).catch(() => {
212477
+ });
212478
+ }
212479
+ setPassword("");
212480
+ window.worker.sshAddRecent(target).catch(() => {
212481
+ });
212482
+ onConnect(agent);
212483
+ } catch (err) {
212484
+ setErrorMsg(err?.message || "Failed to build connection");
212485
+ setStep("error");
212486
+ }
212487
+ };
212488
+ const passwordField = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-pw", children: [
212489
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
212490
+ "input",
212491
+ {
212492
+ className: "remote-input",
212493
+ type: "password",
212494
+ placeholder: "Password (optional — blank prompts in terminal)",
212495
+ value: password,
212496
+ onChange: (e) => setPassword(e.target.value),
212497
+ autoCapitalize: "off",
212498
+ autoCorrect: "off",
212499
+ spellCheck: false
212500
+ }
212501
+ ),
212502
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-pw-hint", children: "Used once for this connection — never stored." })
212503
+ ] });
212504
+ const agentLabel = agentCmd === "claude" ? "Claude Code" : "shell";
212505
+ const agentToggle = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-toggle", children: [
212506
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "remote-toggle-label", children: "Run:" }),
212507
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
212508
+ "button",
212509
+ {
212510
+ className: `remote-toggle-btn${agentCmd === "claude" ? " is-active" : ""}`,
212511
+ onClick: () => setAgentCmd("claude"),
212512
+ children: "Claude Code"
212513
+ }
212514
+ ),
212515
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
212516
+ "button",
212517
+ {
212518
+ className: `remote-toggle-btn${agentCmd === "" ? " is-active" : ""}`,
212519
+ onClick: () => setAgentCmd(""),
212520
+ children: "Shell"
212521
+ }
212522
+ )
212523
+ ] });
212524
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
212525
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
212526
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: "CONNECT TO REMOTE" }),
212527
+ step === "hosts" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212528
+ agentToggle,
212529
+ recents.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212530
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "RECENT" }),
212531
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-list", children: recents.map((r) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "agent-picker-item", onClick: () => startProbe(r), children: [
212532
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: r }),
212533
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "reconnect" })
212534
+ ] }, r)) })
212535
+ ] }),
212536
+ configHosts.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212537
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "~/.ssh/config" }),
212538
+ /* @__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: [
212539
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: h2.alias }),
212540
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "agent-picker-desc", children: [
212541
+ h2.user ? `${h2.user}@` : "",
212542
+ h2.hostName || ""
212543
+ ] })
212544
+ ] }, h2.alias)) })
212545
+ ] }),
212546
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "OR TYPE A HOST" }),
212547
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212548
+ "form",
212549
+ {
212550
+ className: "remote-manual",
212551
+ onSubmit: (e) => {
212552
+ e.preventDefault();
212553
+ startProbe(manual);
212554
+ },
212555
+ children: [
212556
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
212557
+ "input",
212558
+ {
212559
+ className: "remote-input",
212560
+ type: "text",
212561
+ placeholder: "user@host",
212562
+ value: manual,
212563
+ onChange: (e) => setManual(e.target.value),
212564
+ autoCapitalize: "off",
212565
+ autoCorrect: "off",
212566
+ spellCheck: false
212567
+ }
212568
+ ),
212569
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-btn", type: "submit", disabled: !manual.trim(), children: "Connect" })
212570
+ ]
212571
+ }
212572
+ ),
212573
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", onClick: onBack, children: "← back to agents" })
212574
+ ] }),
212575
+ step === "probing" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-status", children: [
212576
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-spinner" }),
212577
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
212578
+ "Connecting to ",
212579
+ /* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: target }),
212580
+ "…"
212581
+ ] }),
212582
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-status-sub", children: "Checking for tmux sessions" })
212583
+ ] }),
212584
+ step === "action" && probe && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212585
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-target-line", children: [
212586
+ /* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: target }),
212587
+ " · run ",
212588
+ agentLabel
212589
+ ] }),
212590
+ probe.authRequired && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212591
+ /* @__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." }),
212592
+ passwordField
212593
+ ] }),
212594
+ probe.reachable && probe.sessions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212595
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "REATTACH" }),
212596
+ /* @__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(
212597
+ "button",
212598
+ {
212599
+ className: "agent-picker-item",
212600
+ onClick: () => connect({ kind: "attach", session: s }),
212601
+ children: [
212602
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: s }),
212603
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "attach running tmux session" })
212604
+ ]
212605
+ },
212606
+ s
212607
+ )) })
212608
+ ] }),
212609
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "START" }),
212610
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-list", children: [
212611
+ (probe.reachable && probe.tmuxInstalled || probe.authRequired) && /* @__PURE__ */ jsxRuntimeExports.jsxs(
212612
+ "button",
212613
+ {
212614
+ className: "agent-picker-item",
212615
+ onClick: () => {
212616
+ const session = probe.reachable ? nextSessionName(probe.sessions) : `${TMUX_PREFIX}1`;
212617
+ connect(probe.reachable ? { kind: "new-tmux", session } : { kind: "auto", session });
212618
+ },
212619
+ children: [
212620
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "agent-picker-name", children: [
212621
+ "New tmux session (",
212622
+ probe.reachable ? nextSessionName(probe.sessions) : `${TMUX_PREFIX}1`,
212623
+ ")"
212624
+ ] }),
212625
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "persists across disconnects (recommended)" })
212626
+ ]
212627
+ }
212628
+ ),
212629
+ probe.reachable && !probe.tmuxInstalled && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: "tmux isn't installed on this host — running without it." }),
212630
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212631
+ "button",
212632
+ {
212633
+ className: "agent-picker-item",
212634
+ onClick: () => connect({ kind: "no-tmux" }),
212635
+ children: [
212636
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "Run without tmux" }),
212637
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "agent-picker-desc", children: [
212638
+ agentLabel,
212639
+ " directly — won't survive a dropped connection"
212640
+ ] })
212641
+ ]
212642
+ }
212643
+ )
212644
+ ] }),
212645
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", onClick: () => setStep("hosts"), children: "← choose another host" })
212646
+ ] }),
212647
+ step === "error" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212648
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-error", children: [
212649
+ "Couldn't connect to ",
212650
+ /* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: target })
212651
+ ] }),
212652
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-status-sub", children: errorMsg }),
212653
+ passwordField,
212654
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-section-label", children: "TRY ANYWAY (INTERACTIVE)" }),
212655
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-list", children: [
212656
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212657
+ "button",
212658
+ {
212659
+ className: "agent-picker-item",
212660
+ onClick: () => connect({ kind: "auto", session: `${TMUX_PREFIX}1` }),
212661
+ children: [
212662
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "Connect with tmux" }),
212663
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "ssh will prompt if a password / key passphrase is needed" })
212664
+ ]
212665
+ }
212666
+ ),
212667
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212668
+ "button",
212669
+ {
212670
+ className: "agent-picker-item",
212671
+ onClick: () => connect({ kind: "no-tmux" }),
212672
+ children: [
212673
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "Connect without tmux" }),
212674
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "agent-picker-desc", children: [
212675
+ "run ",
212676
+ agentLabel,
212677
+ " directly"
212678
+ ] })
212679
+ ]
212680
+ }
212681
+ )
212682
+ ] }),
212683
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", onClick: () => setStep("hosts"), children: "← choose another host" })
212684
+ ] })
212685
+ ] }) });
212686
+ }
212422
212687
  function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
212423
212688
  const displayPath = cwd2.replace(/^\/Users\/[^/]+/, "~");
212424
212689
  const [logChat, setLogChat] = reactExports.useState(false);
212425
212690
  const [loaded, setLoaded] = reactExports.useState(false);
212691
+ const [remoteMode, setRemoteMode] = reactExports.useState(false);
212426
212692
  reactExports.useEffect(() => {
212427
212693
  let cancelled = false;
212428
212694
  window.worker.getLogChat().then((r) => {
@@ -212451,6 +212717,17 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
212451
212717
  }
212452
212718
  onSelect(agent);
212453
212719
  };
212720
+ if (remoteMode) {
212721
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
212722
+ RemoteConnect,
212723
+ {
212724
+ onConnect: (agent) => {
212725
+ handleSelect(agent);
212726
+ },
212727
+ onBack: () => setRemoteMode(false)
212728
+ }
212729
+ );
212730
+ }
212454
212731
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
212455
212732
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
212456
212733
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-cwd", onClick: onChangeCwd, title: "Click to change directory", children: [
@@ -212459,18 +212736,31 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
212459
212736
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-cwd-change", children: "change" })
212460
212737
  ] }),
212461
212738
  /* @__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
- )) }),
212739
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-list", children: [
212740
+ agents.map((a) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
212741
+ "button",
212742
+ {
212743
+ className: "agent-picker-item",
212744
+ onClick: () => handleSelect(a),
212745
+ children: [
212746
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: a.name }),
212747
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: a.description })
212748
+ ]
212749
+ },
212750
+ a.id
212751
+ )),
212752
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
212753
+ "button",
212754
+ {
212755
+ className: "agent-picker-item",
212756
+ onClick: () => setRemoteMode(true),
212757
+ children: [
212758
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "🌐 Remote (SSH)" }),
212759
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: "Run an agent on a remote machine" })
212760
+ ]
212761
+ }
212762
+ )
212763
+ ] }),
212474
212764
  /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "agent-picker-option", onClick: (e) => e.stopPropagation(), children: [
212475
212765
  /* @__PURE__ */ jsxRuntimeExports.jsx(
212476
212766
  "input",
@@ -212485,6 +212775,84 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
212485
212775
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-hint", children: "Select an agent to start" })
212486
212776
  ] }) });
212487
212777
  }
212778
+ function ProjectPicker({ onDone }) {
212779
+ const [state, setState2] = reactExports.useState("loading");
212780
+ const [projects, setProjects] = reactExports.useState([]);
212781
+ const [errorMsg, setErrorMsg] = reactExports.useState("");
212782
+ const [binding, setBinding] = reactExports.useState(false);
212783
+ const [filter, setFilter] = reactExports.useState("");
212784
+ reactExports.useEffect(() => {
212785
+ let cancelled = false;
212786
+ window.worker.listProjects().then((r) => {
212787
+ if (cancelled) return;
212788
+ if (r.ok) {
212789
+ setProjects(r.projects || []);
212790
+ setState2("ready");
212791
+ } else {
212792
+ setErrorMsg(r.error || "Could not load projects");
212793
+ setState2("error");
212794
+ }
212795
+ }).catch((err) => {
212796
+ if (cancelled) return;
212797
+ setErrorMsg(err?.message || "Could not load projects");
212798
+ setState2("error");
212799
+ });
212800
+ return () => {
212801
+ cancelled = true;
212802
+ };
212803
+ }, []);
212804
+ const pick = async (p) => {
212805
+ setBinding(true);
212806
+ try {
212807
+ await window.worker.bindProject(p.root_path);
212808
+ } catch {
212809
+ }
212810
+ onDone();
212811
+ };
212812
+ const shown = filter.trim() ? projects.filter((p) => p.name.toLowerCase().includes(filter.toLowerCase()) || p.root_path.toLowerCase().includes(filter.toLowerCase())) : projects;
212813
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
212814
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
212815
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: "SELECT CTLSURF PROJECT" }),
212816
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-target-line", children: "Bind this remote session to a project" }),
212817
+ state === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-status", children: [
212818
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-spinner" }),
212819
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading projects…" })
212820
+ ] }),
212821
+ state === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: errorMsg }) }),
212822
+ state === "ready" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
212823
+ projects.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: "No ctlsurf projects found. You can skip and bind one later." }),
212824
+ projects.length > 6 && /* @__PURE__ */ jsxRuntimeExports.jsx(
212825
+ "input",
212826
+ {
212827
+ className: "remote-input",
212828
+ style: { width: "100%", boxSizing: "border-box", margin: "4px 0 8px" },
212829
+ type: "text",
212830
+ placeholder: "Filter projects…",
212831
+ value: filter,
212832
+ onChange: (e) => setFilter(e.target.value),
212833
+ autoCapitalize: "off",
212834
+ autoCorrect: "off",
212835
+ spellCheck: false
212836
+ }
212837
+ ),
212838
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-list remote-host-scroll", children: shown.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
212839
+ "button",
212840
+ {
212841
+ className: "agent-picker-item project-item",
212842
+ disabled: binding,
212843
+ onClick: () => pick(p),
212844
+ title: p.root_path,
212845
+ children: [
212846
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: p.name }),
212847
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: p.root_path })
212848
+ ]
212849
+ },
212850
+ p.id
212851
+ )) })
212852
+ ] }),
212853
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", disabled: binding, onClick: onDone, children: "Skip — no project" })
212854
+ ] }) });
212855
+ }
212488
212856
  function removePane(node, paneId) {
212489
212857
  if (node.type === "leaf") {
212490
212858
  return node.paneId === paneId ? null : node;
@@ -213094,7 +213462,7 @@ function Segmented({ options, value, onChange }) {
213094
213462
  );
213095
213463
  }) });
213096
213464
  }
213097
- function TicketPanel({ open, onClose }) {
213465
+ function TicketPane() {
213098
213466
  const [project, setProject] = reactExports.useState(null);
213099
213467
  const [view, setView] = reactExports.useState("list");
213100
213468
  const [tickets, setTickets] = reactExports.useState([]);
@@ -213124,19 +213492,23 @@ function TicketPanel({ open, onClose }) {
213124
213492
  }
213125
213493
  }, []);
213126
213494
  reactExports.useEffect(() => {
213127
- if (!open) return;
213128
- setView("list");
213129
213495
  let cancelled = false;
213130
- window.worker.getTicketProject().then((r) => {
213496
+ const loadProject = () => window.worker.getTicketProject().then((r) => {
213131
213497
  if (!cancelled) setProject(r?.name ?? null);
213132
213498
  }).catch(() => {
213133
213499
  if (!cancelled) setProject(null);
213134
213500
  });
213501
+ loadProject();
213135
213502
  void refreshList();
213503
+ const unsub = window.worker.onCwdChanged(() => {
213504
+ loadProject();
213505
+ void refreshList();
213506
+ });
213136
213507
  return () => {
213137
213508
  cancelled = true;
213509
+ unsub();
213138
213510
  };
213139
- }, [open, refreshList]);
213511
+ }, [refreshList]);
213140
213512
  const openNewForm = reactExports.useCallback(() => {
213141
213513
  setEditingId(null);
213142
213514
  setTitle("");
@@ -213172,129 +213544,122 @@ function TicketPanel({ open, onClose }) {
213172
213544
  }
213173
213545
  }, [title, description, status2, priority, editingId, save.kind, refreshList]);
213174
213546
  reactExports.useEffect(() => {
213175
- if (!open) return;
213547
+ if (view !== "form") return;
213176
213548
  const onKey = (e) => {
213177
213549
  if (e.key === "Escape") {
213178
213550
  e.preventDefault();
213179
- if (view === "form") setView("list");
213180
- else onClose();
213181
- } else if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && view === "form") {
213551
+ setView("list");
213552
+ } else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
213182
213553
  e.preventDefault();
213183
213554
  void handleSave();
213184
213555
  }
213185
213556
  };
213186
213557
  window.addEventListener("keydown", onKey);
213187
213558
  return () => window.removeEventListener("keydown", onKey);
213188
- }, [open, view, onClose, handleSave]);
213189
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213190
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `ticket-scrim ${open ? "open" : ""}`, onClick: onClose }),
213191
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `ticket-drawer ${open ? "open" : ""}`, role: "dialog", "aria-label": "Tickets", children: [
213192
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-header", children: [
213193
- /* @__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
- ] })
213559
+ }, [view, handleSave]);
213560
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-pane", role: "region", "aria-label": "Tickets", children: [
213561
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-drawer-header", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-heading", children: [
213562
+ 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 }) }),
213563
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-headtext", children: [
213564
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-title", children: view === "form" ? editingId ? "Edit ticket" : "New ticket" : "Tickets" }),
213565
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-subtitle", children: project ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213566
+ "in ",
213567
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: project })
213568
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-warn", children: "no ctlsurf project for this tab" }) })
213569
+ ] })
213570
+ ] }) }),
213571
+ view === "list" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213572
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-list-body", children: [
213573
+ listLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-list-msg", children: "Loading tickets…" }),
213574
+ !listLoading && listError && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-error", children: [
213575
+ "⚠ ",
213576
+ listError
213202
213577
  ] }),
213203
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-drawer-close", onClick: onClose, title: "Close", children: "×" })
213204
- ] }),
213205
- view === "list" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213206
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-list-body", children: [
213207
- listLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-list-msg", children: "Loading tickets…" }),
213208
- !listLoading && listError && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-error", children: [
213209
- "⚠ ",
213210
- listError
213211
- ] }),
213212
- !listLoading && !listError && tickets.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-empty", children: [
213213
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, { size: 26 }) }),
213214
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-title", children: "No tickets yet" }),
213215
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-hint", children: "Log the first one for this project." })
213216
- ] }),
213217
- !listLoading && tickets.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ticket-card", onClick: () => openEditForm(t), children: [
213218
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-bar", style: { background: colorOf(STATUS_OPTIONS, t.status) } }),
213219
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-main", children: [
213220
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-title", children: t.title || "Untitled" }),
213221
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-meta", children: [
213222
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ticket-card-status", style: { color: colorOf(STATUS_OPTIONS, t.status) }, children: [
213223
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-seg-dot", style: { background: colorOf(STATUS_OPTIONS, t.status) } }),
213224
- t.status
213225
- ] }),
213226
- t.created && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-date", children: formatDate(t.created) })
213227
- ] })
213228
- ] }),
213229
- /* @__PURE__ */ jsxRuntimeExports.jsx(
213230
- "span",
213231
- {
213232
- className: "ticket-card-pri",
213233
- style: { color: colorOf(PRIORITY_OPTIONS, t.priority), borderColor: `${colorOf(PRIORITY_OPTIONS, t.priority)}66` },
213234
- children: t.priority
213235
- }
213236
- )
213237
- ] }, t.id))
213578
+ !listLoading && !listError && tickets.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-empty", children: [
213579
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, { size: 26 }) }),
213580
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-title", children: "No tickets yet" }),
213581
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-hint", children: "Log the first one for this project." })
213238
213582
  ] }),
213239
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-footer", children: [
213240
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-hint", children: tickets.length > 0 && `${tickets.length} ticket${tickets.length === 1 ? "" : "s"}` }),
213241
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-btn-primary", onClick: openNewForm, children: "+ New ticket" })
213242
- ] })
213243
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213244
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-body", children: [
213245
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "ticket-field", children: [
213246
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-field-label", children: "Title" }),
213247
- /* @__PURE__ */ jsxRuntimeExports.jsx(
213248
- "input",
213249
- {
213250
- type: "text",
213251
- className: "ticket-input-title",
213252
- value: title,
213253
- autoFocus: true,
213254
- placeholder: "What needs doing?",
213255
- onChange: (e) => setTitle(e.target.value)
213256
- }
213257
- )
213258
- ] }),
213259
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "ticket-field", children: [
213260
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-field-label", children: "Description" }),
213261
- /* @__PURE__ */ jsxRuntimeExports.jsx(
213262
- "textarea",
213263
- {
213264
- value: description,
213265
- rows: 5,
213266
- placeholder: "Add detail, context, links…",
213267
- onChange: (e) => setDescription(e.target.value)
213268
- }
213269
- )
213270
- ] }),
213271
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-field", children: [
213272
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-field-label", children: "Status" }),
213273
- /* @__PURE__ */ jsxRuntimeExports.jsx(Segmented, { options: STATUS_OPTIONS, value: status2, onChange: setStatus })
213274
- ] }),
213275
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-field", children: [
213276
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-field-label", children: "Priority" }),
213277
- /* @__PURE__ */ jsxRuntimeExports.jsx(Segmented, { options: PRIORITY_OPTIONS, value: priority, onChange: setPriority })
213583
+ !listLoading && tickets.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ticket-card", onClick: () => openEditForm(t), children: [
213584
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-bar", style: { background: colorOf(STATUS_OPTIONS, t.status) } }),
213585
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-main", children: [
213586
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-title", children: t.title || "Untitled" }),
213587
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-meta", children: [
213588
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ticket-card-status", style: { color: colorOf(STATUS_OPTIONS, t.status) }, children: [
213589
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-seg-dot", style: { background: colorOf(STATUS_OPTIONS, t.status) } }),
213590
+ t.status
213591
+ ] }),
213592
+ t.created && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-date", children: formatDate(t.created) })
213593
+ ] })
213278
213594
  ] }),
213279
- save.kind === "error" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-error", children: [
213280
- "⚠ ",
213281
- save.message
213282
- ] })
213595
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
213596
+ "span",
213597
+ {
213598
+ className: "ticket-card-pri",
213599
+ style: { color: colorOf(PRIORITY_OPTIONS, t.priority), borderColor: `${colorOf(PRIORITY_OPTIONS, t.priority)}66` },
213600
+ children: t.priority
213601
+ }
213602
+ )
213603
+ ] }, t.id))
213604
+ ] }),
213605
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-footer", children: [
213606
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-hint", children: tickets.length > 0 && `${tickets.length} ticket${tickets.length === 1 ? "" : "s"}` }),
213607
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-btn-primary", onClick: openNewForm, children: "+ New ticket" })
213608
+ ] })
213609
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213610
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-body", children: [
213611
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "ticket-field", children: [
213612
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-field-label", children: "Title" }),
213613
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
213614
+ "input",
213615
+ {
213616
+ type: "text",
213617
+ className: "ticket-input-title",
213618
+ value: title,
213619
+ autoFocus: true,
213620
+ placeholder: "What needs doing?",
213621
+ onChange: (e) => setTitle(e.target.value)
213622
+ }
213623
+ )
213283
213624
  ] }),
213284
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-footer", children: [
213285
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-hint", children: "⌘↵ to save" }),
213286
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-footer-btns", children: [
213287
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-btn-secondary", onClick: () => setView("list"), children: "Cancel" }),
213288
- /* @__PURE__ */ jsxRuntimeExports.jsx(
213289
- "button",
213290
- {
213291
- className: "ticket-btn-primary",
213292
- onClick: handleSave,
213293
- disabled: !title.trim() || save.kind === "saving",
213294
- children: save.kind === "saving" ? "Saving…" : editingId ? "Save changes" : "Save ticket"
213295
- }
213296
- )
213297
- ] })
213625
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "ticket-field", children: [
213626
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-field-label", children: "Description" }),
213627
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
213628
+ "textarea",
213629
+ {
213630
+ value: description,
213631
+ rows: 5,
213632
+ placeholder: "Add detail, context, links…",
213633
+ onChange: (e) => setDescription(e.target.value)
213634
+ }
213635
+ )
213636
+ ] }),
213637
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-field", children: [
213638
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-field-label", children: "Status" }),
213639
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Segmented, { options: STATUS_OPTIONS, value: status2, onChange: setStatus })
213640
+ ] }),
213641
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-field", children: [
213642
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-field-label", children: "Priority" }),
213643
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Segmented, { options: PRIORITY_OPTIONS, value: priority, onChange: setPriority })
213644
+ ] }),
213645
+ save.kind === "error" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-error", children: [
213646
+ "⚠ ",
213647
+ save.message
213648
+ ] })
213649
+ ] }),
213650
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-footer", children: [
213651
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-hint", children: "⌘↵ to save" }),
213652
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-footer-btns", children: [
213653
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-btn-secondary", onClick: () => setView("list"), children: "Cancel" }),
213654
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
213655
+ "button",
213656
+ {
213657
+ className: "ticket-btn-primary",
213658
+ onClick: handleSave,
213659
+ disabled: !title.trim() || save.kind === "saving",
213660
+ children: save.kind === "saving" ? "Saving…" : editingId ? "Save changes" : "Save ticket"
213661
+ }
213662
+ )
213298
213663
  ] })
213299
213664
  ] })
213300
213665
  ] })
@@ -213305,7 +213670,7 @@ const DEFAULT_LAYOUT = {
213305
213670
  direction: "horizontal",
213306
213671
  children: [
213307
213672
  { type: "leaf", paneId: "terminal" },
213308
- { type: "leaf", paneId: "ctlsurf" }
213673
+ { type: "leaf", paneId: "tickets" }
213309
213674
  ],
213310
213675
  sizes: [50, 50]
213311
213676
  };
@@ -213333,7 +213698,6 @@ function App() {
213333
213698
  const [activeTabId, setActiveTabId] = reactExports.useState(tabs[0].id);
213334
213699
  const [trackingActive, setTrackingActive] = reactExports.useState(false);
213335
213700
  const [agentStates, setAgentStates] = reactExports.useState({});
213336
- const [showTicketPanel, setShowTicketPanel] = reactExports.useState(false);
213337
213701
  const [showFloatingMic, setShowFloatingMic] = reactExports.useState(() => {
213338
213702
  try {
213339
213703
  return localStorage.getItem("ctlsurf.floatingMicVisible") !== "false";
@@ -213350,6 +213714,7 @@ function App() {
213350
213714
  }, []);
213351
213715
  const [pickerTargetTabId, setPickerTargetTabId] = reactExports.useState(tabs[0].id);
213352
213716
  const [showAgentPicker, setShowAgentPicker] = reactExports.useState(true);
213717
+ const [showProjectPicker, setShowProjectPicker] = reactExports.useState(false);
213353
213718
  const visiblePaneIds = findPaneIds(layout2);
213354
213719
  reactExports.useEffect(() => {
213355
213720
  async function init2() {
@@ -213482,6 +213847,9 @@ function App() {
213482
213847
  }
213483
213848
  setShowAgentPicker(false);
213484
213849
  setPickerTargetTabId(null);
213850
+ if (agent.id.startsWith("remote-")) {
213851
+ setShowProjectPicker(true);
213852
+ }
213485
213853
  }, [pickerTargetTabId, tabs]);
213486
213854
  const closeTab = reactExports.useCallback(async (tabId) => {
213487
213855
  await window.worker.killPty(tabId);
@@ -213561,6 +213929,10 @@ function App() {
213561
213929
  e.preventDefault();
213562
213930
  togglePane("ctlsurf");
213563
213931
  break;
213932
+ case "K":
213933
+ e.preventDefault();
213934
+ togglePane("tickets");
213935
+ break;
213564
213936
  }
213565
213937
  }
213566
213938
  };
@@ -213636,7 +214008,8 @@ function App() {
213636
214008
  )) })
213637
214009
  ] })
213638
214010
  },
213639
- { id: "ctlsurf", label: "ctlsurf", content: /* @__PURE__ */ jsxRuntimeExports.jsx(CtlsurfPanel, { navigate: ctlsurfNav }) }
214011
+ { id: "ctlsurf", label: "ctlsurf", content: /* @__PURE__ */ jsxRuntimeExports.jsx(CtlsurfPanel, { navigate: ctlsurfNav }) },
214012
+ { id: "tickets", label: "Tickets", content: /* @__PURE__ */ jsxRuntimeExports.jsx(TicketPane, {}) }
213640
214013
  ];
213641
214014
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "app", children: [
213642
214015
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "titlebar", children: [
@@ -213665,9 +214038,9 @@ function App() {
213665
214038
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
213666
214039
  "button",
213667
214040
  {
213668
- className: `titlebar-btn titlebar-icon-btn ${showTicketPanel ? "active" : ""}`,
213669
- onClick: () => setShowTicketPanel((v2) => !v2),
213670
- title: "Tickets for the active project",
214041
+ className: `titlebar-btn titlebar-icon-btn ${visiblePaneIds.includes("tickets") ? "active" : ""}`,
214042
+ onClick: () => togglePane("tickets"),
214043
+ title: "Toggle tickets pane for the active project",
213671
214044
  "aria-label": "Tickets",
213672
214045
  children: [
213673
214046
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -213761,7 +214134,6 @@ function App() {
213761
214134
  ) }),
213762
214135
  /* @__PURE__ */ jsxRuntimeExports.jsx(StatusBar, { wsStatus, cwd: cwd2, onChangeCwd: handleChangeCwd, updateInfo }),
213763
214136
  /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, { open: showSettings, onClose: () => setShowSettings(false) }),
213764
- /* @__PURE__ */ jsxRuntimeExports.jsx(TicketPanel, { open: showTicketPanel, onClose: () => setShowTicketPanel(false) }),
213765
214137
  showAgentPicker && agents.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
213766
214138
  AgentPicker,
213767
214139
  {
@@ -213777,6 +214149,7 @@ function App() {
213777
214149
  }
213778
214150
  }
213779
214151
  ),
214152
+ showProjectPicker && /* @__PURE__ */ jsxRuntimeExports.jsx(ProjectPicker, { onDone: () => setShowProjectPicker(false) }),
213780
214153
  showFloatingMic && /* @__PURE__ */ jsxRuntimeExports.jsx(
213781
214154
  FloatingMic,
213782
214155
  {