@phenx-inc/ctlsurf 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/out/headless/index.mjs +29 -19
  2. package/out/headless/index.mjs.map +2 -2
  3. package/out/main/index.js +35 -19
  4. package/out/renderer/assets/{cssMode-R8fTFLae.js → cssMode-KhYyY-BM.js} +3 -3
  5. package/out/renderer/assets/{freemarker2-CyjWzWoP.js → freemarker2-BFsSgcWk.js} +1 -1
  6. package/out/renderer/assets/{handlebars-CrhCGIRM.js → handlebars-Be6_aBsm.js} +1 -1
  7. package/out/renderer/assets/{html-Cc3dPNZr.js → html-G8mvsyfc.js} +1 -1
  8. package/out/renderer/assets/{htmlMode-BWtG6ywk.js → htmlMode-Cfd5VCjw.js} +3 -3
  9. package/out/renderer/assets/{index-BL9njXIL.css → index-Bi_MjBqs.css} +233 -8
  10. package/out/renderer/assets/{index-kkG56aKv.js → index-MqgBcJzy.js} +392 -120
  11. package/out/renderer/assets/{javascript-C8T_SdLC.js → javascript-XyDWi6dC.js} +2 -2
  12. package/out/renderer/assets/{jsonMode-D1zR4VVU.js → jsonMode-DZT5gBVW.js} +3 -3
  13. package/out/renderer/assets/{liquid-COSMe9Uf.js → liquid-BuajCNJ9.js} +1 -1
  14. package/out/renderer/assets/{lspLanguageFeatures-CXGfrtzl.js → lspLanguageFeatures-BYkg2Yc-.js} +1 -1
  15. package/out/renderer/assets/{mdx-RfS44T-L.js → mdx-D0Qx2GMS.js} +1 -1
  16. package/out/renderer/assets/{python-DFSWKHUu.js → python-ChncNy6S.js} +1 -1
  17. package/out/renderer/assets/{razor-DtH2_uvo.js → razor-C6MUtfaS.js} +1 -1
  18. package/out/renderer/assets/{tsMode-BYIQ-S9Z.js → tsMode-C2aMdzdU.js} +1 -1
  19. package/out/renderer/assets/{typescript-HhQUr1Rb.js → typescript-CB71AtAT.js} +1 -1
  20. package/out/renderer/assets/{xml-KuhgdKfD.js → xml-tHS1Ec9w.js} +1 -1
  21. package/out/renderer/assets/{yaml-W_kzCFH7.js → yaml-CZcp5ueF.js} +1 -1
  22. package/out/renderer/index.html +2 -2
  23. package/package.json +1 -1
  24. package/src/main/index.ts +13 -1
  25. package/src/main/orchestrator.ts +8 -1
  26. package/src/main/ticketStore.ts +26 -20
  27. package/src/renderer/App.tsx +19 -3
  28. package/src/renderer/components/CtlsurfPanel.tsx +169 -35
  29. package/src/renderer/components/SpeakControls.tsx +8 -2
  30. package/src/renderer/components/TerminalPanel.tsx +141 -44
  31. package/src/renderer/components/TicketPanel.tsx +126 -29
  32. package/src/renderer/styles.css +233 -8
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-R8fTFLae.js","./lspLanguageFeatures-CXGfrtzl.js","./htmlMode-BWtG6ywk.js","./jsonMode-D1zR4VVU.js","./javascript-C8T_SdLC.js","./typescript-HhQUr1Rb.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-KhYyY-BM.js","./lspLanguageFeatures-BYkg2Yc-.js","./htmlMode-Cfd5VCjw.js","./jsonMode-DZT5gBVW.js","./javascript-XyDWi6dC.js","./typescript-CB71AtAT.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
  }
@@ -18829,36 +18829,41 @@ function copySelectionAsEmailTable(terminal) {
18829
18829
  })
18830
18830
  );
18831
18831
  }
18832
- function openExternal(uri) {
18833
- const win = window.open();
18834
- if (win) {
18835
- try {
18836
- win.opener = null;
18837
- } catch {
18838
- }
18839
- win.location.href = uri;
18840
- }
18841
- }
18842
18832
  function handleTerminalLink(_event, uri) {
18843
- window.worker.getEmbedUrl(uri).then((embedUrl) => {
18844
- if (embedUrl) {
18845
- window.dispatchEvent(new CustomEvent("ctlsurf-open-url", { detail: { url: embedUrl } }));
18846
- } else {
18847
- openExternal(uri);
18848
- }
18849
- }).catch(() => openExternal(uri));
18833
+ const openInPanel = (url) => window.dispatchEvent(new CustomEvent("ctlsurf-open-url", { detail: { url } }));
18834
+ window.worker.getEmbedUrl(uri).then((embedUrl) => openInPanel(embedUrl || uri)).catch(() => openInPanel(uri));
18850
18835
  }
18851
18836
  const _terminals = /* @__PURE__ */ new Map();
18852
18837
  function isAtBottom(terminal) {
18853
18838
  const buf = terminal.buffer.active;
18854
- return buf.viewportY >= buf.baseY;
18839
+ return buf.baseY - buf.viewportY <= 2;
18855
18840
  }
