@phenx-inc/ctlsurf 0.1.21 → 0.3.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 (35) hide show
  1. package/out/headless/index.mjs +409 -99
  2. package/out/headless/index.mjs.map +4 -4
  3. package/out/main/index.js +419 -77
  4. package/out/preload/index.js +12 -8
  5. package/out/renderer/assets/{cssMode-C6bY9C4O.js → cssMode-DiOmyihM.js} +3 -3
  6. package/out/renderer/assets/{freemarker2-CkAJiX1K.js → freemarker2-BAfv60yb.js} +1 -1
  7. package/out/renderer/assets/{handlebars-DnLXVUXp.js → handlebars-Ult17NzQ.js} +1 -1
  8. package/out/renderer/assets/{html-Ds5-qvDh.js → html-DCxh4J-1.js} +1 -1
  9. package/out/renderer/assets/{htmlMode-DYFYy4MK.js → htmlMode-CQ5Xenrg.js} +3 -3
  10. package/out/renderer/assets/{index-DwSsD_Xm.js → index-BnCJ1IaZ.js} +308 -101
  11. package/out/renderer/assets/{index-DK9wLFFm.css → index-CrTu3Z4M.css} +132 -0
  12. package/out/renderer/assets/{javascript-CiHhG2a9.js → javascript-U5dsRcHx.js} +2 -2
  13. package/out/renderer/assets/{jsonMode-DdDRlbXP.js → jsonMode-DshPNyVy.js} +3 -3
  14. package/out/renderer/assets/{liquid-BP5mb-uD.js → liquid-jHHLYTlB.js} +1 -1
  15. package/out/renderer/assets/{lspLanguageFeatures-Dljhj5Gh.js → lspLanguageFeatures-CUafmPGy.js} +1 -1
  16. package/out/renderer/assets/{mdx-D4u3N7dt.js → mdx-Ct-tiY6g.js} +1 -1
  17. package/out/renderer/assets/{python-BQDHXVwp.js → python-wD3UwKPV.js} +1 -1
  18. package/out/renderer/assets/{razor-BfXW9cDc.js → razor-11ECS4oH.js} +1 -1
  19. package/out/renderer/assets/{tsMode-BGTjG8Ow.js → tsMode-D-7JexQ_.js} +1 -1
  20. package/out/renderer/assets/{typescript-422MU_YO.js → typescript-Cvna1mak.js} +1 -1
  21. package/out/renderer/assets/{xml-B6EKhHiy.js → xml-JsEaImjA.js} +1 -1
  22. package/out/renderer/assets/{yaml-LkO_eGYb.js → yaml-B8pCNDb_.js} +1 -1
  23. package/out/renderer/index.html +2 -2
  24. package/package.json +1 -1
  25. package/src/main/ctlsurfApi.ts +26 -0
  26. package/src/main/headless.ts +40 -34
  27. package/src/main/index.ts +95 -13
  28. package/src/main/orchestrator.ts +160 -55
  29. package/src/main/timeTracker.ts +223 -0
  30. package/src/main/tui.ts +25 -5
  31. package/src/preload/index.ts +23 -15
  32. package/src/renderer/App.tsx +197 -43
  33. package/src/renderer/components/SettingsDialog.tsx +38 -1
  34. package/src/renderer/components/TerminalPanel.tsx +109 -59
  35. package/src/renderer/styles.css +132 -0
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-C6bY9C4O.js","./lspLanguageFeatures-Dljhj5Gh.js","./htmlMode-DYFYy4MK.js","./jsonMode-DdDRlbXP.js","./javascript-CiHhG2a9.js","./typescript-422MU_YO.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-DiOmyihM.js","./lspLanguageFeatures-CUafmPGy.js","./htmlMode-CQ5Xenrg.js","./jsonMode-DshPNyVy.js","./javascript-U5dsRcHx.js","./typescript-Cvna1mak.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
  }
@@ -18721,22 +18721,21 @@ function requireAddonWebLinks() {
18721
18721
  return addonWebLinks.exports;
18722
18722
  }
18723
18723
  var addonWebLinksExports = requireAddonWebLinks();
18724
- let _terminal = null;
18725
- let _fitAddon = null;
18726
- let _currentAgentId = null;
18727
- let _pinnedToBottom = true;
18724
+ const _terminals = /* @__PURE__ */ new Map();
18728
18725
  function isAtBottom(terminal) {
18729
18726
  const buf = terminal.buffer.active;
18730
18727
  return buf.viewportY >= buf.baseY;
18731
18728
  }
