@phenx-inc/ctlsurf 0.15.1 → 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-B77M9HlN.js → cssMode-Xx1-SozN.js} +3 -3
- package/out/renderer/assets/{freemarker2-oUT2zvyQ.js → freemarker2-DM49yPw1.js} +1 -1
- package/out/renderer/assets/{handlebars-jCckoi5j.js → handlebars-DJJBblpV.js} +1 -1
- package/out/renderer/assets/{html-kAgrAaKW.js → html-DnMnJOxw.js} +1 -1
- package/out/renderer/assets/{htmlMode-DIWkIawy.js → htmlMode-BG5u2CSH.js} +3 -3
- package/out/renderer/assets/{index-CN3UN7Tm.js → index-D65QIRev.js} +318 -95
- package/out/renderer/assets/{index-3sFJEz7i.css → index-DID1LlMU.css} +88 -0
- package/out/renderer/assets/{javascript-B9GYUE-I.js → javascript-Dci_phmN.js} +2 -2
- package/out/renderer/assets/{jsonMode-BzFKTZjJ.js → jsonMode-Dmp3JJSm.js} +3 -3
- package/out/renderer/assets/{liquid-Ccw-S_Y6.js → liquid-C9KVysk1.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-BE0g7ubH.js → lspLanguageFeatures-IDD0-KWa.js} +1 -1
- package/out/renderer/assets/{mdx-oXeAA0qU.js → mdx-ascmBFXX.js} +1 -1
- package/out/renderer/assets/{python-DlSX3TvA.js → python-DcbM4Hy5.js} +1 -1
- package/out/renderer/assets/{razor-ButO9S7r.js → razor-XJROxpYK.js} +1 -1
- package/out/renderer/assets/{tsMode-CTCpE-67.js → tsMode-EXbzul3n.js} +1 -1
- package/out/renderer/assets/{typescript-9o62WOnS.js → typescript-DhTtFBLt.js} +1 -1
- package/out/renderer/assets/{xml-C4nOH2_D.js → xml-BLfEkVl1.js} +1 -1
- package/out/renderer/assets/{yaml-mXYgKoWW.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/FloatingMic.tsx +15 -11
- package/src/renderer/components/ProjectPicker.tsx +44 -13
- package/src/renderer/components/TerminalPanel.tsx +45 -9
- 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
|
}
|
|
@@ -19532,6 +19532,11 @@ function getOrCreateTerminal(tabId, onExit) {
|
|
|
19532
19532
|
}
|
|
19533
19533
|
const terminal = new xtermExports.Terminal({
|
|
19534
19534
|
cursorBlink: true,
|
|
19535
|
+
// Codex and other modern CLIs often render links as OSC 8 hyperlinks
|
|
19536
|
+
// instead of plain URL text. OSC 8 links are handled by xterm core (not
|
|
19537
|
+
// WebLinksAddon), whose default activation calls window.open(). Route them
|
|
19538
|
+
// through the same embedded-panel handler as detected plain-text URLs.
|
|
19539
|
+
linkHandler: { activate: handleTerminalLink },
|
|
19535
19540
|
// Off by default on macOS; without this a right-click would reselect the
|
|
19536
19541
|
// word under the cursor and clobber a multi-line table selection.
|
|
19537
19542
|
rightClickSelectsWord: false,
|
|
@@ -19594,7 +19599,7 @@ function getOrCreateTerminal(tabId, onExit) {
|
|
|
19594
19599
|
fitAddon,
|
|
19595
19600
|
unsubData: null,
|
|
19596
19601
|
unsubExit: null,
|
|
19597
|
-
|
|
19602
|
+
spawnedSessionKey: null,
|
|
19598
19603
|
pinnedToBottom: true,
|
|
19599
19604
|
anchorLine: null,
|
|
19600
19605
|
userScrollPending: false,
|
|
@@ -19638,6 +19643,25 @@ function getOrCreateTerminal(tabId, onExit) {
|
|
|
19638
19643
|
function focusTerminal(tabId) {
|
|
19639
19644
|
_terminals.get(tabId)?.terminal.focus();
|
|
19640
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
|
+
}
|
|
19641
19665
|
function destroyTerminal(tabId) {
|
|
19642
19666
|
const state = _terminals.get(tabId);
|
|
19643
19667
|
if (!state) return;
|
|
@@ -19646,7 +19670,7 @@ function destroyTerminal(tabId) {
|
|
|
19646
19670
|
state.terminal.dispose();
|
|
19647
19671
|
_terminals.delete(tabId);
|
|
19648
19672
|
}
|
|
19649
|
-
function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
19673
|
+
function TerminalPanel({ tabId, agent, sessionVersion, onSpawn, onExit, isActive }) {
|
|
19650
19674
|
const containerRef = reactExports.useRef(null);
|
|
19651
19675
|
const [menu, setMenu] = reactExports.useState(null);
|
|
19652
19676
|
const [snapOpen, setSnapOpen] = reactExports.useState(false);
|
|
@@ -19736,7 +19760,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
|
19736
19760
|
const attached = _terminals.get(tabId);
|
|
19737
19761
|
if (attached) reassert(attached);
|
|
19738
19762
|
const onWheel = () => markGesture(tabId);
|
|
19739
|
-
terminal.element?.addEventListener("wheel", onWheel, { passive: true });
|
|
19763
|
+
terminal.element?.addEventListener("wheel", onWheel, { capture: true, passive: true });
|
|
19740
19764
|
let resizeTimeout;
|
|
19741
19765
|
const handleResize = () => {
|
|
19742
19766
|
clearTimeout(resizeTimeout);
|
|
@@ -19759,15 +19783,16 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
|
19759
19783
|
clearTimeout(resizeTimeout);
|
|
19760
19784
|
window.removeEventListener("resize", handleResize);
|
|
19761
19785
|
observer.disconnect();
|
|
19762
|
-
terminal.element?.removeEventListener("wheel", onWheel);
|
|
19786
|
+
terminal.element?.removeEventListener("wheel", onWheel, true);
|
|
19763
19787
|
};
|
|
19764
19788
|
}, [tabId, onExit]);
|
|
19765
19789
|
reactExports.useEffect(() => {
|
|
19766
19790
|
if (!agent) return;
|
|
19767
19791
|
const state = _terminals.get(tabId);
|
|
19768
19792
|
if (!state) return;
|
|
19769
|
-
|
|
19770
|
-
state.
|
|
19793
|
+
const sessionKey = `${agent.id}:${sessionVersion}`;
|
|
19794
|
+
if (state.spawnedSessionKey === sessionKey) return;
|
|
19795
|
+
state.spawnedSessionKey = sessionKey;
|
|
19771
19796
|
state.terminal.clear();
|
|
19772
19797
|
onSpawn(tabId, agent).then(() => {
|
|
19773
19798
|
const s = _terminals.get(tabId);
|
|
@@ -19780,7 +19805,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
|
19780
19805
|
}
|
|
19781
19806
|
state.terminal.focus();
|
|
19782
19807
|
});
|
|
19783
|
-
}, [tabId, agent, onSpawn]);
|
|
19808
|
+
}, [tabId, agent, sessionVersion, onSpawn]);
|
|
19784
19809
|
reactExports.useEffect(() => {
|
|
19785
19810
|
if (isActive) {
|
|
19786
19811
|
window.worker.setActiveTab(tabId);
|
|
@@ -20318,7 +20343,7 @@ function VoiceInput({ onTranscript, variant = "titlebar" }) {
|
|
|
20318
20343
|
const POS_KEY = "ctlsurf.floatingMicPos";
|
|
20319
20344
|
const EDGE = 20;
|
|
20320
20345
|
const TOP_MIN = 46;
|
|
20321
|
-
const BOTTOM_GAP =
|
|
20346
|
+
const BOTTOM_GAP = 72;
|
|
20322
20347
|
function loadPos() {
|
|
20323
20348
|
try {
|
|
20324
20349
|
const raw = localStorage.getItem(POS_KEY);
|
|
@@ -20366,15 +20391,17 @@ function FloatingMic({ onTranscript, onHide }) {
|
|
|
20366
20391
|
};
|
|
20367
20392
|
}, []);
|
|
20368
20393
|
reactExports.useEffect(() => {
|
|
20369
|
-
|
|
20370
|
-
|
|
20371
|
-
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20394
|
+
setPos((current) => {
|
|
20395
|
+
if (current) return clamp2(current.x, current.y);
|
|
20396
|
+
const el = elRef.current;
|
|
20397
|
+
const w = el?.offsetWidth ?? 64;
|
|
20398
|
+
const h2 = el?.offsetHeight ?? 90;
|
|
20399
|
+
return clamp2(
|
|
20400
|
+
window.innerWidth - w - EDGE,
|
|
20401
|
+
window.innerHeight - h2 - BOTTOM_GAP
|
|
20402
|
+
);
|
|
20376
20403
|
});
|
|
20377
|
-
}, [
|
|
20404
|
+
}, [clamp2]);
|
|
20378
20405
|
reactExports.useEffect(() => {
|
|
20379
20406
|
const onResize = () => setPos((p) => p ? clamp2(p.x, p.y) : p);
|
|
20380
20407
|
window.addEventListener("resize", onResize);
|
|
@@ -208416,7 +208443,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
|
|
|
208416
208443
|
modeConfigurationDefault$2
|
|
208417
208444
|
);
|
|
208418
208445
|
function getMode$3() {
|
|
208419
|
-
return __vitePreload(() => import("./cssMode-
|
|
208446
|
+
return __vitePreload(() => import("./cssMode-Xx1-SozN.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
|
|
208420
208447
|
}
|
|
208421
208448
|
languages.onLanguage("less", () => {
|
|
208422
208449
|
getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
|
|
@@ -208521,7 +208548,7 @@ const razorLanguageService = registerHTMLLanguageService(
|
|
|
208521
208548
|
);
|
|
208522
208549
|
const razorDefaults = razorLanguageService.defaults;
|
|
208523
208550
|
function getMode$2() {
|
|
208524
|
-
return __vitePreload(() => import("./htmlMode-
|
|
208551
|
+
return __vitePreload(() => import("./htmlMode-BG5u2CSH.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
|
|
208525
208552
|
}
|
|
208526
208553
|
function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
|
|
208527
208554
|
const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
|
|
@@ -208605,7 +208632,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
|
|
|
208605
208632
|
);
|
|
208606
208633
|
const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
|
|
208607
208634
|
function getMode$1() {
|
|
208608
|
-
return __vitePreload(() => import("./jsonMode-
|
|
208635
|
+
return __vitePreload(() => import("./jsonMode-Dmp3JJSm.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
|
|
208609
208636
|
}
|
|
208610
208637
|
languages.register({
|
|
208611
208638
|
id: "json",
|
|
@@ -208851,7 +208878,7 @@ const getJavaScriptWorker = () => {
|
|
|
208851
208878
|
return getMode().then((mode) => mode.getJavaScriptWorker());
|
|
208852
208879
|
};
|
|
208853
208880
|
function getMode() {
|
|
208854
|
-
return __vitePreload(() => import("./tsMode-
|
|
208881
|
+
return __vitePreload(() => import("./tsMode-EXbzul3n.js"), true ? [] : void 0, import.meta.url);
|
|
208855
208882
|
}
|
|
208856
208883
|
languages.onLanguage("typescript", () => {
|
|
208857
208884
|
return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
|
|
@@ -209046,49 +209073,49 @@ registerLanguage({
|
|
|
209046
209073
|
extensions: [".ftl", ".ftlh", ".ftlx"],
|
|
209047
209074
|
aliases: ["FreeMarker2", "Apache FreeMarker2"],
|
|
209048
209075
|
loader: () => {
|
|
209049
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209076
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
|
|
209050
209077
|
}
|
|
209051
209078
|
});
|
|
209052
209079
|
registerLanguage({
|
|
209053
209080
|
id: "freemarker2.tag-angle.interpolation-dollar",
|
|
209054
209081
|
aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
|
|
209055
209082
|
loader: () => {
|
|
209056
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209083
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
|
|
209057
209084
|
}
|
|
209058
209085
|
});
|
|
209059
209086
|
registerLanguage({
|
|
209060
209087
|
id: "freemarker2.tag-bracket.interpolation-dollar",
|
|
209061
209088
|
aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
|
|
209062
209089
|
loader: () => {
|
|
209063
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209090
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
|
|
209064
209091
|
}
|
|
209065
209092
|
});
|
|
209066
209093
|
registerLanguage({
|
|
209067
209094
|
id: "freemarker2.tag-angle.interpolation-bracket",
|
|
209068
209095
|
aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
|
|
209069
209096
|
loader: () => {
|
|
209070
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209097
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
|
|
209071
209098
|
}
|
|
209072
209099
|
});
|
|
209073
209100
|
registerLanguage({
|
|
209074
209101
|
id: "freemarker2.tag-bracket.interpolation-bracket",
|
|
209075
209102
|
aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
|
|
209076
209103
|
loader: () => {
|
|
209077
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209104
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
|
|
209078
209105
|
}
|
|
209079
209106
|
});
|
|
209080
209107
|
registerLanguage({
|
|
209081
209108
|
id: "freemarker2.tag-auto.interpolation-dollar",
|
|
209082
209109
|
aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
|
|
209083
209110
|
loader: () => {
|
|
209084
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209111
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
|
|
209085
209112
|
}
|
|
209086
209113
|
});
|
|
209087
209114
|
registerLanguage({
|
|
209088
209115
|
id: "freemarker2.tag-auto.interpolation-bracket",
|
|
209089
209116
|
aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
|
|
209090
209117
|
loader: () => {
|
|
209091
|
-
return __vitePreload(() => import("./freemarker2-
|
|
209118
|
+
return __vitePreload(() => import("./freemarker2-DM49yPw1.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
|
|
209092
209119
|
}
|
|
209093
209120
|
});
|
|
209094
209121
|
registerLanguage({
|
|
@@ -209109,7 +209136,7 @@ registerLanguage({
|
|
|
209109
209136
|
extensions: [".handlebars", ".hbs"],
|
|
209110
209137
|
aliases: ["Handlebars", "handlebars", "hbs"],
|
|
209111
209138
|
mimetypes: ["text/x-handlebars-template"],
|
|
209112
|
-
loader: () => __vitePreload(() => import("./handlebars-
|
|
209139
|
+
loader: () => __vitePreload(() => import("./handlebars-DJJBblpV.js"), true ? [] : void 0, import.meta.url)
|
|
209113
209140
|
});
|
|
209114
209141
|
registerLanguage({
|
|
209115
209142
|
id: "hcl",
|
|
@@ -209122,7 +209149,7 @@ registerLanguage({
|
|
|
209122
209149
|
extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
|
|
209123
209150
|
aliases: ["HTML", "htm", "html", "xhtml"],
|
|
209124
209151
|
mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
|
|
209125
|
-
loader: () => __vitePreload(() => import("./html-
|
|
209152
|
+
loader: () => __vitePreload(() => import("./html-DnMnJOxw.js"), true ? [] : void 0, import.meta.url)
|
|
209126
209153
|
});
|
|
209127
209154
|
registerLanguage({
|
|
209128
209155
|
id: "ini",
|
|
@@ -209145,7 +209172,7 @@ registerLanguage({
|
|
|
209145
209172
|
filenames: ["jakefile"],
|
|
209146
209173
|
aliases: ["JavaScript", "javascript", "js"],
|
|
209147
209174
|
mimetypes: ["text/javascript"],
|
|
209148
|
-
loader: () => __vitePreload(() => import("./javascript-
|
|
209175
|
+
loader: () => __vitePreload(() => import("./javascript-Dci_phmN.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
|
|
209149
209176
|
});
|
|
209150
209177
|
registerLanguage({
|
|
209151
209178
|
id: "julia",
|
|
@@ -209184,7 +209211,7 @@ registerLanguage({
|
|
|
209184
209211
|
extensions: [".liquid", ".html.liquid"],
|
|
209185
209212
|
aliases: ["Liquid", "liquid"],
|
|
209186
209213
|
mimetypes: ["application/liquid"],
|
|
209187
|
-
loader: () => __vitePreload(() => import("./liquid-
|
|
209214
|
+
loader: () => __vitePreload(() => import("./liquid-C9KVysk1.js"), true ? [] : void 0, import.meta.url)
|
|
209188
209215
|
});
|
|
209189
209216
|
registerLanguage({
|
|
209190
209217
|
id: "m3",
|
|
@@ -209202,7 +209229,7 @@ registerLanguage({
|
|
|
209202
209229
|
id: "mdx",
|
|
209203
209230
|
extensions: [".mdx"],
|
|
209204
209231
|
aliases: ["MDX", "mdx"],
|
|
209205
|
-
loader: () => __vitePreload(() => import("./mdx-
|
|
209232
|
+
loader: () => __vitePreload(() => import("./mdx-ascmBFXX.js"), true ? [] : void 0, import.meta.url)
|
|
209206
209233
|
});
|
|
209207
209234
|
registerLanguage({
|
|
209208
209235
|
id: "mips",
|
|
@@ -209301,7 +209328,7 @@ registerLanguage({
|
|
|
209301
209328
|
extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
|
|
209302
209329
|
aliases: ["Python", "py"],
|
|
209303
209330
|
firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
|
|
209304
|
-
loader: () => __vitePreload(() => import("./python-
|
|
209331
|
+
loader: () => __vitePreload(() => import("./python-DcbM4Hy5.js"), true ? [] : void 0, import.meta.url)
|
|
209305
209332
|
});
|
|
209306
209333
|
registerLanguage({
|
|
209307
209334
|
id: "qsharp",
|
|
@@ -209320,7 +209347,7 @@ registerLanguage({
|
|
|
209320
209347
|
extensions: [".cshtml"],
|
|
209321
209348
|
aliases: ["Razor", "razor"],
|
|
209322
209349
|
mimetypes: ["text/x-cshtml"],
|
|
209323
|
-
loader: () => __vitePreload(() => import("./razor-
|
|
209350
|
+
loader: () => __vitePreload(() => import("./razor-XJROxpYK.js"), true ? [] : void 0, import.meta.url)
|
|
209324
209351
|
});
|
|
209325
209352
|
registerLanguage({
|
|
209326
209353
|
id: "redis",
|
|
@@ -209453,7 +209480,7 @@ registerLanguage({
|
|
|
209453
209480
|
aliases: ["TypeScript", "ts", "typescript"],
|
|
209454
209481
|
mimetypes: ["text/typescript"],
|
|
209455
209482
|
loader: () => {
|
|
209456
|
-
return __vitePreload(() => import("./typescript-
|
|
209483
|
+
return __vitePreload(() => import("./typescript-DhTtFBLt.js"), true ? [] : void 0, import.meta.url);
|
|
209457
209484
|
}
|
|
209458
209485
|
});
|
|
209459
209486
|
registerLanguage({
|
|
@@ -209498,14 +209525,14 @@ registerLanguage({
|
|
|
209498
209525
|
firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
|
|
209499
209526
|
aliases: ["XML", "xml"],
|
|
209500
209527
|
mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
|
|
209501
|
-
loader: () => __vitePreload(() => import("./xml-
|
|
209528
|
+
loader: () => __vitePreload(() => import("./xml-BLfEkVl1.js"), true ? [] : void 0, import.meta.url)
|
|
209502
209529
|
});
|
|
209503
209530
|
registerLanguage({
|
|
209504
209531
|
id: "yaml",
|
|
209505
209532
|
extensions: [".yaml", ".yml"],
|
|
209506
209533
|
aliases: ["YAML", "yaml", "YML", "yml"],
|
|
209507
209534
|
mimetypes: ["application/x-yaml", "text/x-yaml"],
|
|
209508
|
-
loader: () => __vitePreload(() => import("./yaml-
|
|
209535
|
+
loader: () => __vitePreload(() => import("./yaml-DpzsqvWa.js"), true ? [] : void 0, import.meta.url)
|
|
209509
209536
|
});
|
|
209510
209537
|
var __defProp = Object.defineProperty;
|
|
209511
209538
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -213279,11 +213306,14 @@ function RemoteConnect({ onConnect, onBack }) {
|
|
|
213279
213306
|
] })
|
|
213280
213307
|
] }) });
|
|
213281
213308
|
}
|
|
213282
|
-
function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
213283
|
-
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\/[^/]+/, "~");
|
|
213284
213312
|
const [logChat, setLogChat] = reactExports.useState(false);
|
|
213285
213313
|
const [loaded, setLoaded] = reactExports.useState(false);
|
|
213286
213314
|
const [remoteMode, setRemoteMode] = reactExports.useState(false);
|
|
213315
|
+
const [handoff, setHandoff] = reactExports.useState("fresh");
|
|
213316
|
+
const [switching, setSwitching] = reactExports.useState(false);
|
|
213287
213317
|
reactExports.useEffect(() => {
|
|
213288
213318
|
let cancelled = false;
|
|
213289
213319
|
window.worker.getLogChat().then((r) => {
|
|
@@ -213304,20 +213334,30 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
|
213304
213334
|
}
|
|
213305
213335
|
};
|
|
213306
213336
|
const handleSelect = async (agent) => {
|
|
213337
|
+
if (switching) return;
|
|
213338
|
+
setSwitching(true);
|
|
213307
213339
|
if (loaded) {
|
|
213308
213340
|
try {
|
|
213309
213341
|
await window.worker.setLogChat(logChat);
|
|
213310
213342
|
} catch {
|
|
213311
213343
|
}
|
|
213312
213344
|
}
|
|
213313
|
-
|
|
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);
|
|
213314
213354
|
};
|
|
213315
213355
|
if (remoteMode) {
|
|
213316
213356
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
213317
213357
|
RemoteConnect,
|
|
213318
213358
|
{
|
|
213319
213359
|
onConnect: (agent) => {
|
|
213320
|
-
handleSelect(agent);
|
|
213360
|
+
void handleSelect(agent);
|
|
213321
213361
|
},
|
|
213322
213362
|
onBack: () => setRemoteMode(false)
|
|
213323
213363
|
}
|
|
@@ -213325,18 +213365,55 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
|
213325
213365
|
}
|
|
213326
213366
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
|
|
213327
213367
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
|
|
213328
|
-
/* @__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: [
|
|
213329
213369
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-cwd-icon", children: "📂" }),
|
|
213330
213370
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-cwd-path", children: displayPath }),
|
|
213331
213371
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-cwd-change", children: "change" })
|
|
213332
213372
|
] }),
|
|
213333
|
-
/* @__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
|
+
] }),
|
|
213334
213410
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-list", children: [
|
|
213335
213411
|
agents.map((a) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
213336
213412
|
"button",
|
|
213337
213413
|
{
|
|
213338
213414
|
className: "agent-picker-item",
|
|
213339
|
-
|
|
213415
|
+
disabled: switching,
|
|
213416
|
+
onClick: () => void handleSelect(a),
|
|
213340
213417
|
children: [
|
|
213341
213418
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: a.name }),
|
|
213342
213419
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-desc", children: a.description })
|
|
@@ -213348,6 +213425,7 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
|
213348
213425
|
"button",
|
|
213349
213426
|
{
|
|
213350
213427
|
className: "agent-picker-item",
|
|
213428
|
+
disabled: switching,
|
|
213351
213429
|
onClick: () => setRemoteMode(true),
|
|
213352
213430
|
children: [
|
|
213353
213431
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "agent-picker-name", children: "🌐 Remote (SSH)" }),
|
|
@@ -213367,15 +213445,17 @@ function AgentPicker({ agents, cwd: cwd2, onSelect, onChangeCwd }) {
|
|
|
213367
213445
|
),
|
|
213368
213446
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Log chat to ctlsurf" })
|
|
213369
213447
|
] }),
|
|
213370
|
-
/* @__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" })
|
|
213371
213450
|
] }) });
|
|
213372
213451
|
}
|
|
213373
|
-
function ProjectPicker({ onDone }) {
|
|
213452
|
+
function ProjectPicker({ onDone, currentProject }) {
|
|
213374
213453
|
const [state, setState2] = reactExports.useState("loading");
|
|
213375
213454
|
const [projects, setProjects] = reactExports.useState([]);
|
|
213376
213455
|
const [errorMsg, setErrorMsg] = reactExports.useState("");
|
|
213377
213456
|
const [binding, setBinding] = reactExports.useState(false);
|
|
213378
213457
|
const [filter, setFilter] = reactExports.useState("");
|
|
213458
|
+
const [notifyAgent, setNotifyAgent] = reactExports.useState(true);
|
|
213379
213459
|
reactExports.useEffect(() => {
|
|
213380
213460
|
let cancelled = false;
|
|
213381
213461
|
window.worker.listProjects().then((r) => {
|
|
@@ -213399,23 +213479,36 @@ function ProjectPicker({ onDone }) {
|
|
|
213399
213479
|
const pick = async (p) => {
|
|
213400
213480
|
setBinding(true);
|
|
213401
213481
|
try {
|
|
213402
|
-
await window.worker.bindProject(p.root_path);
|
|
213403
|
-
|
|
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);
|
|
213404
213494
|
}
|
|
213405
|
-
onDone();
|
|
213406
213495
|
};
|
|
213407
213496
|
const shown = filter.trim() ? projects.filter((p) => p.name.toLowerCase().includes(filter.toLowerCase()) || p.root_path.toLowerCase().includes(filter.toLowerCase())) : projects;
|
|
213408
213497
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "agent-picker-modal", children: [
|
|
213409
213498
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-brand", children: "ctlsurf" }),
|
|
213410
213499
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "agent-picker-title", children: "SELECT CTLSURF PROJECT" }),
|
|
213411
|
-
/* @__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
|
+
] }),
|
|
213412
213505
|
state === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "remote-status", children: [
|
|
213413
213506
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-spinner" }),
|
|
213414
213507
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading projects…" })
|
|
213415
213508
|
] }),
|
|
213416
213509
|
state === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "remote-note", children: errorMsg }) }),
|
|
213417
213510
|
state === "ready" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
213418
|
-
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." }),
|
|
213419
213512
|
projects.length > 6 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
213420
213513
|
"input",
|
|
213421
213514
|
{
|
|
@@ -213445,7 +213538,22 @@ function ProjectPicker({ onDone }) {
|
|
|
213445
213538
|
p.id
|
|
213446
213539
|
)) })
|
|
213447
213540
|
] }),
|
|
213448
|
-
/* @__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" })
|
|
213449
213557
|
] }) });
|
|
213450
213558
|
}
|
|
213451
213559
|
function removePane(node, paneId) {
|
|
@@ -214753,6 +214861,41 @@ let _tabCounter = 0;
|
|
|
214753
214861
|
function nextTabId() {
|
|
214754
214862
|
return `tab-${++_tabCounter}`;
|
|
214755
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
|
+
}
|
|
214756
214899
|
function App() {
|
|
214757
214900
|
const [agents, setAgents] = reactExports.useState([]);
|
|
214758
214901
|
const [sessionStart, setSessionStart] = reactExports.useState(null);
|
|
@@ -214767,7 +214910,7 @@ function App() {
|
|
|
214767
214910
|
const ctlsurfNavCounter = reactExports.useRef(0);
|
|
214768
214911
|
const [tabs, setTabs] = reactExports.useState(() => {
|
|
214769
214912
|
const id = nextTabId();
|
|
214770
|
-
return [{ id, label: "Terminal 1", agent: null, agentStatus: "idle" }];
|
|
214913
|
+
return [{ id, label: "Terminal 1", agent: null, agentStatus: "idle", sessionVersion: 0 }];
|
|
214771
214914
|
});
|
|
214772
214915
|
const [activeTabId, setActiveTabId] = reactExports.useState(tabs[0].id);
|
|
214773
214916
|
const [trackingActive, setTrackingActive] = reactExports.useState(false);
|
|
@@ -214880,21 +215023,44 @@ function App() {
|
|
|
214880
215023
|
setTimeout(() => window.worker.writePty(activeTabId, "\r"), 120);
|
|
214881
215024
|
}, [activeTabId]);
|
|
214882
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());
|
|
214883
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);
|
|
214884
215035
|
setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "active" } : t));
|
|
214885
215036
|
setSessionStart(/* @__PURE__ */ new Date());
|
|
214886
215037
|
const spawnCwd = cwdRef.current || await window.worker.getCwd().catch(() => window.worker.getHomePath());
|
|
214887
215038
|
setCwd(spawnCwd);
|
|
214888
215039
|
cwdRef.current = spawnCwd;
|
|
214889
|
-
|
|
214890
|
-
|
|
214891
|
-
|
|
214892
|
-
|
|
214893
|
-
|
|
214894
|
-
|
|
214895
|
-
|
|
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;
|
|
215047
|
+
}
|
|
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);
|
|
214896
215062
|
}
|
|
214897
|
-
}, [
|
|
215063
|
+
}, []);
|
|
214898
215064
|
const handleExit = reactExports.useCallback((tabId) => {
|
|
214899
215065
|
setTimeout(() => {
|
|
214900
215066
|
setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "exited" } : t));
|
|
@@ -214903,36 +215069,84 @@ function App() {
|
|
|
214903
215069
|
const handleChangeCwd = reactExports.useCallback(async () => {
|
|
214904
215070
|
const newCwd = await window.worker.browseCwd();
|
|
214905
215071
|
const activeTab = tabs.find((t) => t.id === activeTabId);
|
|
214906
|
-
if (!newCwd
|
|
215072
|
+
if (!newCwd) return;
|
|
214907
215073
|
cwdRef.current = newCwd;
|
|
214908
215074
|
setCwd(newCwd);
|
|
214909
|
-
|
|
214910
|
-
|
|
214911
|
-
|
|
214912
|
-
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));
|
|
214913
215078
|
}, [tabs, activeTabId]);
|
|
214914
215079
|
const addTab = reactExports.useCallback(() => {
|
|
214915
215080
|
const id = nextTabId();
|
|
214916
215081
|
const num = _tabCounter;
|
|
214917
|
-
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 }]);
|
|
214918
215083
|
setActiveTabId(id);
|
|
214919
215084
|
setPickerTargetTabId(id);
|
|
214920
215085
|
setShowAgentPicker(true);
|
|
214921
215086
|
}, []);
|
|
214922
|
-
const handlePickerSelect = reactExports.useCallback((agent) => {
|
|
215087
|
+
const handlePickerSelect = reactExports.useCallback(async (agent, selectedCwd, handoff) => {
|
|
214923
215088
|
const tabId = pickerTargetTabId;
|
|
214924
215089
|
if (tabId) {
|
|
214925
|
-
|
|
214926
|
-
|
|
214927
|
-
|
|
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
|
+
});
|
|
214928
215122
|
}
|
|
214929
215123
|
setShowAgentPicker(false);
|
|
214930
215124
|
setPickerTargetTabId(null);
|
|
214931
215125
|
if (agent.id.startsWith("remote-")) {
|
|
214932
215126
|
setShowProjectPicker(true);
|
|
214933
215127
|
}
|
|
214934
|
-
}, [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]);
|
|
214935
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);
|
|
214936
215150
|
await window.worker.killPty(tabId);
|
|
214937
215151
|
destroyTerminal(tabId);
|
|
214938
215152
|
setTabs((prev) => {
|
|
@@ -214946,7 +215160,7 @@ function App() {
|
|
|
214946
215160
|
setActiveTabId(id);
|
|
214947
215161
|
setPickerTargetTabId(id);
|
|
214948
215162
|
setShowAgentPicker(true);
|
|
214949
|
-
return [{ id, label: `Terminal ${num}`, agent: null, agentStatus: "idle" }];
|
|
215163
|
+
return [{ id, label: `Terminal ${num}`, agent: null, agentStatus: "idle", sessionVersion: 0 }];
|
|
214950
215164
|
}
|
|
214951
215165
|
return next;
|
|
214952
215166
|
});
|
|
@@ -215080,6 +215294,7 @@ function App() {
|
|
|
215080
215294
|
{
|
|
215081
215295
|
tabId: tab.id,
|
|
215082
215296
|
agent: tab.agent,
|
|
215297
|
+
sessionVersion: tab.sessionVersion,
|
|
215083
215298
|
onSpawn: handleSpawn,
|
|
215084
215299
|
onExit: handleExit,
|
|
215085
215300
|
isActive: tab.id === activeTabId
|
|
@@ -215210,19 +215425,28 @@ function App() {
|
|
|
215210
215425
|
),
|
|
215211
215426
|
/* @__PURE__ */ jsxRuntimeExports.jsx(SpeakControls, { remote: activeAgentIsRemote }),
|
|
215212
215427
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
|
|
215213
|
-
|
|
215214
|
-
|
|
215215
|
-
|
|
215216
|
-
"
|
|
215217
|
-
|
|
215218
|
-
|
|
215219
|
-
|
|
215220
|
-
|
|
215221
|
-
|
|
215222
|
-
|
|
215223
|
-
|
|
215224
|
-
|
|
215225
|
-
|
|
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
|
+
),
|
|
215226
215450
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
|
|
215227
215451
|
ALL_PANE_IDS.map((id) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
215228
215452
|
"button",
|
|
@@ -215253,16 +215477,15 @@ function App() {
|
|
|
215253
215477
|
agents,
|
|
215254
215478
|
cwd: cwd2 || "",
|
|
215255
215479
|
onSelect: handlePickerSelect,
|
|
215256
|
-
onChangeCwd:
|
|
215257
|
-
|
|
215258
|
-
|
|
215259
|
-
|
|
215260
|
-
|
|
215261
|
-
|
|
215262
|
-
}
|
|
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
|
|
215263
215486
|
}
|
|
215264
215487
|
),
|
|
215265
|
-
showProjectPicker && /* @__PURE__ */ jsxRuntimeExports.jsx(ProjectPicker, {
|
|
215488
|
+
showProjectPicker && /* @__PURE__ */ jsxRuntimeExports.jsx(ProjectPicker, { currentProject: projectName, onDone: handleProjectPickerDone }),
|
|
215266
215489
|
showFloatingMic && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
215267
215490
|
FloatingMic,
|
|
215268
215491
|
{
|