18856
- function scrollIfPinned(tabId, terminal) {
18857
- const state = _terminals.get(tabId);
18858
- if (state?.pinnedToBottom) {
18859
- terminal.scrollToBottom();
18841
+ function notifyPin(state) {
18842
+ state.pinListeners.forEach((fn) => fn(state.pinnedToBottom));
18843
+ }
18844
+ function setPinned(state, pinned) {
18845
+ if (pinned) state.anchorLine = null;
18846
+ if (state.pinnedToBottom === pinned) return;
18847
+ state.pinnedToBottom = pinned;
18848
+ notifyPin(state);
18849
+ }
18850
+ function reassert(state) {
18851
+ const { terminal } = state;
18852
+ state.holding = true;
18853
+ try {
18854
+ if (state.pinnedToBottom) {
18855
+ terminal.scrollToBottom();
18856
+ } else if (state.anchorLine != null && terminal.buffer.active.viewportY !== state.anchorLine) {
18857
+ terminal.scrollToLine(state.anchorLine);
18858
+ }
18859
+ } finally {
18860
+ state.holding = false;
18860
18861
  }
18861
18862
  }
18863
+ function markGesture(tabId) {
18864
+ const s = _terminals.get(tabId);
18865
+ if (s) s.userScrollPending = true;
18866
+ }
18862
18867
  function getOrCreateTerminal(tabId, onExit) {
18863
18868
  const existing = _terminals.get(tabId);
18864
18869
  if (existing) {
@@ -18902,6 +18907,9 @@ function getOrCreateTerminal(tabId, onExit) {
18902
18907
  terminal.loadAddon(new addonWebLinksExports.WebLinksAddon(handleTerminalLink));
18903
18908
  terminal.attachCustomKeyEventHandler((e) => {
18904
18909
  if (e.type !== "keydown") return true;
18910
+ if (e.key === "PageUp" || e.key === "PageDown" || e.key === "Home" || e.key === "End") {
18911
+ markGesture(tabId);
18912
+ }
18905
18913
  const isCombo = (e.metaKey || e.ctrlKey) && e.shiftKey && e.code === "KeyE";
18906
18914
  if (!isCombo) return true;
18907
18915
  e.preventDefault();
@@ -18914,20 +18922,34 @@ function getOrCreateTerminal(tabId, onExit) {
18914
18922
  unsubData: null,
18915
18923
  unsubExit: null,
18916
18924
  spawnedAgentId: null,
18917
- pinnedToBottom: true
18925
+ pinnedToBottom: true,
18926
+ anchorLine: null,
18927
+ userScrollPending: false,
18928
+ holding: false,
18929
+ pinListeners: /* @__PURE__ */ new Set()
18918
18930
  };
18919
18931
  terminal.onScroll(() => {
18920
- state.pinnedToBottom = isAtBottom(terminal);
18932
+ if (state.holding) return;
18933
+ if (!state.userScrollPending) {
18934
+ reassert(state);
18935
+ return;
18936
+ }
18937
+ state.userScrollPending = false;
18938
+ if (isAtBottom(terminal)) {
18939
+ setPinned(state, true);
18940
+ } else {
18941
+ state.anchorLine = terminal.buffer.active.viewportY;
18942
+ setPinned(state, false);
18943
+ }
18921
18944
  });
18922
18945
  terminal.onData((data) => {
18923
- state.pinnedToBottom = true;
18946
+ setPinned(state, true);
18924
18947
  terminal.scrollToBottom();
18925
18948
  window.worker.writePty(tabId, data);
18926
18949
  });
18927
18950
  state.unsubData = window.worker.onPtyData((eventTabId, data) => {
18928
18951
  if (eventTabId === tabId) {
18929
- terminal.write(data);
18930
- scrollIfPinned(tabId, terminal);
18952
+ terminal.write(data, () => reassert(state));
18931
18953
  }
18932
18954
  });
18933
18955
  state.unsubExit = window.worker.onPtyExit((eventTabId, code) => {
@@ -18955,6 +18977,14 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
18955
18977
  const containerRef = reactExports.useRef(null);
18956
18978
  const [menu, setMenu] = reactExports.useState(null);
18957
18979
  const [toast, setToast] = reactExports.useState(null);
18980
+ const [followBottom, setFollowBottom] = reactExports.useState(true);
18981
+ const jumpToLatest = () => {
18982
+ const s = _terminals.get(tabId);
18983
+ if (!s) return;
18984
+ setPinned(s, true);
18985
+ s.terminal.scrollToBottom();
18986
+ s.terminal.focus();
18987
+ };
18958
18988
  const handleContextMenu = (e) => {
18959
18989
  const sel = _terminals.get(tabId)?.terminal.getSelection() ?? "";
18960
18990
  if (!convertToEmailTable(sel)) return;
@@ -19006,7 +19036,10 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19006
19036
  terminal.open(containerRef.current);
19007
19037
  }
19008
19038
  fitAddon.fit();
19009
- scrollIfPinned(tabId, terminal);
19039
+ const attached = _terminals.get(tabId);
19040
+ if (attached) reassert(attached);
19041
+ const onWheel = () => markGesture(tabId);
19042
+ terminal.element?.addEventListener("wheel", onWheel, { passive: true });
19010
19043
  let resizeTimeout;
19011
19044
  const handleResize = () => {
19012
19045
  clearTimeout(resizeTimeout);
@@ -19018,7 +19051,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19018
19051
  state.fitAddon.fit();
19019
19052
  const { cols, rows } = state.terminal;
19020
19053
  if (cols < 10 || rows < 5) return;
19021
- scrollIfPinned(tabId, state.terminal);
19054
+ reassert(state);
19022
19055
  window.worker.resizePty(tabId, cols, rows);
19023
19056
  }, 150);
19024
19057
  };
@@ -19029,6 +19062,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19029
19062
  clearTimeout(resizeTimeout);
19030
19063
  window.removeEventListener("resize", handleResize);
19031
19064
  observer.disconnect();
19065
+ terminal.element?.removeEventListener("wheel", onWheel);
19032
19066
  };
19033
19067
  }, [tabId, onExit]);
19034
19068
  reactExports.useEffect(() => {
@@ -19042,7 +19076,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19042
19076
  const s = _terminals.get(tabId);
19043
19077
  if (s) {
19044
19078
  s.fitAddon.fit();
19045
- s.pinnedToBottom = true;
19079
+ setPinned(s, true);
19046
19080
  s.terminal.scrollToBottom();
19047
19081
  const { cols, rows } = s.terminal;
19048
19082
  window.worker.resizePty(tabId, cols, rows);
@@ -19059,7 +19093,7 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19059
19093
  if (state) {
19060
19094
  state.fitAddon.fit();
19061
19095
  state.terminal.focus();
19062
- scrollIfPinned(tabId, state.terminal);
19096
+ reassert(state);
19063
19097
  const { cols, rows } = state.terminal;
19064
19098
  window.worker.resizePty(tabId, cols, rows);
19065
19099
  }
@@ -19067,8 +19101,33 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19067
19101
  });
19068
19102
  }
19069
19103
  }, [isActive, tabId]);
19104
+ reactExports.useEffect(() => {
19105
+ const s = _terminals.get(tabId);
19106
+ if (!s) return;
19107
+ setFollowBottom(s.pinnedToBottom);
19108
+ const fn = (p) => setFollowBottom(p);
19109
+ s.pinListeners.add(fn);
19110
+ return () => {
19111
+ s.pinListeners.delete(fn);
19112
+ };
19113
+ }, [tabId]);
19070
19114
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19071
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "terminal-container", ref: containerRef, onContextMenu: handleContextMenu }),
19115
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "terminal-wrap", children: [
19116
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "terminal-container", ref: containerRef, onContextMenu: handleContextMenu }),
19117
+ !followBottom && /* @__PURE__ */ jsxRuntimeExports.jsx(
19118
+ "button",
19119
+ {
19120
+ type: "button",
19121
+ className: "term-jump-latest",
19122
+ onMouseDown: (e) => {
19123
+ e.preventDefault();
19124
+ jumpToLatest();
19125
+ },
19126
+ title: "Jump to latest output",
19127
+ children: "↓ Latest"
19128
+ }
19129
+ )
19130
+ ] }),
19072
19131
  menu && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "term-context-menu", style: { position: "fixed", left: menu.x, top: menu.y }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onMouseDown: (e) => {
19073
19132
  e.preventDefault();
19074
19133
  runConvert();
@@ -20022,7 +20081,7 @@ function FloatingMic({ onTranscript, onHide }) {
20022
20081
  ] });