18732
- function scrollIfPinned(terminal) {
18733
- if (_pinnedToBottom) {
18729
+ function scrollIfPinned(tabId, terminal) {
18730
+ const state = _terminals.get(tabId);
18731
+ if (state?.pinnedToBottom) {
18734
18732
  terminal.scrollToBottom();
18735
18733
  }
18736
18734
  }
18737
- function getOrCreateTerminal(onExit) {
18738
- if (_terminal && _fitAddon) {
18739
- return { terminal: _terminal, fitAddon: _fitAddon };
18735
+ function getOrCreateTerminal(tabId, onExit) {
18736
+ const existing = _terminals.get(tabId);
18737
+ if (existing) {
18738
+ return { terminal: existing.terminal, fitAddon: existing.fitAddon };
18740
18739
  }
18741
18740
  const terminal = new xtermExports.Terminal({
18742
18741
  cursorBlink: true,
@@ -18771,32 +18770,51 @@ function getOrCreateTerminal(onExit) {
18771
18770
  const fitAddon = new addonFitExports.FitAddon();
18772
18771
  terminal.loadAddon(fitAddon);
18773
18772
  terminal.loadAddon(new addonWebLinksExports.WebLinksAddon());
18773
+ const state = {
18774
+ terminal,
18775
+ fitAddon,
18776
+ unsubData: null,
18777
+ unsubExit: null,
18778
+ spawnedAgentId: null,
18779
+ pinnedToBottom: true
18780
+ };
18774
18781
  terminal.onScroll(() => {
18775
- _pinnedToBottom = isAtBottom(terminal);
18782
+ state.pinnedToBottom = isAtBottom(terminal);
18776
18783
  });
18777
18784
  terminal.onData((data) => {
18778
- _pinnedToBottom = true;
18785
+ state.pinnedToBottom = true;
18779
18786
  terminal.scrollToBottom();
18780
- window.worker.writePty(data);
18787
+ window.worker.writePty(tabId, data);
18781
18788
  });
18782
- window.worker.onPtyData((data) => {
18783
- terminal.write(data);
18784
- scrollIfPinned(terminal);
18789
+ state.unsubData = window.worker.onPtyData((eventTabId, data) => {
18790
+ if (eventTabId === tabId) {
18791
+ terminal.write(data);
18792
+ scrollIfPinned(tabId, terminal);
18793
+ }
18785
18794
  });
18786
- window.worker.onPtyExit((code) => {
18787
- terminal.writeln(`\r
18795
+ state.unsubExit = window.worker.onPtyExit((eventTabId, code) => {
18796
+ if (eventTabId === tabId) {
18797
+ terminal.writeln(`\r
18788
18798
  \x1B[33m[Process exited with code ${code}]\x1B[0m`);
18789
- onExit();
18799
+ onExit(tabId);
18800
+ }
18790
18801
  });
18791
- _terminal = terminal;
18792
- _fitAddon = fitAddon;
18802
+ _terminals.set(tabId, state);
18793
18803
  return { terminal, fitAddon };
18794
18804
  }
18795
- function TerminalPanel({ agent, onSpawn, onExit }) {
18805
+ function destroyTerminal(tabId) {
18806
+ const state = _terminals.get(tabId);
18807
+ if (!state) return;
18808
+ state.unsubData?.();
18809
+ state.unsubExit?.();
18810
+ state.terminal.dispose();
18811
+ _terminals.delete(tabId);
18812
+ }
18813
+ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
18796
18814
  const containerRef = reactExports.useRef(null);
18797
18815
  reactExports.useEffect(() => {
18798
18816
  if (!containerRef.current) return;
18799
- const { terminal, fitAddon } = getOrCreateTerminal(onExit);
18817
+ const { terminal, fitAddon } = getOrCreateTerminal(tabId, onExit);
18800
18818
  const existingParent = terminal.element?.parentElement;
18801
18819
  if (existingParent && existingParent !== containerRef.current) {
18802
18820
  containerRef.current.appendChild(terminal.element);
@@ -18804,16 +18822,17 @@ function TerminalPanel({ agent, onSpawn, onExit }) {
18804
18822
  terminal.open(containerRef.current);
18805
18823
  }
18806
18824
  fitAddon.fit();
18807
- scrollIfPinned(terminal);
18825
+ scrollIfPinned(tabId, terminal);
18808
18826
  let resizeTimeout;
18809
18827
  const handleResize = () => {
18810
18828
  clearTimeout(resizeTimeout);
18811
18829
  resizeTimeout = setTimeout(() => {
18812
- if (_fitAddon && _terminal) {
18813
- _fitAddon.fit();
18814
- scrollIfPinned(_terminal);
18815
- const { cols, rows } = _terminal;
18816
- window.worker.resizePty(cols, rows);
18830
+ const state = _terminals.get(tabId);
18831
+ if (state) {
18832
+ state.fitAddon.fit();
18833
+ scrollIfPinned(tabId, state.terminal);
18834
+ const { cols, rows } = state.terminal;
18835
+ window.worker.resizePty(tabId, cols, rows);
18817
18836
  }
18818
18837
  }, 150);
18819
18838
  };
@@ -18825,23 +18844,43 @@ function TerminalPanel({ agent, onSpawn, onExit }) {
18825
18844
  window.removeEventListener("resize", handleResize);
18826
18845
  observer.disconnect();
18827
18846
  };
18828
- }, [onExit]);
18847
+ }, [tabId, onExit]);
18829
18848
  reactExports.useEffect(() => {
18830
- if (!agent || !_terminal) return;
18831
- if (_currentAgentId === agent.id) return;
18832
- _currentAgentId = agent.id;
18833
- _terminal.clear();
18834
- onSpawn(agent).then(() => {
18835
- if (_fitAddon && _terminal) {
18836
- _fitAddon.fit();
18837
- _pinnedToBottom = true;
18838
- _terminal.scrollToBottom();
18839
- const { cols, rows } = _terminal;
18840
- window.worker.resizePty(cols, rows);
18841
- }
18842
- _terminal?.focus();
18843
- });
18844
- }, [agent, onSpawn]);
18849
+ if (!agent) return;
18850
+ const state = _terminals.get(tabId);
18851
+ if (!state) return;
18852
+ if (state.spawnedAgentId === agent.id) return;
18853
+ state.spawnedAgentId = agent.id;
18854
+ state.terminal.clear();
18855
+ onSpawn(tabId, agent).then(() => {
18856
+ const s = _terminals.get(tabId);
18857
+ if (s) {
18858
+ s.fitAddon.fit();
18859
+ s.pinnedToBottom = true;
18860
+ s.terminal.scrollToBottom();
18861
+ const { cols, rows } = s.terminal;
18862
+ window.worker.resizePty(tabId, cols, rows);
18863
+ }
18864
+ state.terminal.focus();
18865
+ });
18866
+ }, [tabId, agent, onSpawn]);
18867
+ reactExports.useEffect(() => {
18868
+ if (isActive) {
18869
+ window.worker.setActiveTab(tabId);
18870
+ requestAnimationFrame(() => {
18871
+ setTimeout(() => {
18872
+ const state = _terminals.get(tabId);
18873
+ if (state) {
18874
+ state.fitAddon.fit();
18875
+ state.terminal.focus();
18876
+ scrollIfPinned(tabId, state.terminal);
18877
+ const { cols, rows } = state.terminal;
18878
+ window.worker.resizePty(tabId, cols, rows);
18879
+ }
18880
+ }, 50);
18881
+ });
18882
+ }
18883
+ }, [isActive, tabId]);
18845
18884
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "terminal-container", ref: containerRef });
18846
18885
  }
18847
18886
  function CtlsurfPanel() {
@@ -206517,7 +206556,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
206517
206556
  modeConfigurationDefault$2
206518
206557
  );
206519
206558
  function getMode$3() {
206520
- return __vitePreload(() => import("./cssMode-C6bY9C4O.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
206559
+ return __vitePreload(() => import("./cssMode-DiOmyihM.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
206521
206560
  }
206522
206561
  languages.onLanguage("less", () => {
206523
206562
  getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
@@ -206622,7 +206661,7 @@ const razorLanguageService = registerHTMLLanguageService(
206622
206661
  );
206623
206662
  const razorDefaults = razorLanguageService.defaults;
206624
206663
  function getMode$2() {
206625
- return __vitePreload(() => import("./htmlMode-DYFYy4MK.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
206664
+ return __vitePreload(() => import("./htmlMode-CQ5Xenrg.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
206626
206665
  }
206627
206666
  function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
206628
206667
  const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
@@ -206706,7 +206745,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
206706
206745
  );
206707
206746
  const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
206708
206747
  function getMode$1() {
206709
- return __vitePreload(() => import("./jsonMode-DdDRlbXP.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
206748
+ return __vitePreload(() => import("./jsonMode-DshPNyVy.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
206710
206749
  }
206711
206750
  languages.register({
206712
206751
  id: "json",
@@ -206952,7 +206991,7 @@ const getJavaScriptWorker = () => {
206952
206991
  return getMode().then((mode) => mode.getJavaScriptWorker());
206953
206992
  };
206954
206993
  function getMode() {
206955
- return __vitePreload(() => import("./tsMode-BGTjG8Ow.js"), true ? [] : void 0, import.meta.url);
206994
+ return __vitePreload(() => import("./tsMode-D-7JexQ_.js"), true ? [] : void 0, import.meta.url);
206956
206995
  }
206957
206996
  languages.onLanguage("typescript", () => {
206958
206997
  return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
@@ -207147,49 +207186,49 @@ registerLanguage({
207147
207186
  extensions: [".ftl", ".ftlh", ".ftlx"],
207148
207187
  aliases: ["FreeMarker2", "Apache FreeMarker2"],
207149
207188
  loader: () => {
207150
- return __vitePreload(() => import("./freemarker2-CkAJiX1K.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207189
+ return __vitePreload(() => import("./freemarker2-BAfv60yb.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207151
207190
  }
207152
207191
  });
207153
207192
  registerLanguage({
207154
207193
  id: "freemarker2.tag-angle.interpolation-dollar",
207155
207194
  aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
207156
207195
  loader: () => {
207157
- return __vitePreload(() => import("./freemarker2-CkAJiX1K.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
207196
+ return __vitePreload(() => import("./freemarker2-BAfv60yb.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
207158
207197
  }
207159
207198
  });
207160
207199
  registerLanguage({
207161
207200
  id: "freemarker2.tag-bracket.interpolation-dollar",
207162
207201
  aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
207163
207202
  loader: () => {
207164
- return __vitePreload(() => import("./freemarker2-CkAJiX1K.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
207203
+ return __vitePreload(() => import("./freemarker2-BAfv60yb.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
207165
207204
  }
207166
207205
  });
207167
207206
  registerLanguage({
207168
207207
  id: "freemarker2.tag-angle.interpolation-bracket",
207169
207208
  aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
207170
207209
  loader: () => {
207171
- return __vitePreload(() => import("./freemarker2-CkAJiX1K.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
207210
+ return __vitePreload(() => import("./freemarker2-BAfv60yb.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
207172
207211
  }
207173
207212
  });
207174
207213
  registerLanguage({
207175
207214
  id: "freemarker2.tag-bracket.interpolation-bracket",
207176
207215
  aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
207177
207216
  loader: () => {
207178
- return __vitePreload(() => import("./freemarker2-CkAJiX1K.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
207217
+ return __vitePreload(() => import("./freemarker2-BAfv60yb.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
207179
207218
  }
207180
207219
  });
207181
207220
  registerLanguage({
207182
207221
  id: "freemarker2.tag-auto.interpolation-dollar",
207183
207222
  aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
207184
207223
  loader: () => {
207185
- return __vitePreload(() => import("./freemarker2-CkAJiX1K.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207224
+ return __vitePreload(() => import("./freemarker2-BAfv60yb.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207186
207225
  }
207187
207226
  });
207188
207227
  registerLanguage({
207189
207228
  id: "freemarker2.tag-auto.interpolation-bracket",
207190
207229
  aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
207191
207230
  loader: () => {
207192
- return __vitePreload(() => import("./freemarker2-CkAJiX1K.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
207231
+ return __vitePreload(() => import("./freemarker2-BAfv60yb.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
207193
207232
  }
207194
207233
  });
207195
207234
  registerLanguage({
@@ -207210,7 +207249,7 @@ registerLanguage({
207210
207249
  extensions: [".handlebars", ".hbs"],
207211
207250
  aliases: ["Handlebars", "handlebars", "hbs"],
207212
207251
  mimetypes: ["text/x-handlebars-template"],
207213
- loader: () => __vitePreload(() => import("./handlebars-DnLXVUXp.js"), true ? [] : void 0, import.meta.url)
207252
+ loader: () => __vitePreload(() => import("./handlebars-Ult17NzQ.js"), true ? [] : void 0, import.meta.url)
207214
207253
  });
207215
207254
  registerLanguage({
207216
207255
  id: "hcl",
@@ -207223,7 +207262,7 @@ registerLanguage({
207223
207262
  extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
207224
207263
  aliases: ["HTML", "htm", "html", "xhtml"],
207225
207264
  mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
207226
- loader: () => __vitePreload(() => import("./html-Ds5-qvDh.js"), true ? [] : void 0, import.meta.url)
207265
+ loader: () => __vitePreload(() => import("./html-DCxh4J-1.js"), true ? [] : void 0, import.meta.url)
207227
207266
  });
207228
207267
  registerLanguage({
207229
207268
  id: "ini",
@@ -207246,7 +207285,7 @@ registerLanguage({
207246
207285
  filenames: ["jakefile"],
207247
207286
  aliases: ["JavaScript", "javascript", "js"],
207248
207287
  mimetypes: ["text/javascript"],
207249
- loader: () => __vitePreload(() => import("./javascript-CiHhG2a9.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
207288
+ loader: () => __vitePreload(() => import("./javascript-U5dsRcHx.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
207250
207289
  });
207251
207290
  registerLanguage({
207252
207291
  id: "julia",
@@ -207285,7 +207324,7 @@ registerLanguage({
207285
207324
  extensions: [".liquid", ".html.liquid"],
207286
207325
  aliases: ["Liquid", "liquid"],
207287
207326
  mimetypes: ["application/liquid"],
207288
- loader: () => __vitePreload(() => import("./liquid-BP5mb-uD.js"), true ? [] : void 0, import.meta.url)
207327
+ loader: () => __vitePreload(() => import("./liquid-jHHLYTlB.js"), true ? [] : void 0, import.meta.url)
207289
207328
  });
207290
207329
  registerLanguage({
207291
207330
  id: "m3",
@@ -207303,7 +207342,7 @@ registerLanguage({
207303
207342
  id: "mdx",
207304
207343
  extensions: [".mdx"],
207305
207344
  aliases: ["MDX", "mdx"],
207306
- loader: () => __vitePreload(() => import("./mdx-D4u3N7dt.js"), true ? [] : void 0, import.meta.url)
207345
+ loader: () => __vitePreload(() => import("./mdx-Ct-tiY6g.js"), true ? [] : void 0, import.meta.url)
207307
207346
  });
207308
207347
  registerLanguage({
207309
207348
  id: "mips",
@@ -207402,7 +207441,7 @@ registerLanguage({
207402
207441
  extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
207403
207442
  aliases: ["Python", "py"],
207404
207443
  firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
207405
- loader: () => __vitePreload(() => import("./python-BQDHXVwp.js"), true ? [] : void 0, import.meta.url)
207444
+ loader: () => __vitePreload(() => import("./python-wD3UwKPV.js"), true ? [] : void 0, import.meta.url)
207406
207445
  });
207407
207446
  registerLanguage({
207408
207447
  id: "qsharp",
@@ -207421,7 +207460,7 @@ registerLanguage({
207421
207460
  extensions: [".cshtml"],
207422
207461
  aliases: ["Razor", "razor"],
207423
207462
  mimetypes: ["text/x-cshtml"],
207424
- loader: () => __vitePreload(() => import("./razor-BfXW9cDc.js"), true ? [] : void 0, import.meta.url)
207463
+ loader: () => __vitePreload(() => import("./razor-11ECS4oH.js"), true ? [] : void 0, import.meta.url)
207425
207464
  });
207426
207465
  registerLanguage({
207427
207466
  id: "redis",
@@ -207554,7 +207593,7 @@ registerLanguage({
207554
207593
  aliases: ["TypeScript", "ts", "typescript"],
207555
207594
  mimetypes: ["text/typescript"],
207556
207595
  loader: () => {
207557
- return __vitePreload(() => import("./typescript-422MU_YO.js"), true ? [] : void 0, import.meta.url);
207596
+ return __vitePreload(() => import("./typescript-Cvna1mak.js"), true ? [] : void 0, import.meta.url);
207558
207597
  }
207559
207598
  });
207560
207599
  registerLanguage({
@@ -207599,14 +207638,14 @@ registerLanguage({
207599
207638
  firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
207600
207639
  aliases: ["XML", "xml"],
207601
207640
  mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
207602
- loader: () => __vitePreload(() => import("./xml-B6EKhHiy.js"), true ? [] : void 0, import.meta.url)
207641
+ loader: () => __vitePreload(() => import("./xml-JsEaImjA.js"), true ? [] : void 0, import.meta.url)
207603
207642
  });
207604
207643
  registerLanguage({
207605
207644
  id: "yaml",
207606
207645
  extensions: [".yaml", ".yml"],
207607
207646
  aliases: ["YAML", "yaml", "YML", "yml"],
207608
207647
  mimetypes: ["application/x-yaml", "text/x-yaml"],
207609
- loader: () => __vitePreload(() => import("./yaml-LkO_eGYb.js"), true ? [] : void 0, import.meta.url)
207648
+ loader: () => __vitePreload(() => import("./yaml-B8pCNDb_.js"), true ? [] : void 0, import.meta.url)
207610
207649
  });
207611
207650
  var __defProp = Object.defineProperty;
207612
207651
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -211475,6 +211514,8 @@ function SettingsDialog({ open, onClose }) {
211475
211514
  const [apiKey, setApiKey] = reactExports.useState("");
211476
211515
  const [baseUrl, setBaseUrl] = reactExports.useState("");
211477
211516
  const [dataspacePageId, setDataspacePageId] = reactExports.useState("");
211517
+ const [trackTime, setTrackTime] = reactExports.useState(false);
211518
+ const [idleTimeoutMin, setIdleTimeoutMin] = reactExports.useState(15);
211478
211519
  const [saved, setSaved] = reactExports.useState(false);
211479
211520
  const loadProfiles = async () => {
211480
211521
  const data = await window.worker.listProfiles();
@@ -211494,6 +211535,8 @@ function SettingsDialog({ open, onClose }) {
211494
211535
  setApiKey("");
211495
211536
  setBaseUrl(profile.baseUrl);
211496
211537
  setDataspacePageId(profile.dataspacePageId || "");
211538
+ setTrackTime(!!profile.trackTime);
211539
+ setIdleTimeoutMin(profile.idleTimeoutMin ?? 15);
211497
211540
  setSaved(false);
211498
211541
  };
211499
211542
  const startNewProfile = () => {
@@ -211503,6 +211546,8 @@ function SettingsDialog({ open, onClose }) {
211503
211546
  setApiKey("");
211504
211547
  setBaseUrl("http://localhost:8000");
211505
211548
  setDataspacePageId("");
211549
+ setTrackTime(true);
211550
+ setIdleTimeoutMin(15);
211506
211551
  setSaved(false);
211507
211552
  };
211508
211553
  const handleSave = async () => {
@@ -211510,7 +211555,9 @@ function SettingsDialog({ open, onClose }) {
211510
211555
  const data = {
211511
211556
  name: name.trim(),
211512
211557
  baseUrl: baseUrl.trim() || "https://app.ctlsurf.com",
211513
- dataspacePageId: dataspacePageId.trim()
211558
+ dataspacePageId: dataspacePageId.trim(),
211559
+ trackTime,
211560
+ idleTimeoutMin: Math.max(1, Math.floor(idleTimeoutMin)) || 15
211514
211561
  };
211515
211562
  if (apiKey.trim()) {
211516
211563
  data.apiKey = apiKey.trim();
@@ -211590,6 +211637,31 @@ function SettingsDialog({ open, onClose }) {
211590
211637
  }
211591
211638
  )
211592
211639
  ] }),
211640
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "settings-checkbox", children: [
211641
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
211642
+ "input",
211643
+ {
211644
+ type: "checkbox",
211645
+ checked: trackTime,
211646
+ onChange: (e) => setTrackTime(e.target.checked)
211647
+ }
211648
+ ),
211649
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Track time per project" }),
211650
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "settings-hint", children: `Logs each session to a "Time Tracking" datastore on the project's Agent Datastore page.` })
211651
+ ] }),
211652
+ trackTime && /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { children: [
211653
+ "Idle timeout (min)",
211654
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
211655
+ "input",
211656
+ {
211657
+ type: "number",
211658
+ min: 1,
211659
+ value: idleTimeoutMin,
211660
+ onChange: (e) => setIdleTimeoutMin(parseInt(e.target.value, 10) || 15)
211661
+ }
211662
+ ),
211663
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "settings-hint", children: "Gaps longer than this without terminal activity are counted as idle, not work." })
211664
+ ] }),
211593
211665
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "settings-actions", children: [
211594
211666
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "btn-secondary", onClick: () => setEditingId(null), children: "Back" }),
211595
211667
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "btn-primary", onClick: handleSave, children: saved ? "Saved!" : "Save" })
@@ -211638,18 +211710,27 @@ const DEFAULT_LAYOUT = {
211638
211710
  sizes: [50, 50]
211639
211711
  };
211640
211712
  const ALL_PANE_IDS = ["editor", "terminal", "ctlsurf"];
211713
+ let _tabCounter = 0;
211714
+ function nextTabId() {
211715
+ return `tab-${++_tabCounter}`;
211716
+ }
211641
211717
  function App() {
211642
211718
  const [agents, setAgents] = reactExports.useState([]);
211643
- const [selectedAgent, setSelectedAgent] = reactExports.useState(null);
211644
- const [agentStatus, setAgentStatus] = reactExports.useState("idle");
211645
211719
  const [sessionStart, setSessionStart] = reactExports.useState(null);
211646
211720
  const [layout2, setLayout] = reactExports.useState(DEFAULT_LAYOUT);
211647
211721
  const [hiddenPanes, setHiddenPanes] = reactExports.useState(/* @__PURE__ */ new Set());
211648
211722
  const [showSettings, setShowSettings] = reactExports.useState(false);
211649
211723
  const [wsStatus, setWsStatus] = reactExports.useState("disconnected");
211650
211724
  const [cwd2, setCwd] = reactExports.useState(null);
211651
- const visiblePaneIds = findPaneIds(layout2);
211725
+ const [tabs, setTabs] = reactExports.useState(() => {
211726
+ const id = nextTabId();
211727
+ return [{ id, label: "Terminal 1", agent: null, agentStatus: "idle" }];
211728
+ });
211729
+ const [activeTabId, setActiveTabId] = reactExports.useState(tabs[0].id);
211730
+ const [trackingActive, setTrackingActive] = reactExports.useState(false);
211731
+ const [pickerTargetTabId, setPickerTargetTabId] = reactExports.useState(tabs[0].id);
211652
211732
  const [showAgentPicker, setShowAgentPicker] = reactExports.useState(true);
211733
+ const visiblePaneIds = findPaneIds(layout2);
211653
211734
  reactExports.useEffect(() => {
211654
211735
  async function init2() {
211655
211736
  const list2 = await window.worker.listAgents();
@@ -211672,41 +211753,102 @@ function App() {
211672
211753
  });
211673
211754
  return unsub;
211674
211755
  }, []);
211675
- const spawnGenRef = reactExports.useRef(0);
211756
+ reactExports.useEffect(() => {
211757
+ let cancelled = false;
211758
+ const refresh = async () => {
211759
+ try {
211760
+ const r = await window.worker.getTracking();
211761
+ if (!cancelled) setTrackingActive(!!r?.active);
211762
+ } catch {
211763
+ }
211764
+ };
211765
+ refresh();
211766
+ const id = setInterval(refresh, 4e3);
211767
+ return () => {
211768
+ cancelled = true;
211769
+ clearInterval(id);
211770
+ };
211771
+ }, [activeTabId]);
211772
+ const handleToggleTracking = reactExports.useCallback(async () => {
211773
+ try {
211774
+ const r = await window.worker.setTracking(!trackingActive);
211775
+ setTrackingActive(!!r?.active);
211776
+ } catch (err) {
211777
+ console.error("[tracking] toggle failed", err);
211778
+ }
211779
+ }, [trackingActive]);
211676
211780
  const cwdRef = reactExports.useRef(null);
211677
- const handleSpawn = reactExports.useCallback(async (agent) => {
211678
- spawnGenRef.current += 1;
211679
- setAgentStatus("active");
211781
+ const handleSpawn = reactExports.useCallback(async (tabId, agent) => {
211782
+ setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "active" } : t));
211680
211783
  setSessionStart(/* @__PURE__ */ new Date());
211681
211784
  const spawnCwd = cwdRef.current || await window.worker.getCwd().catch(() => window.worker.getHomePath());
211682
211785
  setCwd(spawnCwd);
211683
211786
  cwdRef.current = spawnCwd;
211684
- await window.worker.spawnAgent(agent, spawnCwd);
211787
+ await window.worker.spawnAgent(tabId, agent, spawnCwd);
211685
211788
  }, []);
211686
211789
  const handleAgentChange = reactExports.useCallback(async (agentId) => {
211687
211790
  const agent = agents.find((a) => a.id === agentId);
211688
211791
  if (agent) {
211689
- await window.worker.killPty();
211690
- setSelectedAgent(agent);
211792
+ await window.worker.killPty(activeTabId);
211793
+ setTabs((prev) => prev.map((t) => t.id === activeTabId ? { ...t, agent, agentStatus: "idle" } : t));
211691
211794
  }
211692
- }, [agents]);
211693
- const handleExit = reactExports.useCallback(() => {
211694
- const gen = spawnGenRef.current;
211795
+ }, [agents, activeTabId]);
211796
+ const handleExit = reactExports.useCallback((tabId) => {
211695
211797
  setTimeout(() => {
211696
- if (spawnGenRef.current === gen) setAgentStatus("exited");
211798
+ setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "exited" } : t));
211697
211799
  }, 200);
211698
211800
  }, []);
211699
211801
  const handleChangeCwd = reactExports.useCallback(async () => {
211700
211802
  const newCwd = await window.worker.browseCwd();
211701
- if (!newCwd || !selectedAgent) return;
211803
+ const activeTab = tabs.find((t) => t.id === activeTabId);
211804
+ if (!newCwd || !activeTab?.agent) return;
211702
211805
  cwdRef.current = newCwd;
211703
211806
  setCwd(newCwd);
211704
- await window.worker.killPty();
211705
- spawnGenRef.current += 1;
211706
- setAgentStatus("active");
211807
+ await window.worker.killPty(activeTabId);
211808
+ setTabs((prev) => prev.map((t) => t.id === activeTabId ? { ...t, agentStatus: "active" } : t));
211707
211809
  setSessionStart(/* @__PURE__ */ new Date());
211708
- await window.worker.spawnAgent(selectedAgent, newCwd);
211709
- }, [selectedAgent]);
211810
+ await window.worker.spawnAgent(activeTabId, activeTab.agent, newCwd);
211811
+ }, [tabs, activeTabId]);
211812
+ const addTab = reactExports.useCallback(() => {
211813
+ const id = nextTabId();
211814
+ const num = _tabCounter;
211815
+ setTabs((prev) => [...prev, { id, label: `Terminal ${num}`, agent: null, agentStatus: "idle" }]);
211816
+ setActiveTabId(id);
211817
+ setPickerTargetTabId(id);
211818
+ setShowAgentPicker(true);
211819
+ }, []);
211820
+ const handlePickerSelect = reactExports.useCallback((agent) => {
211821
+ const tabId = pickerTargetTabId;
211822
+ if (tabId) {
211823
+ setTabs((prev) => prev.map(
211824
+ (t) => t.id === tabId ? { ...t, agent, label: `${agent.name} ${tabs.filter((x) => x.agent?.id === agent.id).length + 1}` } : t
211825
+ ));
211826
+ }
211827
+ setShowAgentPicker(false);
211828
+ setPickerTargetTabId(null);
211829
+ }, [pickerTargetTabId, tabs]);
211830
+ const closeTab = reactExports.useCallback(async (tabId) => {
211831
+ await window.worker.killPty(tabId);
211832
+ destroyTerminal(tabId);
211833
+ setTabs((prev) => {
211834
+ const next = prev.filter((t) => t.id !== tabId);
211835
+ if (tabId === activeTabId && next.length > 0) {
211836
+ setActiveTabId(next[next.length - 1].id);
211837
+ }
211838
+ if (next.length === 0) {
211839
+ const id = nextTabId();
211840
+ const num = _tabCounter;
211841
+ setActiveTabId(id);
211842
+ setPickerTargetTabId(id);
211843
+ setShowAgentPicker(true);
211844
+ return [{ id, label: `Terminal ${num}`, agent: null, agentStatus: "idle" }];
211845
+ }
211846
+ return next;
211847
+ });
211848
+ }, [activeTabId]);
211849
+ const switchTab = reactExports.useCallback((tabId) => {
211850
+ setActiveTabId(tabId);
211851
+ }, []);
211710
211852
  const togglePane = reactExports.useCallback((id) => {
211711
211853
  if (visiblePaneIds.includes(id)) {
211712
211854
  const newLayout = removePane(layout2, id);
@@ -211774,7 +211916,59 @@ function App() {
211774
211916
  {
211775
211917
  id: "terminal",
211776
211918
  label: "Terminal",
211777
- content: /* @__PURE__ */ jsxRuntimeExports.jsx(TerminalPanel, { agent: selectedAgent, onSpawn: handleSpawn, onExit: handleExit })
211919
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "terminal-tabs-wrapper", children: [
211920
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "terminal-tab-bar", children: [
211921
+ tabs.map((tab) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
211922
+ "div",
211923
+ {
211924
+ className: `terminal-tab ${tab.id === activeTabId ? "active" : ""}`,
211925
+ onClick: () => switchTab(tab.id),
211926
+ children: [
211927
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "terminal-tab-label", children: [
211928
+ tab.agent ? tab.agent.name : tab.label,
211929
+ tab.agentStatus === "active" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "terminal-tab-dot active" }),
211930
+ tab.agentStatus === "exited" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "terminal-tab-dot exited" })
211931
+ ] }),
211932
+ tabs.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
211933
+ "button",
211934
+ {
211935
+ className: "terminal-tab-close",
211936
+ onClick: (e) => {
211937
+ e.stopPropagation();
211938
+ closeTab(tab.id);
211939
+ },
211940
+ title: "Close tab",
211941
+ children: "×"
211942
+ }
211943
+ )
211944
+ ]
211945
+ },
211946
+ tab.id
211947
+ )),
211948
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "terminal-tab-add", onClick: addTab, title: "New terminal tab", children: "+" })
211949
+ ] }),
211950
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "terminal-tabs-content", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntimeExports.jsx(
211951
+ "div",
211952
+ {
211953
+ className: "terminal-tab-panel",
211954
+ style: {
211955
+ display: tab.id === activeTabId ? "flex" : "none",
211956
+ flex: 1
211957
+ },
211958
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
211959
+ TerminalPanel,
211960
+ {
211961
+ tabId: tab.id,
211962
+ agent: tab.agent,
211963
+ onSpawn: handleSpawn,
211964
+ onExit: handleExit,
211965
+ isActive: tab.id === activeTabId
211966
+ }
211967
+ )
211968
+ },
211969
+ tab.id
211970
+ )) })
211971
+ ] })
211778
211972
  },
