@phenx-inc/ctlsurf 0.15.2 → 0.16.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.
- package/out/headless/index.mjs +10 -3
- package/out/headless/index.mjs.map +2 -2
- package/out/main/index.js +54 -4
- package/out/preload/index.js +2 -0
- package/out/renderer/assets/{cssMode-CPfLk04b.js → cssMode-Xx1-SozN.js} +3 -3
- package/out/renderer/assets/{freemarker2-CDHpuFqv.js → freemarker2-DM49yPw1.js} +1 -1
- package/out/renderer/assets/{handlebars-CgX9Jz36.js → handlebars-DJJBblpV.js} +1 -1
- package/out/renderer/assets/{html-DdgFccDG.js → html-DnMnJOxw.js} +1 -1
- package/out/renderer/assets/{htmlMode-CRHIwtvP.js → htmlMode-BG5u2CSH.js} +3 -3
- package/out/renderer/assets/{index-DVdWgw7p.js → index-D65QIRev.js} +300 -84
- package/out/renderer/assets/{index-3sFJEz7i.css → index-DID1LlMU.css} +88 -0
- package/out/renderer/assets/{javascript-CHhxL0s7.js → javascript-Dci_phmN.js} +2 -2
- package/out/renderer/assets/{jsonMode-Cy7-eWai.js → jsonMode-Dmp3JJSm.js} +3 -3
- package/out/renderer/assets/{liquid-vMkF9pfk.js → liquid-C9KVysk1.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-nAygNzuG.js → lspLanguageFeatures-IDD0-KWa.js} +1 -1
- package/out/renderer/assets/{mdx-C4UVz5dW.js → mdx-ascmBFXX.js} +1 -1
- package/out/renderer/assets/{python-BHemzN9g.js → python-DcbM4Hy5.js} +1 -1
- package/out/renderer/assets/{razor-DH6NPzbo.js → razor-XJROxpYK.js} +1 -1
- package/out/renderer/assets/{tsMode-DxBdlGZB.js → tsMode-EXbzul3n.js} +1 -1
- package/out/renderer/assets/{typescript-BRCOaBQ5.js → typescript-DhTtFBLt.js} +1 -1
- package/out/renderer/assets/{xml-DVv9gDP1.js → xml-BLfEkVl1.js} +1 -1
- package/out/renderer/assets/{yaml-jB-C3CdS.js → yaml-DpzsqvWa.js} +1 -1
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/src/main/index.ts +52 -1
- package/src/main/orchestrator.ts +18 -3
- package/src/preload/index.ts +4 -0
- package/src/renderer/App.tsx +179 -50
- package/src/renderer/components/AgentPicker.tsx +61 -10
- package/src/renderer/components/ProjectPicker.tsx +44 -13
- package/src/renderer/components/TerminalPanel.tsx +34 -7
- package/src/renderer/styles.css +88 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-Xx1-SozN.js","./lspLanguageFeatures-IDD0-KWa.js","./htmlMode-BG5u2CSH.js","./jsonMode-Dmp3JJSm.js","./javascript-Dci_phmN.js","./typescript-DhTtFBLt.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
|
}
|
|
@@ -19599,7 +19599,7 @@ function getOrCreateTerminal(tabId, onExit) {
|
|
|
19599
19599
|
fitAddon,
|
|
19600
19600
|
unsubData: null,
|
|
19601
19601
|
unsubExit: null,
|
|
19602
|
-
|
|
19602
|
+
spawnedSessionKey: null,
|
|
19603
19603
|
pinnedToBottom: true,
|
|
19604
19604
|
anchorLine: null,
|
|
19605
19605
|
userScrollPending: false,
|
|
@@ -19643,6 +19643,25 @@ function getOrCreateTerminal(tabId, onExit) {
|
|
|
19643
19643
|
function focusTerminal(tabId) {
|
|
19644
19644
|
_terminals.get(tabId)?.terminal.focus();
|
|
19645
19645
|
}
|
|
19646
|
+
const MAX_HANDOFF_CHARS = 1e6;
|
|
19647
|
+
function captureTerminalSession(tabId) {
|
|
19648
|
+
const terminal = _terminals.get(tabId)?.terminal;
|
|
19649
|
+
if (!terminal) return "";
|
|
19650
|
+
const buffer = terminal.buffer.active;
|
|
19651
|
+
const lines = [];
|
|
19652
|
+
for (let i2 = 0; i2 < buffer.length; i2++) {
|
|
19653
|
+
const line = buffer.getLine(i2);
|
|
19654
|
+
if (!line) continue;
|
|
19655
|
+
const text22 = line.translateToString(true);
|
|
19656
|
+
if (line.isWrapped && lines.length > 0) lines[lines.length - 1] += text22;
|
|
19657
|
+
else lines.push(text22);
|
|
19658
|
+
}
|
|
19659
|
+
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
|
19660
|
+
const text2 = lines.join("\n").trim();
|
|
19661
|
+
if (text2.length <= MAX_HANDOFF_CHARS) return text2;
|
|
19662
|
+
return `[Earlier terminal output omitted; showing the last ${MAX_HANDOFF_CHARS.toLocaleString()} characters.]
|
|
19663
|
+
${text2.slice(-MAX_HANDOFF_CHARS)}`;
|
|
19664
|
+
}
|
|
19646
19665
|
function destroyTerminal(tabId) {
|
|
19647
19666
|
const state = _terminals.get(tabId);
|
|
19648
19667
|
if (!state) return;
|
|
@@ -19651,7 +19670,7 @@ function destroyTerminal(tabId) {
|
|
|
19651
19670
|
state.terminal.dispose();
|
|
19652
19671
|
_terminals.delete(tabId);
|
|
19653
19672
|
}
|
|
19654
|
-
function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
19673
|
+
function TerminalPanel({ tabId, agent, sessionVersion, onSpawn, onExit, isActive }) {
|
|
19655
19674
|
const containerRef = reactExports.useRef(null);
|
|
19656
19675
|
const [menu, setMenu] = reactExports.useState(null);
|
|
19657
19676
|
const [snapOpen, setSnapOpen] = reactExports.useState(false);
|
|
@@ -19771,8 +19790,9 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
|
19771
19790
|
if (!agent) return;
|
|
19772
19791
|
const state = _terminals.get(tabId);
|
|
19773
19792
|
if (!state) return;
|
|
19774
|
-
|
|
19775
|
-
state.
|
|
19793
|
+
const sessionKey = `${agent.id}:${sessionVersion}`;
|
|
19794
|
+
if (state.spawnedSessionKey === sessionKey) return;
|
|
19795
|
+
state.spawnedSessionKey = sessionKey;
|
|
19776
19796
|
state.terminal.clear();
|
|
19777
19797
|
onSpawn(tabId, agent).then(() => {
|
|
19778
19798
|
const s = _terminals.get(tabId);
|
|
@@ -19785,7 +19805,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
|
19785
19805
|
}
|
|
19786
19806
|
state.terminal.focus();
|
|
19787
19807
|
});
|
|
19788
|
-
}, [tabId, agent, onSpawn]);
|
|
19808
|
+
}, [tabId, agent, sessionVersion, onSpawn]);
|
|
19789
19809
|
reactExports.useEffect(() => {
|
|
19790
19810
|
if (isActive) {
|
|
19791
19811
|
window.worker.setActiveTab(tabId);
|
|
@@ -208423,7 +208443,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
|
|
|
208423
208443
|
modeConfigurationDefault$2
|
|
208424
208444
|
);
|
|
208425
208445
|
function getMode$3() {
|
|
208426
|
-
return __vitePreload(() => import("./cssMode-
|
|
208446
|
+
return __vitePreload(() => import("./cssMode-Xx1-SozN.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
|
|
208427
208447
|
}
|
|
208428
208448
|
languages.onLanguage("less", () => {
|
|
208429
208449
|
getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
|
|
@@ -208528,7 +208548,7 @@ const razorLanguageService = registerHTMLLanguageService(
|
|
|
208528
208548
|
);
|
|
208529
208549
|
const razorDefaults = razorLanguageService.defaults;
|
|
208530
208550
|
function getMode$2() {
|
|
208531
|
-
return __vitePreload(() => import("./htmlMode-
|
|
208551
|
+
return __vitePreload(() => import("./htmlMode-BG5u2CSH.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
|
|
208532
208552
|
}
|
|
208533
208553
|
function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
|
|
208534
208554
|
const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
|
|
@@ -208612,7 +208632,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
|
|
|
208612
208632
|
);
|
|
208613
208633
|
const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
|
|
208614
208634
|
function getMode$1() {
|
|
208615
|
-
return __vitePreload(() => import("./jsonMode-
|
|
208635
|
+
return __vitePreload(() => import("./jsonMode-Dmp3JJSm.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
|
|
208616
208636
|
}
|
|
208617
208637
|
languages.register({
|
|
208618
208638
|
id: "json",
|
|
@@ -208858,7 +208878,7 @@ const getJavaScriptWorker = () => {
|
|
|
208858
208878
|
return getMode().then((mode) => mode.getJavaScriptWorker());
|
|
208859
208879
|
};
|
|
208860
208880
|
function getMode() {
|
|
208861
|
-
return __vitePreload(() => import("./tsMode-
|
|
208881
|
+
return __vitePreload(() => import("./tsMode-EXbzul3n.js"), true ? [] : void 0, import.meta.url);
|
|
208862
208882
|
}
|
|
208863
208883
|
languages.onLanguage("typescript", () => {
|
|
208864
208884
|
return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
|
|
@@ -209053,49 +209073,49 @@ registerLanguage({
|
|
|
209053
209073
|
extensions: [".ftl", ".ftlh", ".ftlx"],
|
|
209054
209074
|
aliases: ["FreeMarker2", "Apache FreeMarker2"],
|
|
209055
209075
|
loader: () => {
|
|
209056
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209076
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
|
|
209057
209077
|
}
|
|
209058
209078
|
});
|
|
209059
209079
|
registerLanguage({
|
|
209060
209080
|
id: "freemarker2.tag-angle.interpolation-dollar",
|
|
209061
209081
|
aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
|
|
209062
209082
|
loader: () => {
|
|
209063
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209083
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
|
|
209064
209084
|
}
|
|
209065
209085
|
});
|
|
209066
209086
|
registerLanguage({
|
|
209067
209087
|
id: "freemarker2.tag-bracket.interpolation-dollar",
|
|
209068
209088
|
aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
|
|
209069
209089
|
loader: () => {
|
|
209070
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209090
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
|
|
209071
209091
|
}
|
|
209072
209092
|
});
|
|
209073
209093
|
registerLanguage({
|
|
209074
209094
|
id: "freemarker2.tag-angle.interpolation-bracket",
|
|
209075
209095
|
aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
|
|
209076
209096
|
loader: () => {
|
|
209077
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209097
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
|
|
209078
209098
|
}
|
|
209079
209099
|
});
|
|
209080
209100
|
registerLanguage({
|
|
209081
209101
|
id: "freemarker2.tag-bracket.interpolation-bracket",
|
|
209082
209102
|
aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
|
|
209083
209103
|
loader: () => {
|
|
209084
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209104
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
|
|
209085
209105
|
}
|
|
209086
209106
|
});
|
|
209087
209107
|
registerLanguage({
|
|
209088
209108
|
id: "freemarker2.tag-auto.interpolation-dollar",
|
|
209089
209109
|
aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
|
|
209090
209110
|
loader: () => {
|
|
209091
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209111
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
|
|
209092
209112
|
}
|
|
209093
209113
|
});
|
|
209094
209114
|
registerLanguage({
|
|
209095
209115
|
id: "freemarker2.tag-auto.interpolation-bracket",
|
|
209096
209116
|
aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
|
|
209097
209117
|
loader: () => {
|
|
209098
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209118
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
|
|
209099
209119
|
}
|
|
209100
209120
|
});
|
|
209101
209121
|
registerLanguage({
|
|
@@ -209116,7 +209136,7 @@ registerLanguage({
|
|
|
209116
209136
|
extensions: [".handlebars", ".hbs"],
|
|
209117
209137
|
aliases: ["Handlebars", "handlebars", "hbs"],
|
|
209118
209138
|
mimetypes: ["text/x-handlebars-template"],
|
|
209119
|
-
loader: () => __vitePreload(() => import("./handlebars-
|
|
209139
|
+
loader: () => __vitePreload(() => import("./handlebars-DJJBblpV.js"), true ? [] : void 0, import.meta.url)
|
|
209120
209140
|
});
|
|
209121
209141
|
registerLanguage({
|
|
209122
209142
|
id: "hcl",
|
|
@@ -209129,7 +209149,7 @@ registerLanguage({
|
|
|
209129
209149
|
extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
|
|
209130
209150
|
aliases: ["HTML", "htm", "html", "xhtml"],
|
|
209131
209151
|
mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
|
|
209132
|
-
loader: () => __vitePreload(() => import("./html-
|
|
209152
|
+
loader: () => __vitePreload(() => import("./html-DnMnJOxw.js"), true ? [] : void 0, import.meta.url)
|
|
209133
209153
|
});
|
|
209134
209154
|
registerLanguage({
|
|
209135
209155
|
id: "ini",
|
|
@@ -209152,7 +209172,7 @@ registerLanguage({
|
|
|
209152
209172
|
filenames: ["jakefile"],
|
|
209153
209173
|
aliases: ["JavaScript", "javascript", "js"],
|
|
209154
209174
|
mimetypes: ["text/javascript"],
|
|
209155
|
-
loader: () => __vitePreload(() => import("./javascript-
|
|
209175
|
+
loader: () => __vitePreload(() => import("./javascript-Dci_phmN.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
|
|
209156
209176
|
});
|
|
209157
209177
|
registerLanguage({
|
|
209158
209178
|
id: "julia",
|
|
@@ -209191,7 +209211,7 @@ registerLanguage({
|
|
|
209191
209211
|
extensions: [".liquid", ".html.liquid"],
|
|
209192
209212
|
aliases: ["Liquid", "liquid"],
|
|
209193
209213
|
mimetypes: ["application/liquid"],
|
|
209194
|
-
loader: () => __vitePreload(() => import("./liquid-
|
|
209214
|
+
loader: () => __vitePreload(() => import("./liquid-C9KVysk1.js"), true ? [] : void 0, import.meta.url)
|
|
209195
209215
|
});
|
|
209196
209216
|
registerLanguage({
|
|
209197
209217
|
id: "m3",
|
|
@@ -209209,7 +209229,7 @@ registerLanguage({
|
|
|
209209
209229
|
id: "mdx",
|
|
209210
209230
|
extensions: [".mdx"],
|
|
209211
209231
|
aliases: ["MDX", "mdx"],
|
|
209212
|
-
loader: () => __vitePreload(() => import("./mdx-
|
|
209232
|
+
loader: () => __vitePreload(() => import("./mdx-ascmBFXX.js"), true ? [] : void 0, import.meta.url)
|
|
209213
209233
|
});
|
|
209214
209234
|
registerLanguage({
|
|
209215
209235
|
id: "mips",
|
|
@@ -209308,7 +209328,7 @@ registerLanguage({
|
|
|
209308
209328
|
extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
|
|
209309
209329
|
aliases: ["Python", "py"],
|
|
209310
209330
|
firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
|
|
209311
|
-
loader: () => __vitePreload(() => import("./python-
|
|
209331
|
+
loader: () => __vitePreload(() => import("./python-DcbM4Hy5.js"), true ? [] : void 0, import.meta.url)
|
|
209312
209332
|
});
|
|
209313
209333
|
registerLanguage({
|
|
209314
209334
|
id: "qsharp",
|
|
@@ -209327,7 +209347,7 @@ registerLanguage({
|
|
|
209327
209347
|
extensions: [".cshtml"],
|
|
209328
209348
|
aliases: ["Razor", "razor"],
|
|
209329
209349
|
mimetypes: ["text/x-cshtml"],
|
|
209330
|
-
loader: () => __vitePreload(() => import("./razor-
|
|
209350
|
+
loader: () => __vitePreload(() => import("./razor-XJROxpYK.js"), true ? [] : void 0, import.meta.url)
|
|
209331
209351
|
});
|
|
209332
209352
|
registerLanguage({
|
|
209333
209353
|
id: "redis",
|
|
@@ -209460,7 +209480,7 @@ registerLanguage({
|
|
|
209460
209480
|
aliases: ["TypeScript", "ts", "typescript"],
|
|
209461
209481
|
mimetypes: ["text/typescript"],
|
|
209462
209482
|
loader: () => {
|
|
209463
|
-
return __vitePreload(() => import("./typescript-
|
|
209483
|
+
return __vitePreload(() => import("./typescript-DhTtFBLt.js"), true ? [] : void 0, import.meta.url);
|
|
209464
209484
|
}
|
|
209465
209485
|
});
|
|
209466
209486
|
registerLanguage({
|
|
@@ -209505,14 +209525,14 @@ registerLanguage({
|
|
|
209505
209525
|
firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
|
|
209506
209526
|
aliases: ["XML", "xml"],
|
|
209507
209527
|
mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
|
|
209508
|
-
loader: () => __vitePreload(() => import("./xml-
|
|
209528
|
+
loader: () => __vitePreload(() => import("./xml-BLfEkVl1.js"), true ? [] : void 0, import.meta.url)
|
|
209509
209529
|
});
|
|
209510
209530
|
registerLanguage({
|
|
209511
209531
|
id: "yaml",
|
|
209512
209532
|
extensions: [".yaml", ".yml"],
|
|
209513
209533
|
aliases: ["YAML", "yaml", "YML", "yml"],
|
|
209514
209534
|
mimetypes: ["application/x-yaml", "text/x-yaml"],
|
|
209515
|
-
loader: () => __vitePreload(() => import("./yaml-
|
|
209535
|
+
loader: () => __vitePreload(() => import("./yaml-DpzsqvWa.js"), true ? [] : void 0, import.meta.url)
|
|
209516
209536
|
});
|
|
209517
209537
|
var __defProp = Object.defineProperty;
|
|
209518
209538
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -213286,11 +213306,14 @@ function RemoteConnect({ onConnect, onBack }) {
|
|
|
213286
213306
|
] })
|
|
213287
213307
|
] }) });
|
|
213288
213308
|
}
|
|
213289
|
-
function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
213290
|
-
const
|
|
213309
|
+
function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd, onCancel, restarting = false }) {
|
|
213310
|
+
const [selectedCwd, setSelectedCwd] = reactExports.useState(cwd2);
|
|
213311
|
+
const displayPath = selectedCwd.replace(/^\/Users\/[^/]+/, "~");
|
|
213291
213312
|
const [logChat, setLogChat] = reactExports.useState(false);
|
|
213292
213313
|
const [loaded, setLoaded] = reactExports.useState(false);
|
|
213293
213314
|
const [remoteMode, setRemoteMode] = reactExports.useState(false);
|
|
213315
|
+
const [handoff, setHandoff] = reactExports.useState("fresh");
|
|
213316
|
+
const [switching, setSwitching] = reactExports.useState(false);
|
|
213294
213317
|
reactExports.useEffect(() => {
|
|
213295
213318
|
let cancelled = false;
|
|
213296
213319
|
window.worker.getLogChat().then((r) => {
|
|
@@ -213311,20 +213334,30 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
|
213311
213334
|
}
|
|
213312
213335
|
};
|
|
213313
213336
|
const handleSelect = async (agent) => {
|
|
213337
|
+
if (switching) return;
|
|
213338
|
+
setSwitching(true);
|
|
213314
213339
|
if (loaded) {
|
|
213315
213340
|
try {
|
|
213316
213341
|
await window.worker.setLogChat(logChat);
|
|
213317
213342
|
} catch {
|
|
213318
213343
|
}
|
|
213319
213344
|
}
|
|
213320
|
-
|
|
213345
|
+
try {
|
|
213346
|
+
await onSelect(agent, selectedCwd, handoff);
|
|
213347
|
+
} finally {
|
|
213348
|
+
setSwitching(false);
|
|
213349
|
+
}
|
|
213350
|
+
};
|
|
213351
|
+
const changeCwd = async () => {
|
|
213352
|
+
const next = await onChangeCwd();
|
|
213353
|
+
if (next) setSelectedCwd(next);
|
|
213321
213354
|
};
|
|
213322
213355
|
if (remoteMode) {
|
|
213323
213356
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
213324
213357
|
RemoteConnect,
|
|
213325
213358
|
{
|
|
213326
213359
|
onConnect: (agent) => {
|
|
213327
|
-
handleSelect(agent);
|
|
213360
|
+
void handleSelect(agent);
|
|
213328
213361
|
},
|
|
213329
213362
|
onBack: () => setRemoteMode(false)
|
|
213330
213363
|
}
|
|
@@ -213332,18 +213365,55 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
|
213332
213365
|
}
|
|
213333
213366
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
|
|
213334
213367
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
|
|
213335
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-cwd", onClick:
|
|
213368
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-cwd", onClick: () => void changeCwd(), title: "Click to change directory", children: [
|
|
213336
213369
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-cwd-icon", children: "📂" }),
|
|
213337
213370
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-cwd-path", children: displayPath }),
|
|
213338
213371
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-cwd-change", children: "change" })
|
|
213339
213372
|
] }),
|
|
213340
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: "SELECT AGENT" }),
|
|
213373
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: restarting ? "CHANGE SESSION" : "SELECT AGENT" }),
|
|
213374
|
+
restarting && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
213375
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-warning", children: "Choosing an agent restarts this terminal and ends its current process." }),
|
|
213376
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("fieldset", { className: "agent-handoff-options", children: [
|
|
213377
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("legend", { children: "NEW SESSION CONTEXT" }),
|
|
213378
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: handoff === "fresh" ? "selected" : "", children: [
|
|
213379
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
213380
|
+
"input",
|
|
213381
|
+
{
|
|
213382
|
+
type: "radio",
|
|
213383
|
+
name: "session-handoff",
|
|
213384
|
+
checked: handoff === "fresh",
|
|
213385
|
+
onChange: () => setHandoff("fresh")
|
|
213386
|
+
}
|
|
213387
|
+
),
|
|
213388
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
213389
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Start fresh" }),
|
|
213390
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("small", { children: "Do not send anything from this terminal." })
|
|
213391
|
+
] })
|
|
213392
|
+
] }),
|
|
213393
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: handoff === "context" ? "selected" : "", children: [
|
|
213394
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
213395
|
+
"input",
|
|
213396
|
+
{
|
|
213397
|
+
type: "radio",
|
|
213398
|
+
name: "session-handoff",
|
|
213399
|
+
checked: handoff === "context",
|
|
213400
|
+
onChange: () => setHandoff("context")
|
|
213401
|
+
}
|
|
213402
|
+
),
|
|
213403
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
213404
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Share previous context" }),
|
|
213405
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("small", { children: "Quote it inline, or use a temporary file when the session is long." })
|
|
213406
|
+
] })
|
|
213407
|
+
] })
|
|
213408
|
+
] })
|
|
213409
|
+
] }),
|
|
213341
213410
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-list", children: [
|
|
213342
213411
|
agents.map((a) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
213343
213412
|
"button",
|
|
213344
213413
|
{
|
|
213345
213414
|
className: "agent-picker-item",
|
|
213346
|
-
|
|
213415
|
+
disabled: switching,
|
|
213416
|
+
onClick: () => void handleSelect(a),
|
|
213347
213417
|
children: [
|
|
213348
213418
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: a.name }),
|
|
213349
213419
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: a.description })
|
|
@@ -213355,6 +213425,7 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
|
213355
213425
|
"button",
|
|
213356
213426
|
{
|
|
213357
213427
|
className: "agent-picker-item",
|
|
213428
|
+
disabled: switching,
|
|
213358
213429
|
onClick: () => setRemoteMode(true),
|
|
213359
213430
|
children: [
|
|
213360
213431
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "🌐 Remote (SSH)" }),
|
|
@@ -213374,15 +213445,17 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
|
213374
213445
|
),
|
|
213375
213446
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Log chat to ctlsurf" })
|
|
213376
213447
|
] }),
|
|
213377
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-hint", children: "Select an agent to start" })
|
|
213448
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-hint", children: restarting ? "Select an agent to restart in the directory above" : "Select an agent to start" }),
|
|
213449
|
+
onCancel && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", disabled: switching, onClick: onCancel, children: "Cancel" })
|
|
213378
213450
|
] }) });
|
|
213379
213451
|
}
|
|
213380
|
-
function ProjectPicker({ onDone }) {
|
|
213452
|
+
function ProjectPicker({ onDone, currentProject }) {
|
|
213381
213453
|
const [state, setState2] = reactExports.useState("loading");
|
|
213382
213454
|
const [projects, setProjects] = reactExports.useState([]);
|
|
213383
213455
|
const [errorMsg, setErrorMsg] = reactExports.useState("");
|
|
213384
213456
|
const [binding, setBinding] = reactExports.useState(false);
|
|
213385
213457
|
const [filter, setFilter] = reactExports.useState("");
|
|
213458
|
+
const [notifyAgent, setNotifyAgent] = reactExports.useState(true);
|
|
213386
213459
|
reactExports.useEffect(() => {
|
|
213387
213460
|
let cancelled = false;
|
|
213388
213461
|
window.worker.listProjects().then((r) => {
|
|
@@ -213406,23 +213479,36 @@ function ProjectPicker({ onDone }) {
|
|
|
213406
213479
|
const pick = async (p) => {
|
|
213407
213480
|
setBinding(true);
|
|
213408
213481
|
try {
|
|
213409
|
-
await window.worker.bindProject(p.root_path);
|
|
213410
|
-
|
|
213482
|
+
const result = await window.worker.bindProject(p.root_path);
|
|
213483
|
+
if (!result.ok) {
|
|
213484
|
+
setErrorMsg(result.error || "Could not bind project");
|
|
213485
|
+
setState2("error");
|
|
213486
|
+
setBinding(false);
|
|
213487
|
+
return;
|
|
213488
|
+
}
|
|
213489
|
+
onDone({ project: p, notifyAgent });
|
|
213490
|
+
} catch (err) {
|
|
213491
|
+
setErrorMsg(err instanceof Error ? err.message : "Could not bind project");
|
|
213492
|
+
setState2("error");
|
|
213493
|
+
setBinding(false);
|
|
213411
213494
|
}
|
|
213412
|
-
onDone();
|
|
213413
213495
|
};
|
|
213414
213496
|
const shown = filter.trim() ? projects.filter((p) => p.name.toLowerCase().includes(filter.toLowerCase()) || p.root_path.toLowerCase().includes(filter.toLowerCase())) : projects;
|
|
213415
213497
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
|
|
213416
213498
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
|
|
213417
213499
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: "SELECT CTLSURF PROJECT" }),
|
|
213418
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-target-line", children: "Bind
|
|
213500
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-target-line", children: "Bind the active session to a project" }),
|
|
213501
|
+
currentProject && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-note", children: [
|
|
213502
|
+
"Currently linked to ",
|
|
213503
|
+
currentProject
|
|
213504
|
+
] }),
|
|
213419
213505
|
state === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-status", children: [
|
|
213420
213506
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-spinner" }),
|
|
213421
213507
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading projects…" })
|
|
213422
213508
|
] }),
|
|
213423
213509
|
state === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: errorMsg }) }),
|
|
213424
213510
|
state === "ready" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
213425
|
-
projects.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: "No ctlsurf projects found.
|
|
213511
|
+
projects.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: "No ctlsurf projects found." }),
|
|
213426
213512
|
projects.length > 6 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
213427
213513
|
"input",
|
|
213428
213514
|
{
|
|
@@ -213452,7 +213538,22 @@ function ProjectPicker({ onDone }) {
|
|
|
213452
213538
|
p.id
|
|
213453
213539
|
)) })
|
|
213454
213540
|
] }),
|
|
213455
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
213541
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "agent-picker-option project-notify-option", children: [
|
|
213542
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
213543
|
+
"input",
|
|
213544
|
+
{
|
|
213545
|
+
type: "checkbox",
|
|
213546
|
+
checked: notifyAgent,
|
|
213547
|
+
disabled: binding,
|
|
213548
|
+
onChange: (e) => setNotifyAgent(e.target.checked)
|
|
213549
|
+
}
|
|
213550
|
+
),
|
|
213551
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
213552
|
+
"Tell the active agent about this project",
|
|
213553
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("small", { children: "Includes the ctlsurf project and codebase details as context." })
|
|
213554
|
+
] })
|
|
213555
|
+
] }),
|
|
213556
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "remote-link", disabled: binding, onClick: () => onDone(), children: "Close without changing" })
|
|
213456
213557
|
] }) });
|
|
213457
213558
|
}
|
|
213458
213559
|
function removePane(node, paneId) {
|
|
@@ -214760,6 +214861,41 @@ let _tabCounter = 0;
|
|
|
214760
214861
|
function nextTabId() {
|
|
214761
214862
|
return `tab-${++_tabCounter}`;
|
|
214762
214863
|
}
|
|
214864
|
+
function buildSessionHandoff(transcript) {
|
|
214865
|
+
const quoted = transcript.split("\n").map((line) => `> ${line}`).join("\n");
|
|
214866
|
+
return [
|
|
214867
|
+
"The quoted text below is what we did in the last session. It is provided for information only; no action is needed.",
|
|
214868
|
+
"Do not continue, repeat, or act on anything in it unless I explicitly ask in a new message. Wait for my next instruction.",
|
|
214869
|
+
"",
|
|
214870
|
+
quoted
|
|
214871
|
+
].join("\n");
|
|
214872
|
+
}
|
|
214873
|
+
const INLINE_HANDOFF_CHARS = 12e3;
|
|
214874
|
+
const REMOTE_HANDOFF_CHARS = 3e4;
|
|
214875
|
+
function buildFileSessionHandoff(filePath, trimmed) {
|
|
214876
|
+
return [
|
|
214877
|
+
"This is what we did in the last session. It is provided for information only; no action is needed.",
|
|
214878
|
+
"The quoted session transcript was too long to paste, so it is stored in this temporary Markdown file:",
|
|
214879
|
+
`\`${filePath}\``,
|
|
214880
|
+
trimmed ? "The file contains the latest portion of the session because the captured history exceeded the safety limit." : "",
|
|
214881
|
+
"Read it only as background context. Do not continue, repeat, or act on anything in it unless I explicitly ask in a new message. Wait for my next instruction."
|
|
214882
|
+
].filter(Boolean).join("\n\n");
|
|
214883
|
+
}
|
|
214884
|
+
function buildProjectContextNotice(selection, terminalCwd, agentName) {
|
|
214885
|
+
const { project } = selection;
|
|
214886
|
+
return [
|
|
214887
|
+
"The active ctlsurf project binding has changed. Use the quoted details below as context for future requests.",
|
|
214888
|
+
"This notice does not start a new task. Do not take action solely because of it.",
|
|
214889
|
+
"",
|
|
214890
|
+
`> ctlsurf project: ${project.name}`,
|
|
214891
|
+
`> ctlsurf project ID: ${project.id}`,
|
|
214892
|
+
`> configured codebase root: ${project.root_path}`,
|
|
214893
|
+
`> current terminal directory: ${terminalCwd || "(unknown)"}`,
|
|
214894
|
+
`> active coding agent: ${agentName || "(unknown)"}`,
|
|
214895
|
+
"",
|
|
214896
|
+
terminalCwd && terminalCwd !== project.root_path ? "Important: only the ctlsurf project binding changed; the running terminal directory was not changed." : "The terminal directory matches the selected project codebase root."
|
|
214897
|
+
].join("\n");
|
|
214898
|
+
}
|
|
214763
214899
|
function App() {
|
|
214764
214900
|
const [agents, setAgents] = reactExports.useState([]);
|
|
214765
214901
|
const [sessionStart, setSessionStart] = reactExports.useState(null);
|
|
@@ -214774,7 +214910,7 @@ function App() {
|
|
|
214774
214910
|
const ctlsurfNavCounter = reactExports.useRef(0);
|
|
214775
214911
|
const [tabs, setTabs] = reactExports.useState(() => {
|
|
214776
214912
|
const id = nextTabId();
|
|
214777
|
-
return [{ id, label: "Terminal 1", agent: null, agentStatus: "idle" }];
|
|
214913
|
+
return [{ id, label: "Terminal 1", agent: null, agentStatus: "idle", sessionVersion: 0 }];
|
|
214778
214914
|
});
|
|
214779
214915
|
const [activeTabId, setActiveTabId] = reactExports.useState(tabs[0].id);
|
|
214780
214916
|
const [trackingActive, setTrackingActive] = reactExports.useState(false);
|
|
@@ -214887,21 +215023,44 @@ function App() {
|
|
|
214887
215023
|
setTimeout(() => window.worker.writePty(activeTabId, "\r"), 120);
|
|
214888
215024
|
}, [activeTabId]);
|
|
214889
215025
|
const cwdRef = reactExports.useRef(null);
|
|
215026
|
+
const pendingHandoffsRef = reactExports.useRef(/* @__PURE__ */ new Map());
|
|
215027
|
+
const handoffTimersRef = reactExports.useRef(/* @__PURE__ */ new Map());
|
|
215028
|
+
const handoffGenerationsRef = reactExports.useRef(/* @__PURE__ */ new Map());
|
|
214890
215029
|
const handleSpawn = reactExports.useCallback(async (tabId, agent) => {
|
|
215030
|
+
const oldTimer = handoffTimersRef.current.get(tabId);
|
|
215031
|
+
if (oldTimer) clearTimeout(oldTimer);
|
|
215032
|
+
handoffTimersRef.current.delete(tabId);
|
|
215033
|
+
const handoffGeneration = (handoffGenerationsRef.current.get(tabId) || 0) + 1;
|
|
215034
|
+
handoffGenerationsRef.current.set(tabId, handoffGeneration);
|
|
214891
215035
|
setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "active" } : t));
|
|
214892
215036
|
setSessionStart(/* @__PURE__ */ new Date());
|
|
214893
215037
|
const spawnCwd = cwdRef.current || await window.worker.getCwd().catch(() => window.worker.getHomePath());
|
|
214894
215038
|
setCwd(spawnCwd);
|
|
214895
215039
|
cwdRef.current = spawnCwd;
|
|
214896
|
-
|
|
214897
|
-
|
|
214898
|
-
|
|
214899
|
-
|
|
214900
|
-
|
|
214901
|
-
|
|
214902
|
-
|
|
215040
|
+
try {
|
|
215041
|
+
await window.worker.spawnAgent(tabId, agent, spawnCwd);
|
|
215042
|
+
} catch (err) {
|
|
215043
|
+
console.error("[session] Failed to start agent", err);
|
|
215044
|
+
pendingHandoffsRef.current.delete(tabId);
|
|
215045
|
+
setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "exited" } : t));
|
|
215046
|
+
return;
|
|
214903
215047
|
}
|
|
214904
|
-
|
|
215048
|
+
const handoff = pendingHandoffsRef.current.get(tabId);
|
|
215049
|
+
pendingHandoffsRef.current.delete(tabId);
|
|
215050
|
+
if (handoff) {
|
|
215051
|
+
const timer = setTimeout(() => {
|
|
215052
|
+
if (handoffGenerationsRef.current.get(tabId) !== handoffGeneration) return;
|
|
215053
|
+
window.worker.writePty(tabId, handoff);
|
|
215054
|
+
focusTerminal(tabId);
|
|
215055
|
+
setTimeout(() => {
|
|
215056
|
+
if (handoffGenerationsRef.current.get(tabId) === handoffGeneration) {
|
|
215057
|
+
window.worker.writePty(tabId, "\r");
|
|
215058
|
+
}
|
|
215059
|
+
}, 120);
|
|
215060
|
+
}, 1200);
|
|
215061
|
+
handoffTimersRef.current.set(tabId, timer);
|
|
215062
|
+
}
|
|
215063
|
+
}, []);
|
|
214905
215064
|
const handleExit = reactExports.useCallback((tabId) => {
|
|
214906
215065
|
setTimeout(() => {
|
|
214907
215066
|
setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "exited" } : t));
|
|
@@ -214910,36 +215069,84 @@ function App() {
|
|
|
214910
215069
|
const handleChangeCwd = reactExports.useCallback(async () => {
|
|
214911
215070
|
const newCwd = await window.worker.browseCwd();
|
|
214912
215071
|
const activeTab = tabs.find((t) => t.id === activeTabId);
|
|
214913
|
-
if (!newCwd
|
|
215072
|
+
if (!newCwd) return;
|
|
214914
215073
|
cwdRef.current = newCwd;
|
|
214915
215074
|
setCwd(newCwd);
|
|
214916
|
-
|
|
214917
|
-
|
|
214918
|
-
|
|
214919
|
-
await window.worker.spawnAgent(activeTabId, activeTab.agent, newCwd);
|
|
215075
|
+
if (!activeTab?.agent) return;
|
|
215076
|
+
pendingHandoffsRef.current.delete(activeTabId);
|
|
215077
|
+
setTabs((prev) => prev.map((t) => t.id === activeTabId ? { ...t, agentStatus: "idle", sessionVersion: t.sessionVersion + 1 } : t));
|
|
214920
215078
|
}, [tabs, activeTabId]);
|
|
214921
215079
|
const addTab = reactExports.useCallback(() => {
|
|
214922
215080
|
const id = nextTabId();
|
|
214923
215081
|
const num = _tabCounter;
|
|
214924
|
-
setTabs((prev) => [...prev, { id, label: `Terminal ${num}`, agent: null, agentStatus: "idle" }]);
|
|
215082
|
+
setTabs((prev) => [...prev, { id, label: `Terminal ${num}`, agent: null, agentStatus: "idle", sessionVersion: 0 }]);
|
|
214925
215083
|
setActiveTabId(id);
|
|
214926
215084
|
setPickerTargetTabId(id);
|
|
214927
215085
|
setShowAgentPicker(true);
|
|
214928
215086
|
}, []);
|
|
214929
|
-
const handlePickerSelect = reactExports.useCallback((agent) => {
|
|
215087
|
+
const handlePickerSelect = reactExports.useCallback(async (agent, selectedCwd, handoff) => {
|
|
214930
215088
|
const tabId = pickerTargetTabId;
|
|
214931
215089
|
if (tabId) {
|
|
214932
|
-
|
|
214933
|
-
|
|
214934
|
-
|
|
215090
|
+
if (handoff === "context") {
|
|
215091
|
+
const transcript = captureTerminalSession(tabId);
|
|
215092
|
+
if (!transcript) {
|
|
215093
|
+
pendingHandoffsRef.current.delete(tabId);
|
|
215094
|
+
} else if (transcript.length > INLINE_HANDOFF_CHARS && !agent.id.startsWith("remote-")) {
|
|
215095
|
+
const result = await window.worker.createSessionHandoffFile(transcript);
|
|
215096
|
+
if (result.ok && result.path) {
|
|
215097
|
+
pendingHandoffsRef.current.set(tabId, buildFileSessionHandoff(result.path, !!result.trimmed));
|
|
215098
|
+
} else {
|
|
215099
|
+
console.warn("[session] Could not create handoff file; using a shortened inline handoff", result.error);
|
|
215100
|
+
pendingHandoffsRef.current.set(tabId, buildSessionHandoff(transcript.slice(-REMOTE_HANDOFF_CHARS)));
|
|
215101
|
+
}
|
|
215102
|
+
} else {
|
|
215103
|
+
const inline2 = transcript.length > REMOTE_HANDOFF_CHARS ? `[Earlier terminal output omitted.]
|
|
215104
|
+
${transcript.slice(-REMOTE_HANDOFF_CHARS)}` : transcript;
|
|
215105
|
+
pendingHandoffsRef.current.set(tabId, buildSessionHandoff(inline2));
|
|
215106
|
+
}
|
|
215107
|
+
} else {
|
|
215108
|
+
pendingHandoffsRef.current.delete(tabId);
|
|
215109
|
+
}
|
|
215110
|
+
cwdRef.current = selectedCwd;
|
|
215111
|
+
setCwd(selectedCwd);
|
|
215112
|
+
setTabs((prev) => {
|
|
215113
|
+
const matchingAgents = prev.filter((x) => x.agent?.id === agent.id).length;
|
|
215114
|
+
return prev.map((t) => t.id === tabId ? {
|
|
215115
|
+
...t,
|
|
215116
|
+
agent,
|
|
215117
|
+
agentStatus: "idle",
|
|
215118
|
+
label: `${agent.name} ${matchingAgents + (t.agent?.id === agent.id ? 0 : 1)}`,
|
|
215119
|
+
sessionVersion: t.sessionVersion + 1
|
|
215120
|
+
} : t);
|
|
215121
|
+
});
|
|
214935
215122
|
}
|
|
214936
215123
|
setShowAgentPicker(false);
|
|
214937
215124
|
setPickerTargetTabId(null);
|
|
214938
215125
|
if (agent.id.startsWith("remote-")) {
|
|
214939
215126
|
setShowProjectPicker(true);
|
|
214940
215127
|
}
|
|
214941
|
-
}, [pickerTargetTabId
|
|
215128
|
+
}, [pickerTargetTabId]);
|
|
215129
|
+
const openSessionPicker = reactExports.useCallback(() => {
|
|
215130
|
+
setPickerTargetTabId(activeTabId);
|
|
215131
|
+
setShowAgentPicker(true);
|
|
215132
|
+
}, [activeTabId]);
|
|
215133
|
+
const handleProjectPickerDone = reactExports.useCallback(async (selection) => {
|
|
215134
|
+
setShowProjectPicker(false);
|
|
215135
|
+
if (!selection?.notifyAgent) return;
|
|
215136
|
+
const activeAgent = tabs.find((t) => t.id === activeTabId)?.agent;
|
|
215137
|
+
if (!activeAgent) return;
|
|
215138
|
+
const terminalCwd = await window.worker.getActiveTabCwd().catch(() => cwd2);
|
|
215139
|
+
const notice = buildProjectContextNotice(selection, terminalCwd, activeAgent.name);
|
|
215140
|
+
window.worker.writePty(activeTabId, notice);
|
|
215141
|
+
focusTerminal(activeTabId);
|
|
215142
|
+
setTimeout(() => window.worker.writePty(activeTabId, "\r"), 120);
|
|
215143
|
+
}, [activeTabId, cwd2, tabs]);
|
|
214942
215144
|
const closeTab = reactExports.useCallback(async (tabId) => {
|
|
215145
|
+
pendingHandoffsRef.current.delete(tabId);
|
|
215146
|
+
handoffGenerationsRef.current.set(tabId, (handoffGenerationsRef.current.get(tabId) || 0) + 1);
|
|
215147
|
+
const handoffTimer = handoffTimersRef.current.get(tabId);
|
|
215148
|
+
if (handoffTimer) clearTimeout(handoffTimer);
|
|
215149
|
+
handoffTimersRef.current.delete(tabId);
|
|
214943
215150
|
await window.worker.killPty(tabId);
|
|
214944
215151
|
destroyTerminal(tabId);
|
|
214945
215152
|
setTabs((prev) => {
|
|
@@ -214953,7 +215160,7 @@ function App() {
|
|
|
214953
215160
|
setActiveTabId(id);
|
|
214954
215161
|
setPickerTargetTabId(id);
|
|
214955
215162
|
setShowAgentPicker(true);
|
|
214956
|
-
return [{ id, label: `Terminal ${num}`, agent: null, agentStatus: "idle" }];
|
|
215163
|
+
return [{ id, label: `Terminal ${num}`, agent: null, agentStatus: "idle", sessionVersion: 0 }];
|
|
214957
215164
|
}
|
|
214958
215165
|
return next;
|
|
214959
215166
|
});
|
|
@@ -215087,6 +215294,7 @@ function App() {
|
|
|
215087
215294
|
{
|
|
215088
215295
|
tabId: tab.id,
|
|
215089
215296
|
agent: tab.agent,
|
|
215297
|
+
sessionVersion: tab.sessionVersion,
|
|
215090
215298
|
onSpawn: handleSpawn,
|
|
215091
215299
|
onExit: handleExit,
|
|
215092
215300
|
isActive: tab.id === activeTabId
|
|
@@ -215217,19 +215425,28 @@ function App() {
|
|
|
215217
215425
|
),
|
|
215218
215426
|
/* @__PURE__ */ jsxRuntimeExports.jsx(SpeakControls, { remote: activeAgentIsRemote }),
|
|
215219
215427
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
|
|
215220
|
-
|
|
215221
|
-
|
|
215222
|
-
|
|
215223
|
-
"
|
|
215224
|
-
|
|
215225
|
-
|
|
215226
|
-
|
|
215227
|
-
|
|
215228
|
-
|
|
215229
|
-
|
|
215230
|
-
|
|
215231
|
-
|
|
215232
|
-
|
|
215428
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
215429
|
+
"button",
|
|
215430
|
+
{
|
|
215431
|
+
className: "titlebar-agent-btn active",
|
|
215432
|
+
onClick: openSessionPicker,
|
|
215433
|
+
title: "Change coding agent or project directory for the active terminal",
|
|
215434
|
+
children: [
|
|
215435
|
+
"Agent: ",
|
|
215436
|
+
tabs.find((t) => t.id === activeTabId)?.agent?.name || "Choose",
|
|
215437
|
+
" ▾"
|
|
215438
|
+
]
|
|
215439
|
+
}
|
|
215440
|
+
),
|
|
215441
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
215442
|
+
"button",
|
|
215443
|
+
{
|
|
215444
|
+
className: "titlebar-btn",
|
|
215445
|
+
onClick: () => setShowProjectPicker(true),
|
|
215446
|
+
title: "Change the ctlsurf project linked to the active session",
|
|
215447
|
+
children: "Project…"
|
|
215448
|
+
}
|
|
215449
|
+
),
|
|
215233
215450
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
|
|
215234
215451
|
ALL_PANE_IDS.map((id) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
215235
215452
|
"button",
|
|
@@ -215260,16 +215477,15 @@ function App() {
|
|
|
215260
215477
|
agents,
|
|
215261
215478
|
cwd: cwd2 || "",
|
|
215262
215479
|
onSelect: handlePickerSelect,
|
|
215263
|
-
onChangeCwd:
|
|
215264
|
-
|
|
215265
|
-
|
|
215266
|
-
|
|
215267
|
-
|
|
215268
|
-
|
|
215269
|
-
}
|
|
215480
|
+
onChangeCwd: () => window.worker.browseCwd(),
|
|
215481
|
+
onCancel: tabs.find((t) => t.id === pickerTargetTabId)?.agent ? () => {
|
|
215482
|
+
setShowAgentPicker(false);
|
|
215483
|
+
setPickerTargetTabId(null);
|
|
215484
|
+
} : void 0,
|
|
215485
|
+
restarting: !!tabs.find((t) => t.id === pickerTargetTabId)?.agent
|
|
215270
215486
|
}
|
|
215271
215487
|
),
|
|
215272
|
-
showProjectPicker && /* @__PURE__ */ jsxRuntimeExports.jsx(ProjectPicker, {
|
|
215488
|
+
showProjectPicker && /* @__PURE__ */ jsxRuntimeExports.jsx(ProjectPicker, { currentProject: projectName, onDone: handleProjectPickerDone }),
|
|
215273
215489
|
showFloatingMic && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
215274
215490
|
FloatingMic,
|
|
215275
215491
|
{
|