20023
20082
  }
20024
20083
  const SAMPLE = "This is how spoken agent replies will sound.";
20025
- function SpeakControls() {
20084
+ function SpeakControls({ remote = false }) {
20026
20085
  const [enabled, setEnabled] = reactExports.useState(false);
20027
20086
  const [engine, setEngineState] = reactExports.useState(getEngine);
20028
20087
  const [voices, setVoices] = reactExports.useState([]);
@@ -20114,7 +20173,7 @@ function SpeakControls() {
20114
20173
  speech.stop();
20115
20174
  speech.enqueue(SAMPLE);
20116
20175
  }, []);
20117
- const title = enabled ? "Spoken replies on — click to mute" : "Speak agent replies (off)";
20176
+ const title = remote ? "Spoken replies aren't available for remote SSH sessions — the agent's transcript lives on the remote machine" : enabled ? "Spoken replies on — click to mute" : "Speak agent replies (off)";
20118
20177
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "speak-controls", ref: wrapRef, children: [
20119
20178
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
20120
20179
  "button",
@@ -20123,6 +20182,7 @@ function SpeakControls() {
20123
20182
  className: `titlebar-btn titlebar-icon-btn speak-btn ${enabled ? "active" : ""}`,
20124
20183
  onClick: toggle,
20125
20184
  title,
20185
+ disabled: remote,
20126
20186
  "aria-label": "Toggle spoken agent replies",
20127
20187
  "aria-pressed": enabled,
20128
20188
  children: [
@@ -20247,49 +20307,170 @@ function SpeakControls() {
20247
20307
  error && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "speak-error", children: error })
20248
20308
  ] });
20249
20309
  }