211779
211973
  { id: "ctlsurf", label: "ctlsurf", content: /* @__PURE__ */ jsxRuntimeExports.jsx(CtlsurfPanel, {}) }
211780
211974
  ];
@@ -211783,17 +211977,33 @@ function App() {
211783
211977
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-title", children: "ctlsurf-worker" }),
211784
211978
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "titlebar-controls", children: [
211785
211979
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "titlebar-btn", onClick: () => setShowSettings(true), title: "Settings", children: "Settings" }),
211786
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
211787
- agents.map((a) => /* @__PURE__ */ jsxRuntimeExports.jsx(
211980
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
211788
211981
  "button",
211789
211982
  {
211790
- className: `titlebar-agent-btn ${selectedAgent?.id === a.id ? "active" : ""}`,
211791
- onClick: () => handleAgentChange(a.id),
211792
- title: a.description,
211793
- children: a.name
211794
- },
211795
- a.id
211796
- )),
211983
+ className: `titlebar-btn titlebar-icon-btn ${trackingActive ? "active" : ""}`,
211984
+ onClick: handleToggleTracking,
211985
+ title: trackingActive ? "Time tracking on — click to stop" : "Time tracking off — click to start",
211986
+ "aria-label": "Time tracking",
211987
+ children: [
211988
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "tracking-icon", children: "⏱" }),
211989
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `tracking-dot ${trackingActive ? "on" : "off"}` })
211990
+ ]
211991
+ }
211992
+ ),
211993
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
211994
+ agents.map((a) => {
211995
+ const activeTab = tabs.find((t) => t.id === activeTabId);
211996
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
211997
+ "button",
211998
+ {
211999
+ className: `titlebar-agent-btn ${activeTab?.agent?.id === a.id ? "active" : ""}`,
212000
+ onClick: () => handleAgentChange(a.id),
212001
+ title: a.description,
212002
+ children: a.name
212003
+ },
212004
+ a.id
212005
+ );
212006
+ }),
211797
212007
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
211798
212008
  ALL_PANE_IDS.map((id) => /* @__PURE__ */ jsxRuntimeExports.jsx(
211799
212009
  "button",
@@ -211823,10 +212033,7 @@ function App() {
211823
212033
  {
211824
212034
  agents,
211825
212035
  cwd: cwd2 || "",
211826
- onSelect: (agent) => {
211827
- setSelectedAgent(agent);
211828
- setShowAgentPicker(false);
211829
- },
212036
+ onSelect: handlePickerSelect,
211830
212037
  onChangeCwd: async () => {
211831
212038
  const newCwd = await window.worker.browseCwd();
211832
212039
  if (newCwd) {