20310
+ function displayUrl(raw) {
20311
+ try {
20312
+ const u = new URL(raw);
20313
+ u.searchParams.delete("_code");
20314
+ u.searchParams.delete("embed");
20315
+ return u.toString();
20316
+ } catch {
20317
+ return raw;
20318
+ }
20319
+ }
20250
20320
  function CtlsurfPanel({ navigate }) {
20251
20321
  const webviewRef = reactExports.useRef(null);
20252
- const [url, setUrl] = reactExports.useState(null);
20322
+ const [src, setSrc] = reactExports.useState(null);
20323
+ const [address, setAddress] = reactExports.useState("");
20324
+ const [loading, setLoading] = reactExports.useState(false);
20325
+ const [nav, setNav] = reactExports.useState({ back: false, forward: false });
20253
20326
  const [key, setKey] = reactExports.useState(0);
20254
- const loadUrl = async () => {
20327
+ const hasSrcRef = reactExports.useRef(false);
20328
+ const readyRef = reactExports.useRef(false);
20329
+ const pendingRef = reactExports.useRef(null);
20330
+ const resolve2 = reactExports.useCallback(async (raw) => {
20331
+ try {
20332
+ return await window.worker.getEmbedUrl(raw) || raw;
20333
+ } catch {
20334
+ return raw;
20335
+ }
20336
+ }, []);
20337
+ const load = reactExports.useCallback((url) => {
20338
+ if (!url) return;
20339
+ setAddress(displayUrl(url));
20340
+ if (!hasSrcRef.current) {
20341
+ hasSrcRef.current = true;
20342
+ setSrc(url);
20343
+ return;
20344
+ }
20345
+ const wv = webviewRef.current;
20346
+ if (wv && readyRef.current) wv.loadURL(url);
20347
+ else pendingRef.current = url;
20348
+ }, []);
20349
+ const loadProject = reactExports.useCallback(async () => {
20255
20350
  try {
20256
20351
  const info = await window.worker.getWebviewInfo();
20257
- setUrl(info.pageUrl || info.frontendUrl);
20352
+ load(info.pageUrl || info.frontendUrl);
20258
20353
  } catch (err) {
20259
20354
  console.error("[ctlsurf] Failed to get webview info:", err);
20260
- setUrl("https://app.ctlsurf.com");
20355
+ load("https://app.ctlsurf.com");
20261
20356
  }
20262
- };
20357
+ }, [load]);
20263
20358
  reactExports.useEffect(() => {
20264
- if (!navigate?.url) loadUrl();
20359
+ if (!navigate?.url) loadProject();
20265
20360
  }, []);
20266
20361
  reactExports.useEffect(() => {
20267
- if (navigate?.url) {
20268
- setUrl(navigate.url);
20269
- setKey((k2) => k2 + 1);
20270
- }
20362
+ if (navigate?.url) resolve2(navigate.url).then(load);
20271
20363
  }, [navigate?.n]);
20272
20364
  reactExports.useEffect(() => {
20273
20365
  const unsub = window.worker.onCwdChanged(() => {
20274
- setUrl(null);
20366
+ hasSrcRef.current = false;
20367
+ readyRef.current = false;
20368
+ pendingRef.current = null;
20369
+ setSrc(null);
20275
20370
  setKey((k2) => k2 + 1);
20276
- loadUrl();
20371
+ loadProject();
20277
20372
  });
20278
20373
  return unsub;
20279
- }, []);
20280
- if (!url) {
20281
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ctlsurf-panel", style: { display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#565f89", fontSize: "13px" }, children: "Loading ctlsurf..." }) });
20282
- }
20283
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ctlsurf-panel", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20284
- "webview",
20285
- {
20286
- ref: webviewRef,
20287
- src: url,
20288
- style: { width: "100%", height: "100%" },
20289
- allowpopups: "true"
20290
- },
20291
- key
20292
- ) });
20374
+ }, [loadProject]);
20375
+ reactExports.useEffect(() => {
20376
+ const wv = webviewRef.current;
20377
+ if (!wv) return;
20378
+ const syncNav = () => setNav({ back: wv.canGoBack?.() ?? false, forward: wv.canGoForward?.() ?? false });
20379
+ const onReady = () => {
20380
+ readyRef.current = true;
20381
+ if (pendingRef.current) {
20382
+ wv.loadURL(pendingRef.current);
20383
+ pendingRef.current = null;
20384
+ }
20385
+ syncNav();
20386
+ };
20387
+ const onNavigate = (e) => {
20388
+ const url = e.url;
20389
+ if (url) setAddress(displayUrl(url));
20390
+ syncNav();
20391
+ };
20392
+ const onStart = () => setLoading(true);
20393
+ const onStop = () => {
20394
+ setLoading(false);
20395
+ syncNav();
20396
+ };
20397
+ wv.addEventListener("dom-ready", onReady);
20398
+ wv.addEventListener("did-navigate", onNavigate);
20399
+ wv.addEventListener("did-navigate-in-page", onNavigate);
20400
+ wv.addEventListener("did-start-loading", onStart);
20401
+ wv.addEventListener("did-stop-loading", onStop);
20402
+ return () => {
20403
+ wv.removeEventListener("dom-ready", onReady);
20404
+ wv.removeEventListener("did-navigate", onNavigate);
20405
+ wv.removeEventListener("did-navigate-in-page", onNavigate);
20406
+ wv.removeEventListener("did-start-loading", onStart);
20407
+ wv.removeEventListener("did-stop-loading", onStop);
20408
+ };
20409
+ }, [src, key]);
20410
+ const onSubmit = (e) => {
20411
+ e.preventDefault();
20412
+ let target = address.trim();
20413
+ if (!target) return;
20414
+ if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(target)) target = "https://" + target;
20415
+ resolve2(target).then(load);
20416
+ };
20417
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ctlsurf-panel", children: [
20418
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ctlsurf-addressbar", children: [
20419
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
20420
+ "button",
20421
+ {
20422
+ type: "button",
20423
+ className: "ctlsurf-nav-btn",
20424
+ title: "Back",
20425
+ disabled: !nav.back,
20426
+ onClick: () => webviewRef.current?.goBack?.(),
20427
+ children: "‹"
20428
+ }
20429
+ ),
20430
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
20431
+ "button",
20432
+ {
20433
+ type: "button",
20434
+ className: "ctlsurf-nav-btn",
20435
+ title: "Forward",
20436
+ disabled: !nav.forward,
20437
+ onClick: () => webviewRef.current?.goForward?.(),
20438
+ children: "›"
20439
+ }
20440
+ ),
20441
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
20442
+ "button",
20443
+ {
20444
+ type: "button",
20445
+ className: "ctlsurf-nav-btn",
20446
+ title: loading ? "Stop" : "Reload",
20447
+ onClick: () => loading ? webviewRef.current?.stop?.() : webviewRef.current?.reload?.(),
20448
+ children: loading ? "×" : "⟳"
20449
+ }
20450
+ ),
20451
+ /* @__PURE__ */ jsxRuntimeExports.jsx("form", { className: "ctlsurf-address-form", onSubmit, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20452
+ "input",
20453
+ {
20454
+ className: "ctlsurf-address-input",
20455
+ value: address,
20456
+ spellCheck: false,
20457
+ placeholder: "Enter a URL",
20458
+ onChange: (e) => setAddress(e.target.value),
20459
+ onFocus: (e) => e.currentTarget.select()
20460
+ }
20461
+ ) })
20462
+ ] }),
20463
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ctlsurf-webview-host", children: src ? /* @__PURE__ */ jsxRuntimeExports.jsx(
20464
+ "webview",
20465
+ {
20466
+ ref: webviewRef,
20467
+ src,
20468
+ style: { width: "100%", height: "100%" },
20469
+ allowpopups: "true"
20470
+ },
20471
+ key
20472
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ctlsurf-webview-loading", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#565f89", fontSize: "13px" }, children: "Loading ctlsurf..." }) }) })
20473
+ ] });
20293
20474
  }
20294
20475
  function _arrayLikeToArray(r, a) {
20295
20476
  (null == a || a > r.length) && (a = r.length);
@@ -207841,7 +208022,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
207841
208022
  modeConfigurationDefault$2
207842
208023
  );
207843
208024
  function getMode$3() {
207844
- return __vitePreload(() => import("./cssMode-R8fTFLae.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
208025
+ return __vitePreload(() => import("./cssMode-KhYyY-BM.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
207845
208026
  }
207846
208027
  languages.onLanguage("less", () => {
207847
208028
  getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
@@ -207946,7 +208127,7 @@ const razorLanguageService = registerHTMLLanguageService(
207946
208127
  );
207947
208128
  const razorDefaults = razorLanguageService.defaults;
207948
208129
  function getMode$2() {
207949
- return __vitePreload(() => import("./htmlMode-BWtG6ywk.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
208130
+ return __vitePreload(() => import("./htmlMode-Cfd5VCjw.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
207950
208131
  }
207951
208132
  function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
207952
208133
  const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
@@ -208030,7 +208211,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
208030
208211
  );
208031
208212
  const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
208032
208213
  function getMode$1() {
208033
- return __vitePreload(() => import("./jsonMode-D1zR4VVU.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
208214
+ return __vitePreload(() => import("./jsonMode-DZT5gBVW.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
208034
208215
  }
208035
208216
  languages.register({
208036
208217
  id: "json",
@@ -208276,7 +208457,7 @@ const getJavaScriptWorker = () => {
208276
208457
  return getMode().then((mode) => mode.getJavaScriptWorker());
208277
208458
  };
208278
208459
  function getMode() {
208279
- return __vitePreload(() => import("./tsMode-BYIQ-S9Z.js"), true ? [] : void 0, import.meta.url);
208460
+ return __vitePreload(() => import("./tsMode-C2aMdzdU.js"), true ? [] : void 0, import.meta.url);
208280
208461
  }
208281
208462
  languages.onLanguage("typescript", () => {
208282
208463
  return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
@@ -208471,49 +208652,49 @@ registerLanguage({
208471
208652
  extensions: [".ftl", ".ftlh", ".ftlx"],
208472
208653
  aliases: ["FreeMarker2", "Apache FreeMarker2"],
208473
208654
  loader: () => {
208474
- return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208655
+ return __vitePreload(() => import("./freemarker2-BFsSgcWk.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208475
208656
  }
208476
208657
  });
208477
208658
  registerLanguage({
208478
208659
  id: "freemarker2.tag-angle.interpolation-dollar",
208479
208660
  aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
208480
208661
  loader: () => {
208481
- return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
208662
+ return __vitePreload(() => import("./freemarker2-BFsSgcWk.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
208482
208663
  }
208483
208664
  });
208484
208665
  registerLanguage({
208485
208666
  id: "freemarker2.tag-bracket.interpolation-dollar",
208486
208667
  aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
208487
208668
  loader: () => {
208488
- return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
208669
+ return __vitePreload(() => import("./freemarker2-BFsSgcWk.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
208489
208670
  }
208490
208671
  });
208491
208672
  registerLanguage({
208492
208673
  id: "freemarker2.tag-angle.interpolation-bracket",
208493
208674
  aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
208494
208675
  loader: () => {
208495
- return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
208676
+ return __vitePreload(() => import("./freemarker2-BFsSgcWk.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
208496
208677
  }
208497
208678
  });
208498
208679
  registerLanguage({
208499
208680
  id: "freemarker2.tag-bracket.interpolation-bracket",
208500
208681
  aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
208501
208682
  loader: () => {
208502
- return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
208683
+ return __vitePreload(() => import("./freemarker2-BFsSgcWk.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
208503
208684
  }
208504
208685
  });
208505
208686
  registerLanguage({
208506
208687
  id: "freemarker2.tag-auto.interpolation-dollar",
208507
208688
  aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
208508
208689
  loader: () => {
208509
- return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208690
+ return __vitePreload(() => import("./freemarker2-BFsSgcWk.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
208510
208691
  }
208511
208692
  });
208512
208693
  registerLanguage({
208513
208694
  id: "freemarker2.tag-auto.interpolation-bracket",
208514
208695
  aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
208515
208696
  loader: () => {
208516
- return __vitePreload(() => import("./freemarker2-CyjWzWoP.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
208697
+ return __vitePreload(() => import("./freemarker2-BFsSgcWk.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
208517
208698
  }
208518
208699
  });
208519
208700
  registerLanguage({
@@ -208534,7 +208715,7 @@ registerLanguage({
208534
208715
  extensions: [".handlebars", ".hbs"],
208535
208716
  aliases: ["Handlebars", "handlebars", "hbs"],
208536
208717
  mimetypes: ["text/x-handlebars-template"],
208537
- loader: () => __vitePreload(() => import("./handlebars-CrhCGIRM.js"), true ? [] : void 0, import.meta.url)
208718
+ loader: () => __vitePreload(() => import("./handlebars-Be6_aBsm.js"), true ? [] : void 0, import.meta.url)
208538
208719
  });
208539
208720
  registerLanguage({
208540
208721
  id: "hcl",
@@ -208547,7 +208728,7 @@ registerLanguage({
208547
208728
  extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
208548
208729
  aliases: ["HTML", "htm", "html", "xhtml"],
208549
208730
  mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
208550
- loader: () => __vitePreload(() => import("./html-Cc3dPNZr.js"), true ? [] : void 0, import.meta.url)
208731
+ loader: () => __vitePreload(() => import("./html-G8mvsyfc.js"), true ? [] : void 0, import.meta.url)
208551
208732
  });
208552
208733
  registerLanguage({
208553
208734
  id: "ini",
@@ -208570,7 +208751,7 @@ registerLanguage({
208570
208751
  filenames: ["jakefile"],
208571
208752
  aliases: ["JavaScript", "javascript", "js"],
208572
208753
  mimetypes: ["text/javascript"],
208573
- loader: () => __vitePreload(() => import("./javascript-C8T_SdLC.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
208754
+ loader: () => __vitePreload(() => import("./javascript-XyDWi6dC.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
208574
208755
  });
208575
208756
  registerLanguage({
208576
208757
  id: "julia",
@@ -208609,7 +208790,7 @@ registerLanguage({
208609
208790
  extensions: [".liquid", ".html.liquid"],
208610
208791
  aliases: ["Liquid", "liquid"],
208611
208792
  mimetypes: ["application/liquid"],
208612
- loader: () => __vitePreload(() => import("./liquid-COSMe9Uf.js"), true ? [] : void 0, import.meta.url)
208793
+ loader: () => __vitePreload(() => import("./liquid-BuajCNJ9.js"), true ? [] : void 0, import.meta.url)
208613
208794
  });
208614
208795
  registerLanguage({
208615
208796
  id: "m3",
@@ -208627,7 +208808,7 @@ registerLanguage({
208627
208808
  id: "mdx",
208628
208809
  extensions: [".mdx"],
208629
208810
  aliases: ["MDX", "mdx"],
208630
- loader: () => __vitePreload(() => import("./mdx-RfS44T-L.js"), true ? [] : void 0, import.meta.url)
208811
+ loader: () => __vitePreload(() => import("./mdx-D0Qx2GMS.js"), true ? [] : void 0, import.meta.url)
208631
208812
  });
208632
208813
  registerLanguage({
208633
208814
  id: "mips",
@@ -208726,7 +208907,7 @@ registerLanguage({
208726
208907
  extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
208727
208908
  aliases: ["Python", "py"],
208728
208909
  firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
208729
- loader: () => __vitePreload(() => import("./python-DFSWKHUu.js"), true ? [] : void 0, import.meta.url)
208910
+ loader: () => __vitePreload(() => import("./python-ChncNy6S.js"), true ? [] : void 0, import.meta.url)
208730
208911
  });
208731
208912
  registerLanguage({
208732
208913
  id: "qsharp",
@@ -208745,7 +208926,7 @@ registerLanguage({
208745
208926
  extensions: [".cshtml"],
208746
208927
  aliases: ["Razor", "razor"],
208747
208928
  mimetypes: ["text/x-cshtml"],
208748
- loader: () => __vitePreload(() => import("./razor-DtH2_uvo.js"), true ? [] : void 0, import.meta.url)
208929
+ loader: () => __vitePreload(() => import("./razor-C6MUtfaS.js"), true ? [] : void 0, import.meta.url)
208749
208930
  });
208750
208931
  registerLanguage({
208751
208932
  id: "redis",
@@ -208878,7 +209059,7 @@ registerLanguage({
208878
209059
  aliases: ["TypeScript", "ts", "typescript"],
208879
209060
  mimetypes: ["text/typescript"],
208880
209061
  loader: () => {
208881
- return __vitePreload(() => import("./typescript-HhQUr1Rb.js"), true ? [] : void 0, import.meta.url);
209062
+ return __vitePreload(() => import("./typescript-CB71AtAT.js"), true ? [] : void 0, import.meta.url);
208882
209063
  }
208883
209064
  });
208884
209065
  registerLanguage({
@@ -208923,14 +209104,14 @@ registerLanguage({
208923
209104
  firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
208924
209105
  aliases: ["XML", "xml"],
208925
209106
  mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
208926
- loader: () => __vitePreload(() => import("./xml-KuhgdKfD.js"), true ? [] : void 0, import.meta.url)
209107
+ loader: () => __vitePreload(() => import("./xml-tHS1Ec9w.js"), true ? [] : void 0, import.meta.url)
208927
209108
  });
208928
209109
  registerLanguage({
208929
209110
  id: "yaml",
208930
209111
  extensions: [".yaml", ".yml"],
208931
209112
  aliases: ["YAML", "yaml", "YML", "yml"],
208932
209113
  mimetypes: ["application/x-yaml", "text/x-yaml"],
208933
- loader: () => __vitePreload(() => import("./yaml-W_kzCFH7.js"), true ? [] : void 0, import.meta.url)
209114
+ loader: () => __vitePreload(() => import("./yaml-CZcp5ueF.js"), true ? [] : void 0, import.meta.url)
208934
209115
  });
208935
209116
  var __defProp = Object.defineProperty;
208936
209117
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -213443,6 +213624,47 @@ function TagIcon({ size: size2 = 16 }) {
213443
213624
  }
213444
213625
  );
213445
213626
  }
213627
+ function SendIcon({ size: size2 = 15 }) {
213628
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
213629
+ "svg",
213630
+ {
213631
+ viewBox: "0 0 24 24",
213632
+ width: size2,
213633
+ height: size2,
213634
+ fill: "none",
213635
+ stroke: "currentColor",
213636
+ strokeWidth: "2",
213637
+ strokeLinecap: "round",
213638
+ strokeLinejoin: "round",
213639
+ "aria-hidden": "true",
213640
+ children: [
213641
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
213642
+ /* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
213643
+ ]
213644
+ }
213645
+ );
213646
+ }
213647
+ function ticketPrompt(t, refs) {
213648
+ const lines = [
213649
+ "Please work on this ticket:",
213650
+ `Title: ${t.title || "Untitled"}`,
213651
+ `Priority: ${t.priority}`
213652
+ ];
213653
+ const desc = t.description?.trim();
213654
+ if (desc) lines.push("", desc);
213655
+ lines.push(
213656
+ "",
213657
+ "ctlsurf references (use the ctlsurf MCP tools to read details or update status):",
213658
+ `- Ticket row ID: ${t.id}`
213659
+ );
213660
+ if (refs) {
213661
+ lines.push(
213662
+ `- Tickets datastore block ID: ${refs.blockId}`,
213663
+ `- Agent Datastore page ID: ${refs.pageId}`
213664
+ );
213665
+ }
213666
+ return lines.join("\n");
213667
+ }
213446
213668
  function Segmented({ options, value, onChange }) {
213447
213669
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-segmented", children: options.map((o) => {
213448
213670
  const active = o.value === value;
@@ -213462,12 +213684,50 @@ function Segmented({ options, value, onChange }) {
213462
213684
  );
213463
213685
  }) });
213464
213686
  }
213465
- function TicketPane() {
213687
+ function TicketCard({ ticket, onClick, onSend }) {
213688
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card", children: [
213689
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ticket-card-open", onClick, title: "Edit ticket", children: [
213690
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-bar", style: { background: colorOf(STATUS_OPTIONS, ticket.status) } }),
213691
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-main", children: [
213692
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-title", children: ticket.title || "Untitled" }),
213693
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-meta", children: [
213694
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ticket-card-status", style: { color: colorOf(STATUS_OPTIONS, ticket.status) }, children: [
213695
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-seg-dot", style: { background: colorOf(STATUS_OPTIONS, ticket.status) } }),
213696
+ ticket.status
213697
+ ] }),
213698
+ ticket.created && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-date", children: formatDate(ticket.created) })
213699
+ ] })
213700
+ ] }),
213701
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
213702
+ "span",
213703
+ {
213704
+ className: "ticket-card-pri",
213705
+ style: { color: colorOf(PRIORITY_OPTIONS, ticket.priority), borderColor: `${colorOf(PRIORITY_OPTIONS, ticket.priority)}66` },
213706
+ children: ticket.priority
213707
+ }
213708
+ )
213709
+ ] }),
213710
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
213711
+ "button",
213712
+ {
213713
+ type: "button",
213714
+ className: "ticket-card-send",
213715
+ onClick: onSend,
213716
+ title: "Send to coding terminal",
213717
+ "aria-label": "Send ticket to coding terminal",
213718
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(SendIcon, {})
213719
+ }
213720
+ )
213721
+ ] });
213722
+ }
213723
+ function TicketPane({ onSendToTerminal }) {
213466
213724
  const [project, setProject] = reactExports.useState(null);
213467
213725
  const [view, setView] = reactExports.useState("list");
213468
213726
  const [tickets, setTickets] = reactExports.useState([]);
213727
+ const [refs, setRefs] = reactExports.useState(null);
213469
213728
  const [listLoading, setListLoading] = reactExports.useState(false);
213470
213729
  const [listError, setListError] = reactExports.useState(null);
213730
+ const [showDone, setShowDone] = reactExports.useState(false);
213471
213731
  const [editingId, setEditingId] = reactExports.useState(null);
213472
213732
  const [title, setTitle] = reactExports.useState("");
213473
213733
  const [description, setDescription] = reactExports.useState("");
@@ -213479,13 +213739,17 @@ function TicketPane() {
213479
213739
  setListError(null);
213480
213740
  try {
213481
213741
  const r = await window.worker.listTickets();
213482
- if (r?.ok) setTickets(r.tickets || []);
213483
- else {
213742
+ if (r?.ok) {
213743
+ setTickets(r.tickets || []);
213744
+ setRefs(r.blockId && r.pageId ? { blockId: r.blockId, pageId: r.pageId } : null);
213745
+ } else {
213484
213746
  setTickets([]);
213747
+ setRefs(null);
213485
213748
  setListError(r?.error || "Failed to load tickets");
213486
213749
  }
213487
213750
  } catch (err) {
213488
213751
  setTickets([]);
213752
+ setRefs(null);
213489
213753
  setListError(err?.message || "Failed to load tickets");
213490
213754
  } finally {
213491
213755
  setListLoading(false);
@@ -213557,17 +213821,22 @@ function TicketPane() {
213557
213821
  window.addEventListener("keydown", onKey);
213558
213822
  return () => window.removeEventListener("keydown", onKey);
213559
213823
  }, [view, handleSave]);
213824
+ const activeTickets = tickets.filter((t) => t.status !== "Done");
213825
+ const doneTickets = tickets.filter((t) => t.status === "Done");
213560
213826
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-pane", role: "region", "aria-label": "Tickets", children: [
213561
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-drawer-header", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-heading", children: [
213562
- view === "form" ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-drawer-back", onClick: () => setView("list"), title: "Back (Esc)", children: "‹" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-badge", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, { size: 16 }) }),
213563
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-headtext", children: [
213564
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-title", children: view === "form" ? editingId ? "Edit ticket" : "New ticket" : "Tickets" }),
213565
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-subtitle", children: project ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213566
- "in ",
213567
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: project })
213568
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-warn", children: "no ctlsurf project for this tab" }) })
213569
- ] })
213570
- ] }) }),
213827
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-header", children: [
213828
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-heading", children: [
213829
+ view === "form" ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-drawer-back", onClick: () => setView("list"), title: "Back (Esc)", children: "‹" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-badge", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, { size: 16 }) }),
213830
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-headtext", children: [
213831
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-title", children: view === "form" ? editingId ? "Edit ticket" : "New ticket" : "Tickets" }),
213832
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-subtitle", children: project ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213833
+ "in ",
213834
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: project })
213835
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-warn", children: "no ctlsurf project for this tab" }) })
213836
+ ] })
213837
+ ] }),
213838
+ view === "list" && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-drawer-new", onClick: openNewForm, title: "New ticket", children: "+ New" })
213839
+ ] }),
213571
213840
  view === "list" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213572
213841
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-list-body", children: [
213573
213842
  listLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-list-msg", children: "Loading tickets…" }),
@@ -213580,32 +213849,27 @@ function TicketPane() {
213580
213849
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-title", children: "No tickets yet" }),
213581
213850
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-empty-hint", children: "Log the first one for this project." })
213582
213851
  ] }),
213583
- !listLoading && tickets.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ticket-card", onClick: () => openEditForm(t), children: [
213584
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-bar", style: { background: colorOf(STATUS_OPTIONS, t.status) } }),
213585
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-main", children: [
213586
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-title", children: t.title || "Untitled" }),
213587
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-card-meta", children: [
213588
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ticket-card-status", style: { color: colorOf(STATUS_OPTIONS, t.status) }, children: [
213589
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-seg-dot", style: { background: colorOf(STATUS_OPTIONS, t.status) } }),
213590
- t.status
213591
- ] }),
213592
- t.created && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-card-date", children: formatDate(t.created) })
213593
- ] })
213594
- ] }),
213595
- /* @__PURE__ */ jsxRuntimeExports.jsx(
213596
- "span",
213852
+ !listLoading && activeTickets.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsx(TicketCard, { ticket: t, onClick: () => openEditForm(t), onSend: () => onSendToTerminal(ticketPrompt(t, refs)) }, t.id)),
213853
+ !listLoading && activeTickets.length === 0 && doneTickets.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-list-msg", children: "Nothing open — all caught up." }),
213854
+ !listLoading && doneTickets.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213855
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
213856
+ "button",
213597
213857
  {
213598
- className: "ticket-card-pri",
213599
- style: { color: colorOf(PRIORITY_OPTIONS, t.priority), borderColor: `${colorOf(PRIORITY_OPTIONS, t.priority)}66` },
213600
- children: t.priority
213858
+ type: "button",
213859
+ className: "ticket-done-toggle",
213860
+ onClick: () => setShowDone((v2) => !v2),
213861
+ "aria-expanded": showDone,
213862
+ children: [
213863
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `ticket-done-caret ${showDone ? "open" : ""}`, "aria-hidden": "true", children: "▸" }),
213864
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-done-label", children: "Done" }),
213865
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-done-count", children: doneTickets.length })
213866
+ ]
213601
213867
  }
213602
- )
213603
- ] }, t.id))
213868
+ ),
213869
+ showDone && doneTickets.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsx(TicketCard, { ticket: t, onClick: () => openEditForm(t), onSend: () => onSendToTerminal(ticketPrompt(t, refs)) }, t.id))
213870
+ ] })
213604
213871
  ] }),
213605
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-footer", children: [
213606
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-hint", children: tickets.length > 0 && `${tickets.length} ticket${tickets.length === 1 ? "" : "s"}` }),
213607
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ticket-btn-primary", onClick: openNewForm, children: "+ New ticket" })
213608
- ] })
213872
+ tickets.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ticket-drawer-footer", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ticket-drawer-hint", children: doneTickets.length > 0 ? `${activeTickets.length} open · ${doneTickets.length} done` : `${tickets.length} ticket${tickets.length === 1 ? "" : "s"}` }) })
213609
213873
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
213610
213874
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ticket-drawer-body", children: [
213611
213875
  /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "ticket-field", children: [
@@ -213798,6 +214062,13 @@ function App() {
213798
214062
  focusTerminal(activeTabId);
213799
214063
  setTimeout(() => window.worker.writePty(activeTabId, "\r"), 80);
213800
214064
  }, [activeTabId]);
214065
+ const handleSendToTerminal = reactExports.useCallback((text2) => {
214066
+ const trimmed = text2.trim();
214067
+ if (!trimmed) return;
214068
+ window.worker.writePty(activeTabId, trimmed);
214069
+ focusTerminal(activeTabId);
214070
+ setTimeout(() => window.worker.writePty(activeTabId, "\r"), 120);
214071
+ }, [activeTabId]);
213801
214072
  const cwdRef = reactExports.useRef(null);
213802
214073
  const handleSpawn = reactExports.useCallback(async (tabId, agent) => {
213803
214074
  setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "active" } : t));
@@ -213950,6 +214221,7 @@ function App() {
213950
214221
  window.addEventListener("ctlsurf-open-url", onOpen);
213951
214222
  return () => window.removeEventListener("ctlsurf-open-url", onOpen);
213952
214223
  }, [layout2, togglePane]);
214224
+ const activeAgentIsRemote = !!tabs.find((t) => t.id === activeTabId)?.agent?.id?.startsWith("remote-");
213953
214225
  const panes = [
213954
214226
  { id: "editor", label: "Editor", content: /* @__PURE__ */ jsxRuntimeExports.jsx(EditorPanel, { cwd: cwd2 }) },
213955
214227
  {
@@ -214009,7 +214281,7 @@ function App() {
214009
214281
  ] })
214010
214282
  },
214011
214283
  { id: "ctlsurf", label: "ctlsurf", content: /* @__PURE__ */ jsxRuntimeExports.jsx(CtlsurfPanel, { navigate: ctlsurfNav }) },
214012
- { id: "tickets", label: "Tickets", content: /* @__PURE__ */ jsxRuntimeExports.jsx(TicketPane, {}) }
214284
+ { id: "tickets", label: "Tickets", content: /* @__PURE__ */ jsxRuntimeExports.jsx(TicketPane, { onSendToTerminal: handleSendToTerminal }) }
214013
214285
  ];
214014
214286
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "app", children: [
214015
214287
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "titlebar", children: [
@@ -214095,7 +214367,7 @@ function App() {
214095
214367
  )
214096
214368
  }
214097
214369
  ),
214098
- /* @__PURE__ */ jsxRuntimeExports.jsx(SpeakControls, {}),
214370
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SpeakControls, { remote: activeAgentIsRemote }),
214099
214371
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
214100
214372
  agents.map((a) => {
214101
214373
  const activeTab = tabs.find((t) => t.id === activeTabId);