@phenx-inc/ctlsurf 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/electron-vite.config.ts +5 -0
  2. package/out/headless/index.mjs +2 -1
  3. package/out/headless/index.mjs.map +2 -2
  4. package/out/main/index.js +38 -6
  5. package/out/preload/index.js +3 -0
  6. package/out/renderer/assets/{cssMode-DkmdBgO7.js → cssMode-DbMmcl1h.js} +3 -3
  7. package/out/renderer/assets/{freemarker2-CI-gkP-3.js → freemarker2-CvaHiy92.js} +1 -1
  8. package/out/renderer/assets/{handlebars-D5tEqanR.js → handlebars-D58lUIOu.js} +1 -1
  9. package/out/renderer/assets/{html-fH93EYfn.js → html-D1h1aJbM.js} +1 -1
  10. package/out/renderer/assets/{htmlMode-CRicxcwK.js → htmlMode-BdkAp9qr.js} +3 -3
  11. package/out/renderer/assets/{index-BOOvUI7u.js → index-B60JU1yI.js} +461 -111
  12. package/out/renderer/assets/{index-ezC-iarf.css → index-DJFYmHjz.css} +89 -0
  13. package/out/renderer/assets/{javascript-D1Baz4fV.js → javascript-CXqZcnvb.js} +2 -2
  14. package/out/renderer/assets/{jsonMode-Bquqf3QN.js → jsonMode-BuVr-eSl.js} +3 -3
  15. package/out/renderer/assets/{liquid-ByOcPjBF.js → liquid-LKu0Wd0B.js} +1 -1
  16. package/out/renderer/assets/{lspLanguageFeatures-BxPLl0yy.js → lspLanguageFeatures-Cjr_4HGs.js} +1 -1
  17. package/out/renderer/assets/{mdx-yuNgx0rM.js → mdx-Bl84ILla.js} +1 -1
  18. package/out/renderer/assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm +0 -0
  19. package/out/renderer/assets/{python-2OakgLlA.js → python-0sFd9G1k.js} +1 -1
  20. package/out/renderer/assets/{razor-DnIVMSwa.js → razor-Cqcu1rLJ.js} +1 -1
  21. package/out/renderer/assets/transformers.web-DtSCnG36.js +33668 -0
  22. package/out/renderer/assets/{tsMode-CRIrHuii.js → tsMode-CYd3NUkW.js} +1 -1
  23. package/out/renderer/assets/{typescript-DJ3C8Yly.js → typescript-rkc9lhpi.js} +1 -1
  24. package/out/renderer/assets/{xml-CalvD5_C.js → xml-EsHEUps1.js} +1 -1
  25. package/out/renderer/assets/{yaml-Cgs8pdVp.js → yaml-B9-nQ_s2.js} +1 -1
  26. package/out/renderer/index.html +2 -2
  27. package/package.json +2 -1
  28. package/src/main/index.ts +49 -2
  29. package/src/preload/index.ts +4 -0
  30. package/src/renderer/App.tsx +34 -2
  31. package/src/renderer/components/CtlsurfPanel.tsx +19 -3
  32. package/src/renderer/components/TerminalPanel.tsx +32 -1
  33. package/src/renderer/components/VoiceInput.tsx +313 -0
  34. package/src/renderer/lib/localWhisper.ts +88 -0
  35. package/src/renderer/styles.css +89 -0
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-DkmdBgO7.js","./lspLanguageFeatures-BxPLl0yy.js","./htmlMode-CRicxcwK.js","./jsonMode-Bquqf3QN.js","./javascript-D1Baz4fV.js","./typescript-DJ3C8Yly.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-DbMmcl1h.js","./lspLanguageFeatures-Cjr_4HGs.js","./htmlMode-BdkAp9qr.js","./jsonMode-BuVr-eSl.js","./javascript-CXqZcnvb.js","./typescript-rkc9lhpi.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,6 +18829,25 @@ 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
+ 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));
18850
+ }
18832
18851
  const _terminals = /* @__PURE__ */ new Map();
18833
18852
  function isAtBottom(terminal) {
18834
18853
  const buf = terminal.buffer.active;
@@ -18880,7 +18899,7 @@ function getOrCreateTerminal(tabId, onExit) {
18880
18899
  });
18881
18900
  const fitAddon = new addonFitExports.FitAddon();
18882
18901
  terminal.loadAddon(fitAddon);
18883
- terminal.loadAddon(new addonWebLinksExports.WebLinksAddon());
18902
+ terminal.loadAddon(new addonWebLinksExports.WebLinksAddon(handleTerminalLink));
18884
18903
  terminal.attachCustomKeyEventHandler((e) => {
18885
18904
  if (e.type !== "keydown") return true;
18886
18905
  const isCombo = (e.metaKey || e.ctrlKey) && e.shiftKey && e.code === "KeyE";
@@ -18921,6 +18940,9 @@ function getOrCreateTerminal(tabId, onExit) {
18921
18940
  _terminals.set(tabId, state);
18922
18941
  return { terminal, fitAddon };
18923
18942
  }
18943
+ function focusTerminal(tabId) {
18944
+ _terminals.get(tabId)?.terminal.focus();
18945
+ }
18924
18946
  function destroyTerminal(tabId) {
18925
18947
  const state = _terminals.get(tabId);
18926
18948
  if (!state) return;
@@ -19054,7 +19076,394 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19054
19076
  toast && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "term-toast", children: toast })
19055
19077
  ] });
19056
19078
  }
19057
- function CtlsurfPanel() {
19079
+ const scriptRel = /* @__PURE__ */ (function detectScriptRel() {
19080
+ const relList = typeof document !== "undefined" && document.createElement("link").relList;
19081
+ return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
19082
+ })();
19083
+ const assetsURL = function(dep, importerUrl) {
19084
+ return new URL(dep, importerUrl).href;
19085
+ };
19086
+ const seen = {};
19087
+ const __vitePreload = function preload(baseModule, deps, importerUrl) {
19088
+ let promise = Promise.resolve();
19089
+ if (deps && deps.length > 0) {
19090
+ let allSettled2 = function(promises) {
19091
+ return Promise.all(
19092
+ promises.map(
19093
+ (p) => Promise.resolve(p).then(
19094
+ (value) => ({ status: "fulfilled", value }),
19095
+ (reason) => ({ status: "rejected", reason })
19096
+ )
19097
+ )
19098
+ );
19099
+ };
19100
+ const links = document.getElementsByTagName("link");
19101
+ const cspNonceMeta = document.querySelector(
19102
+ "meta[property=csp-nonce]"
19103
+ );
19104
+ const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
19105
+ promise = allSettled2(
19106
+ deps.map((dep) => {
19107
+ dep = assetsURL(dep, importerUrl);
19108
+ if (dep in seen) return;
19109
+ seen[dep] = true;
19110
+ const isCss = dep.endsWith(".css");
19111
+ const cssSelector = isCss ? '[rel="stylesheet"]' : "";
19112
+ const isBaseRelative = !!importerUrl;
19113
+ if (isBaseRelative) {
19114
+ for (let i2 = links.length - 1; i2 >= 0; i2--) {
19115
+ const link22 = links[i2];
19116
+ if (link22.href === dep && (!isCss || link22.rel === "stylesheet")) {
19117
+ return;
19118
+ }
19119
+ }
19120
+ } else if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
19121
+ return;
19122
+ }
19123
+ const link2 = document.createElement("link");
19124
+ link2.rel = isCss ? "stylesheet" : scriptRel;
19125
+ if (!isCss) {
19126
+ link2.as = "script";
19127
+ }
19128
+ link2.crossOrigin = "";
19129
+ link2.href = dep;
19130
+ if (cspNonce) {
19131
+ link2.setAttribute("nonce", cspNonce);
19132
+ }
19133
+ document.head.appendChild(link2);
19134
+ if (isCss) {
19135
+ return new Promise((res, rej) => {
19136
+ link2.addEventListener("load", res);
19137
+ link2.addEventListener(
19138
+ "error",
19139
+ () => rej(new Error(`Unable to preload CSS for ${dep}`))
19140
+ );
19141
+ });
19142
+ }
19143
+ })
19144
+ );
19145
+ }
19146
+ function handlePreloadError(err) {
19147
+ const e = new Event("vite:preloadError", {
19148
+ cancelable: true
19149
+ });
19150
+ e.payload = err;
19151
+ window.dispatchEvent(e);
19152
+ if (!e.defaultPrevented) {
19153
+ throw err;
19154
+ }
19155
+ }
19156
+ return promise.then((res) => {
19157
+ for (const item of res || []) {
19158
+ if (item.status !== "rejected") continue;
19159
+ handlePreloadError(item.reason);
19160
+ }
19161
+ return baseModule().catch(handlePreloadError);
19162
+ });
19163
+ };
19164
+ const MODEL = "Xenova/whisper-base";
19165
+ const TARGET_SAMPLE_RATE = 16e3;
19166
+ let transcriberPromise = null;
19167
+ async function loadTranscriber(onProgress) {
19168
+ if (!transcriberPromise) {
19169
+ transcriberPromise = (async () => {
19170
+ const { pipeline, env: env2 } = await __vitePreload(async () => {
19171
+ const { pipeline: pipeline2, env: env3 } = await import("./transformers.web-DtSCnG36.js");
19172
+ return { pipeline: pipeline2, env: env3 };
19173
+ }, true ? [] : void 0, import.meta.url);
19174
+ env2.allowLocalModels = false;
19175
+ const common = { progress_callback: onProgress };
19176
+ const hasWebGpu = typeof navigator !== "undefined" && "gpu" in navigator;
19177
+ if (hasWebGpu) {
19178
+ try {
19179
+ return await pipeline("automatic-speech-recognition", MODEL, { ...common, device: "webgpu" });
19180
+ } catch (err) {
19181
+ console.warn("[voice] WebGPU backend failed, falling back to WASM", err);
19182
+ }
19183
+ }
19184
+ return await pipeline("automatic-speech-recognition", MODEL, common);
19185
+ })();
19186
+ transcriberPromise.catch(() => {
19187
+ transcriberPromise = null;
19188
+ });
19189
+ }
19190
+ return transcriberPromise;
19191
+ }
19192
+ async function blobToPcm16k(blob) {
19193
+ if (blob.size === 0) return null;
19194
+ const arrayBuffer = await blob.arrayBuffer();
19195
+ const AudioCtx = window.AudioContext || window.webkitAudioContext;
19196
+ const ctx = new AudioCtx();
19197
+ let decoded;
19198
+ try {
19199
+ decoded = await ctx.decodeAudioData(arrayBuffer);
19200
+ } catch {
19201
+ return null;
19202
+ } finally {
19203
+ ctx.close();
19204
+ }
19205
+ const length = Math.ceil(decoded.duration * TARGET_SAMPLE_RATE);
19206
+ if (length < 1) return null;
19207
+ const offline = new OfflineAudioContext(1, length, TARGET_SAMPLE_RATE);
19208
+ const source = offline.createBufferSource();
19209
+ source.buffer = decoded;
19210
+ source.connect(offline.destination);
19211
+ source.start();
19212
+ const rendered = await offline.startRendering();
19213
+ return rendered.getChannelData(0);
19214
+ }
19215
+ async function transcribeBlob(blob, onProgress) {
19216
+ const transcriber = await loadTranscriber(onProgress);
19217
+ const pcm = await blobToPcm16k(blob);
19218
+ if (!pcm) return "";
19219
+ const result = await transcriber(pcm);
19220
+ const text2 = Array.isArray(result) ? result.map((r) => r.text).join(" ") : result?.text;
19221
+ return (text2 || "").trim();
19222
+ }
19223
+ function getRecognitionCtor() {
19224
+ const w = window;
19225
+ return w.SpeechRecognition || w.webkitSpeechRecognition || null;
19226
+ }
19227
+ const ENGINE_KEY = "ctlsurf.voiceEngine";
19228
+ const WEB_SPEECH_SUPPORTED = getRecognitionCtor() !== null;
19229
+ const LOCAL_SUPPORTED = typeof navigator !== "undefined" && !!navigator.mediaDevices?.getUserMedia && typeof MediaRecorder !== "undefined" && typeof OfflineAudioContext !== "undefined";
19230
+ const ANY_SUPPORTED = WEB_SPEECH_SUPPORTED || LOCAL_SUPPORTED;
19231
+ function loadInitialEngine() {
19232
+ if (!WEB_SPEECH_SUPPORTED && LOCAL_SUPPORTED) return "local";
19233
+ try {
19234
+ if (localStorage.getItem(ENGINE_KEY) === "local" && LOCAL_SUPPORTED) return "local";
19235
+ } catch {
19236
+ }
19237
+ return WEB_SPEECH_SUPPORTED ? "web-speech" : "local";
19238
+ }
19239
+ function isEngineUnavailable(code) {
19240
+ return code === "network" || code === "service-not-allowed";
19241
+ }
19242
+ function describeMicError(err) {
19243
+ const name = err?.name;
19244
+ if (name === "NotAllowedError" || name === "SecurityError") return "Microphone access denied";
19245
+ if (name === "NotFoundError") return "No microphone found";
19246
+ return "Could not start microphone";
19247
+ }
19248
+ function VoiceInput({ onTranscript }) {
19249
+ const [engine, setEngine] = reactExports.useState(loadInitialEngine);
19250
+ const [phase, setPhase] = reactExports.useState("idle");
19251
+ const [interim, setInterim] = reactExports.useState("");
19252
+ const [modelPct, setModelPct] = reactExports.useState(null);
19253
+ const [error, setError] = reactExports.useState(null);
19254
+ const [notice, setNotice] = reactExports.useState(null);
19255
+ const recognitionRef = reactExports.useRef(null);
19256
+ const finalRef = reactExports.useRef("");
19257
+ const streamRef = reactExports.useRef(null);
19258
+ const recorderRef = reactExports.useRef(null);
19259
+ const chunksRef = reactExports.useRef([]);
19260
+ const cancelGestureRef = reactExports.useRef(false);
19261
+ const engineRef = reactExports.useRef(engine);
19262
+ reactExports.useEffect(() => {
19263
+ engineRef.current = engine;
19264
+ }, [engine]);
19265
+ const onTranscriptRef = reactExports.useRef(onTranscript);
19266
+ reactExports.useEffect(() => {
19267
+ onTranscriptRef.current = onTranscript;
19268
+ }, [onTranscript]);
19269
+ reactExports.useEffect(() => {
19270
+ if (!error) return;
19271
+ const t = setTimeout(() => setError(null), 4500);
19272
+ return () => clearTimeout(t);
19273
+ }, [error]);
19274
+ reactExports.useEffect(() => {
19275
+ if (!notice) return;
19276
+ const t = setTimeout(() => setNotice(null), 5e3);
19277
+ return () => clearTimeout(t);
19278
+ }, [notice]);
19279
+ const switchToLocal = reactExports.useCallback((reason) => {
19280
+ try {
19281
+ localStorage.setItem(ENGINE_KEY, "local");
19282
+ } catch {
19283
+ }
19284
+ setEngine("local");
19285
+ setNotice(reason);
19286
+ }, []);
19287
+ const stopStream = reactExports.useCallback(() => {
19288
+ streamRef.current?.getTracks().forEach((t) => t.stop());
19289
+ streamRef.current = null;
19290
+ }, []);
19291
+ const startWebSpeech = reactExports.useCallback(() => {
19292
+ const Ctor = getRecognitionCtor();
19293
+ if (!Ctor || recognitionRef.current) return;
19294
+ setError(null);
19295
+ setNotice(null);
19296
+ setInterim("");
19297
+ finalRef.current = "";
19298
+ const rec = new Ctor();
19299
+ rec.lang = navigator.language || "en-US";
19300
+ rec.continuous = true;
19301
+ rec.interimResults = true;
19302
+ rec.onresult = (event) => {
19303
+ let finalText = "";
19304
+ let interimText = "";
19305
+ for (let i2 = 0; i2 < event.results.length; i2++) {
19306
+ const res = event.results[i2];
19307
+ if (res.isFinal) finalText += res[0].transcript;
19308
+ else interimText += res[0].transcript;
19309
+ }
19310
+ finalRef.current = finalText;
19311
+ setInterim(interimText);
19312
+ };
19313
+ rec.onerror = (event) => {
19314
+ if (isEngineUnavailable(event.error) && LOCAL_SUPPORTED) {
19315
+ finalRef.current = "";
19316
+ switchToLocal("Voice service unavailable — switched to on-device. Press again.");
19317
+ } else if (event.error !== "no-speech" && event.error !== "aborted") {
19318
+ setError(event.error === "not-allowed" ? "Microphone access denied" : `Voice error: ${event.error}`);
19319
+ }
19320
+ };
19321
+ rec.onend = () => {
19322
+ const text2 = finalRef.current.trim();
19323
+ recognitionRef.current = null;
19324
+ setPhase("idle");
19325
+ setInterim("");
19326
+ if (text2) onTranscriptRef.current(text2);
19327
+ };
19328
+ recognitionRef.current = rec;
19329
+ try {
19330
+ rec.start();
19331
+ setPhase("listening");
19332
+ } catch (err) {
19333
+ recognitionRef.current = null;
19334
+ setPhase("idle");
19335
+ setError("Could not start microphone");
19336
+ console.error("[voice] web speech start failed", err);
19337
+ }
19338
+ }, [switchToLocal]);
19339
+ const stopWebSpeech = reactExports.useCallback(() => {
19340
+ try {
19341
+ recognitionRef.current?.stop();
19342
+ } catch {
19343
+ }
19344
+ }, []);
19345
+ const handleModelProgress = reactExports.useCallback((p) => {
19346
+ if (p.status === "progress" && typeof p.progress === "number") {
19347
+ setModelPct(Math.min(100, Math.round(p.progress)));
19348
+ }
19349
+ }, []);
19350
+ const runLocalTranscription = reactExports.useCallback(async (rec) => {
19351
+ stopStream();
19352
+ const blob = new Blob(chunksRef.current, { type: rec.mimeType || "audio/webm" });
19353
+ chunksRef.current = [];
19354
+ recorderRef.current = null;
19355
+ if (blob.size === 0) {
19356
+ setPhase("idle");
19357
+ return;
19358
+ }
19359
+ setPhase("transcribing");
19360
+ setInterim("");
19361
+ try {
19362
+ const text2 = await transcribeBlob(blob, handleModelProgress);
19363
+ if (text2) onTranscriptRef.current(text2);
19364
+ } catch (err) {
19365
+ setError("On-device transcription failed");
19366
+ console.error("[voice] local transcription failed", err);
19367
+ } finally {
19368
+ setPhase("idle");
19369
+ setModelPct(null);
19370
+ }
19371
+ }, [stopStream, handleModelProgress]);
19372
+ const startLocal = reactExports.useCallback(async () => {
19373
+ setError(null);
19374
+ setNotice(null);
19375
+ setInterim("");
19376
+ cancelGestureRef.current = false;
19377
+ try {
19378
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
19379
+ if (cancelGestureRef.current) {
19380
+ stream.getTracks().forEach((t) => t.stop());
19381
+ setPhase("idle");
19382
+ return;
19383
+ }
19384
+ streamRef.current = stream;
19385
+ chunksRef.current = [];
19386
+ const rec = new MediaRecorder(stream);
19387
+ rec.ondataavailable = (e) => {
19388
+ if (e.data.size) chunksRef.current.push(e.data);
19389
+ };
19390
+ rec.onstop = () => {
19391
+ void runLocalTranscription(rec);
19392
+ };
19393
+ recorderRef.current = rec;
19394
+ rec.start();
19395
+ setPhase("listening");
19396
+ } catch (err) {
19397
+ stopStream();
19398
+ setPhase("idle");
19399
+ setError(describeMicError(err));
19400
+ console.error("[voice] getUserMedia failed", err);
19401
+ }
19402
+ }, [runLocalTranscription, stopStream]);
19403
+ const stopLocal = reactExports.useCallback(() => {
19404
+ cancelGestureRef.current = true;
19405
+ const rec = recorderRef.current;
19406
+ if (rec && rec.state !== "inactive") {
19407
+ try {
19408
+ rec.stop();
19409
+ } catch {
19410
+ }
19411
+ }
19412
+ }, []);
19413
+ const handlePointerDown = (e) => {
19414
+ if (!ANY_SUPPORTED || phase !== "idle") return;
19415
+ e.preventDefault();
19416
+ e.currentTarget.setPointerCapture?.(e.pointerId);
19417
+ if (engineRef.current === "web-speech" && WEB_SPEECH_SUPPORTED) startWebSpeech();
19418
+ else if (LOCAL_SUPPORTED) void startLocal();
19419
+ };
19420
+ const handlePointerUp = (e) => {
19421
+ e.currentTarget.releasePointerCapture?.(e.pointerId);
19422
+ if (engineRef.current === "web-speech") stopWebSpeech();
19423
+ else stopLocal();
19424
+ };
19425
+ reactExports.useEffect(() => () => {
19426
+ try {
19427
+ recognitionRef.current?.abort();
19428
+ } catch {
19429
+ }
19430
+ try {
19431
+ recorderRef.current?.stop();
19432
+ } catch {
19433
+ }
19434
+ streamRef.current?.getTracks().forEach((t) => t.stop());
19435
+ }, []);
19436
+ const listening = phase === "listening";
19437
+ const busy = phase === "transcribing";
19438
+ const title = !ANY_SUPPORTED ? "Voice typing not supported in this build" : listening ? "Listening… release to insert" : busy ? "Transcribing…" : engine === "local" ? "Hold to talk (on-device) — speech is typed into the terminal" : "Hold to talk — speech is typed into the terminal";
19439
+ let chip = null;
19440
+ if (error && phase === "idle") chip = { kind: "error", text: error };
19441
+ else if (notice && phase === "idle") chip = { kind: "notice", text: notice };
19442
+ else if (listening) chip = { kind: "listening", text: interim || (engine === "local" ? "Recording…" : "Listening…") };
19443
+ else if (busy) chip = { kind: "busy", text: modelPct !== null ? `Downloading voice model… ${modelPct}%` : "Transcribing…" };
19444
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "voice-input-wrap", children: [
19445
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
19446
+ "button",
19447
+ {
19448
+ type: "button",
19449
+ className: `titlebar-btn titlebar-icon-btn voice-btn ${listening ? "listening" : ""} ${busy ? "busy" : ""}`,
19450
+ disabled: !ANY_SUPPORTED,
19451
+ onPointerDown: handlePointerDown,
19452
+ onPointerUp: handlePointerUp,
19453
+ onPointerCancel: handlePointerUp,
19454
+ onContextMenu: (e) => e.preventDefault(),
19455
+ title,
19456
+ "aria-label": "Voice typing (hold to talk)",
19457
+ children: [
19458
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "voice-icon", "aria-hidden": "true", children: "🎤" }),
19459
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `voice-dot ${listening ? "on" : busy ? "busy" : "off"}` })
19460
+ ]
19461
+ }
19462
+ ),
19463
+ chip && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `voice-chip ${chip.kind}`, children: chip.text })
19464
+ ] });
19465
+ }
19466
+ function CtlsurfPanel({ navigate }) {
19058
19467
  const webviewRef = reactExports.useRef(null);
19059
19468
  const [url, setUrl] = reactExports.useState(null);
19060
19469
  const [key, setKey] = reactExports.useState(0);
@@ -19068,8 +19477,14 @@ function CtlsurfPanel() {
19068
19477
  }
19069
19478
  };
19070
19479
  reactExports.useEffect(() => {
19071
- loadUrl();
19480
+ if (!navigate?.url) loadUrl();
19072
19481
  }, []);
19482
+ reactExports.useEffect(() => {
19483
+ if (navigate?.url) {
19484
+ setUrl(navigate.url);
19485
+ setKey((k2) => k2 + 1);
19486
+ }
19487
+ }, [navigate?.n]);
19073
19488
  reactExports.useEffect(() => {
19074
19489
  const unsub = window.worker.onCwdChanged(() => {
19075
19490
  setUrl(null);
@@ -19700,91 +20115,6 @@ function Ve({ defaultValue: e, defaultLanguage: r, defaultPath: n2, value: t, la
19700
20115
  var fe = Ve;
19701
20116
  var de = reactExports.memo(fe);
19702
20117
  var Ft = de;
19703
- const scriptRel = /* @__PURE__ */ (function detectScriptRel() {
19704
- const relList = typeof document !== "undefined" && document.createElement("link").relList;
19705
- return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
19706
- })();
19707
- const assetsURL = function(dep, importerUrl) {
19708
- return new URL(dep, importerUrl).href;
19709
- };
19710
- const seen = {};
19711
- const __vitePreload = function preload(baseModule, deps, importerUrl) {
19712
- let promise = Promise.resolve();
19713
- if (deps && deps.length > 0) {
19714
- let allSettled2 = function(promises) {
19715
- return Promise.all(
19716
- promises.map(
19717
- (p) => Promise.resolve(p).then(
19718
- (value) => ({ status: "fulfilled", value }),
19719
- (reason) => ({ status: "rejected", reason })
19720
- )
19721
- )
19722
- );
19723
- };
19724
- const links = document.getElementsByTagName("link");
19725
- const cspNonceMeta = document.querySelector(
19726
- "meta[property=csp-nonce]"
19727
- );
19728
- const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
19729
- promise = allSettled2(
19730
- deps.map((dep) => {
19731
- dep = assetsURL(dep, importerUrl);
19732
- if (dep in seen) return;
19733
- seen[dep] = true;
19734
- const isCss = dep.endsWith(".css");
19735
- const cssSelector = isCss ? '[rel="stylesheet"]' : "";
19736
- const isBaseRelative = !!importerUrl;
19737
- if (isBaseRelative) {
19738
- for (let i2 = links.length - 1; i2 >= 0; i2--) {
19739
- const link22 = links[i2];
19740
- if (link22.href === dep && (!isCss || link22.rel === "stylesheet")) {
19741
- return;
19742
- }
19743
- }
19744
- } else if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
19745
- return;
19746
- }
19747
- const link2 = document.createElement("link");
19748
- link2.rel = isCss ? "stylesheet" : scriptRel;
19749
- if (!isCss) {
19750
- link2.as = "script";
19751
- }
19752
- link2.crossOrigin = "";
19753
- link2.href = dep;
19754
- if (cspNonce) {
19755
- link2.setAttribute("nonce", cspNonce);
19756
- }
19757
- document.head.appendChild(link2);
19758
- if (isCss) {
19759
- return new Promise((res, rej) => {
19760
- link2.addEventListener("load", res);
19761
- link2.addEventListener(
19762
- "error",
19763
- () => rej(new Error(`Unable to preload CSS for ${dep}`))
19764
- );
19765
- });
19766
- }
19767
- })
19768
- );
19769
- }
19770
- function handlePreloadError(err) {
19771
- const e = new Event("vite:preloadError", {
19772
- cancelable: true
19773
- });
19774
- e.payload = err;
19775
- window.dispatchEvent(e);
19776
- if (!e.defaultPrevented) {
19777
- throw err;
19778
- }
19779
- }
19780
- return promise.then((res) => {
19781
- for (const item of res || []) {
19782
- if (item.status !== "rejected") continue;
19783
- handlePreloadError(item.reason);
19784
- }
19785
- return baseModule().catch(handlePreloadError);
19786
- });
19787
- };
19788
20118
  function getNLSMessages() {
19789
20119
  return globalThis._VSCODE_NLS_MESSAGES;
19790
20120
  }
@@ -206727,7 +207057,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
206727
207057
  modeConfigurationDefault$2
206728
207058
  );
206729
207059
  function getMode$3() {
206730
- return __vitePreload(() => import("./cssMode-DkmdBgO7.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
207060
+ return __vitePreload(() => import("./cssMode-DbMmcl1h.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
206731
207061
  }
206732
207062
  languages.onLanguage("less", () => {
206733
207063
  getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
@@ -206832,7 +207162,7 @@ const razorLanguageService = registerHTMLLanguageService(
206832
207162
  );
206833
207163
  const razorDefaults = razorLanguageService.defaults;
206834
207164
  function getMode$2() {
206835
- return __vitePreload(() => import("./htmlMode-CRicxcwK.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
207165
+ return __vitePreload(() => import("./htmlMode-BdkAp9qr.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
206836
207166
  }
206837
207167
  function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
206838
207168
  const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
@@ -206916,7 +207246,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
206916
207246
  );
206917
207247
  const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
206918
207248
  function getMode$1() {
206919
- return __vitePreload(() => import("./jsonMode-Bquqf3QN.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
207249
+ return __vitePreload(() => import("./jsonMode-BuVr-eSl.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
206920
207250
  }
206921
207251
  languages.register({
206922
207252
  id: "json",
@@ -207162,7 +207492,7 @@ const getJavaScriptWorker = () => {
207162
207492
  return getMode().then((mode) => mode.getJavaScriptWorker());
207163
207493
  };
207164
207494
  function getMode() {
207165
- return __vitePreload(() => import("./tsMode-CRIrHuii.js"), true ? [] : void 0, import.meta.url);
207495
+ return __vitePreload(() => import("./tsMode-CYd3NUkW.js"), true ? [] : void 0, import.meta.url);
207166
207496
  }
207167
207497
  languages.onLanguage("typescript", () => {
207168
207498
  return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
@@ -207357,49 +207687,49 @@ registerLanguage({
207357
207687
  extensions: [".ftl", ".ftlh", ".ftlx"],
207358
207688
  aliases: ["FreeMarker2", "Apache FreeMarker2"],
207359
207689
  loader: () => {
207360
- return __vitePreload(() => import("./freemarker2-CI-gkP-3.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207690
+ return __vitePreload(() => import("./freemarker2-CvaHiy92.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207361
207691
  }
207362
207692
  });
207363
207693
  registerLanguage({
207364
207694
  id: "freemarker2.tag-angle.interpolation-dollar",
207365
207695
  aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
207366
207696
  loader: () => {
207367
- return __vitePreload(() => import("./freemarker2-CI-gkP-3.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
207697
+ return __vitePreload(() => import("./freemarker2-CvaHiy92.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
207368
207698
  }
207369
207699
  });
207370
207700
  registerLanguage({
207371
207701
  id: "freemarker2.tag-bracket.interpolation-dollar",
207372
207702
  aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
207373
207703
  loader: () => {
207374
- return __vitePreload(() => import("./freemarker2-CI-gkP-3.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
207704
+ return __vitePreload(() => import("./freemarker2-CvaHiy92.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
207375
207705
  }
207376
207706
  });
207377
207707
  registerLanguage({
207378
207708
  id: "freemarker2.tag-angle.interpolation-bracket",
207379
207709
  aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
207380
207710
  loader: () => {
207381
- return __vitePreload(() => import("./freemarker2-CI-gkP-3.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
207711
+ return __vitePreload(() => import("./freemarker2-CvaHiy92.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
207382
207712
  }
207383
207713
  });
207384
207714
  registerLanguage({
207385
207715
  id: "freemarker2.tag-bracket.interpolation-bracket",
207386
207716
  aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
207387
207717
  loader: () => {
207388
- return __vitePreload(() => import("./freemarker2-CI-gkP-3.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
207718
+ return __vitePreload(() => import("./freemarker2-CvaHiy92.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
207389
207719
  }
207390
207720
  });
207391
207721
  registerLanguage({
207392
207722
  id: "freemarker2.tag-auto.interpolation-dollar",
207393
207723
  aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
207394
207724
  loader: () => {
207395
- return __vitePreload(() => import("./freemarker2-CI-gkP-3.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207725
+ return __vitePreload(() => import("./freemarker2-CvaHiy92.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207396
207726
  }
207397
207727
  });
207398
207728
  registerLanguage({
207399
207729
  id: "freemarker2.tag-auto.interpolation-bracket",
207400
207730
  aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
207401
207731
  loader: () => {
207402
- return __vitePreload(() => import("./freemarker2-CI-gkP-3.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
207732
+ return __vitePreload(() => import("./freemarker2-CvaHiy92.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
207403
207733
  }
207404
207734
  });
207405
207735
  registerLanguage({
@@ -207420,7 +207750,7 @@ registerLanguage({
207420
207750
  extensions: [".handlebars", ".hbs"],
207421
207751
  aliases: ["Handlebars", "handlebars", "hbs"],
207422
207752
  mimetypes: ["text/x-handlebars-template"],
207423
- loader: () => __vitePreload(() => import("./handlebars-D5tEqanR.js"), true ? [] : void 0, import.meta.url)
207753
+ loader: () => __vitePreload(() => import("./handlebars-D58lUIOu.js"), true ? [] : void 0, import.meta.url)
207424
207754
  });
207425
207755
  registerLanguage({
207426
207756
  id: "hcl",
@@ -207433,7 +207763,7 @@ registerLanguage({
207433
207763
  extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
207434
207764
  aliases: ["HTML", "htm", "html", "xhtml"],
207435
207765
  mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
207436
- loader: () => __vitePreload(() => import("./html-fH93EYfn.js"), true ? [] : void 0, import.meta.url)
207766
+ loader: () => __vitePreload(() => import("./html-D1h1aJbM.js"), true ? [] : void 0, import.meta.url)
207437
207767
  });
207438
207768
  registerLanguage({
207439
207769
  id: "ini",
@@ -207456,7 +207786,7 @@ registerLanguage({
207456
207786
  filenames: ["jakefile"],
207457
207787
  aliases: ["JavaScript", "javascript", "js"],
207458
207788
  mimetypes: ["text/javascript"],
207459
- loader: () => __vitePreload(() => import("./javascript-D1Baz4fV.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
207789
+ loader: () => __vitePreload(() => import("./javascript-CXqZcnvb.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
207460
207790
  });
207461
207791
  registerLanguage({
207462
207792
  id: "julia",
@@ -207495,7 +207825,7 @@ registerLanguage({
207495
207825
  extensions: [".liquid", ".html.liquid"],
207496
207826
  aliases: ["Liquid", "liquid"],
207497
207827
  mimetypes: ["application/liquid"],
207498
- loader: () => __vitePreload(() => import("./liquid-ByOcPjBF.js"), true ? [] : void 0, import.meta.url)
207828
+ loader: () => __vitePreload(() => import("./liquid-LKu0Wd0B.js"), true ? [] : void 0, import.meta.url)
207499
207829
  });
207500
207830
  registerLanguage({
207501
207831
  id: "m3",
@@ -207513,7 +207843,7 @@ registerLanguage({
207513
207843
  id: "mdx",
207514
207844
  extensions: [".mdx"],
207515
207845
  aliases: ["MDX", "mdx"],
207516
- loader: () => __vitePreload(() => import("./mdx-yuNgx0rM.js"), true ? [] : void 0, import.meta.url)
207846
+ loader: () => __vitePreload(() => import("./mdx-Bl84ILla.js"), true ? [] : void 0, import.meta.url)
207517
207847
  });
207518
207848
  registerLanguage({
207519
207849
  id: "mips",
@@ -207612,7 +207942,7 @@ registerLanguage({
207612
207942
  extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
207613
207943
  aliases: ["Python", "py"],
207614
207944
  firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
207615
- loader: () => __vitePreload(() => import("./python-2OakgLlA.js"), true ? [] : void 0, import.meta.url)
207945
+ loader: () => __vitePreload(() => import("./python-0sFd9G1k.js"), true ? [] : void 0, import.meta.url)
207616
207946
  });
207617
207947
  registerLanguage({
207618
207948
  id: "qsharp",
@@ -207631,7 +207961,7 @@ registerLanguage({
207631
207961
  extensions: [".cshtml"],
207632
207962
  aliases: ["Razor", "razor"],
207633
207963
  mimetypes: ["text/x-cshtml"],
207634
- loader: () => __vitePreload(() => import("./razor-DnIVMSwa.js"), true ? [] : void 0, import.meta.url)
207964
+ loader: () => __vitePreload(() => import("./razor-Cqcu1rLJ.js"), true ? [] : void 0, import.meta.url)
207635
207965
  });
207636
207966
  registerLanguage({
207637
207967
  id: "redis",
@@ -207764,7 +208094,7 @@ registerLanguage({
207764
208094
  aliases: ["TypeScript", "ts", "typescript"],
207765
208095
  mimetypes: ["text/typescript"],
207766
208096
  loader: () => {
207767
- return __vitePreload(() => import("./typescript-DJ3C8Yly.js"), true ? [] : void 0, import.meta.url);
208097
+ return __vitePreload(() => import("./typescript-rkc9lhpi.js"), true ? [] : void 0, import.meta.url);
207768
208098
  }
207769
208099
  });
207770
208100
  registerLanguage({
@@ -207809,14 +208139,14 @@ registerLanguage({
207809
208139
  firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
207810
208140
  aliases: ["XML", "xml"],
207811
208141
  mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
207812
- loader: () => __vitePreload(() => import("./xml-CalvD5_C.js"), true ? [] : void 0, import.meta.url)
208142
+ loader: () => __vitePreload(() => import("./xml-EsHEUps1.js"), true ? [] : void 0, import.meta.url)
207813
208143
  });
207814
208144
  registerLanguage({
207815
208145
  id: "yaml",
207816
208146
  extensions: [".yaml", ".yml"],
207817
208147
  aliases: ["YAML", "yaml", "YML", "yml"],
207818
208148
  mimetypes: ["application/x-yaml", "text/x-yaml"],
207819
- loader: () => __vitePreload(() => import("./yaml-Cgs8pdVp.js"), true ? [] : void 0, import.meta.url)
208149
+ loader: () => __vitePreload(() => import("./yaml-B9-nQ_s2.js"), true ? [] : void 0, import.meta.url)
207820
208150
  });
207821
208151
  var __defProp = Object.defineProperty;
207822
208152
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -212210,6 +212540,8 @@ function App() {
212210
212540
  const [cwd2, setCwd] = reactExports.useState(null);
212211
212541
  const [projectName, setProjectName] = reactExports.useState(null);
212212
212542
  const [updateInfo, setUpdateInfo] = reactExports.useState(null);
212543
+ const [ctlsurfNav, setCtlsurfNav] = reactExports.useState(null);
212544
+ const ctlsurfNavCounter = reactExports.useRef(0);
212213
212545
  const [tabs, setTabs] = reactExports.useState(() => {
212214
212546
  const id = nextTabId();
212215
212547
  return [{ id, label: "Terminal 1", agent: null, agentStatus: "idle" }];
@@ -212284,6 +212616,12 @@ function App() {
212284
212616
  console.error("[tracking] toggle failed", err);
212285
212617
  }
212286
212618
  }, [trackingActive]);
212619
+ const handleVoiceTranscript = reactExports.useCallback((text2) => {
212620
+ const trimmed = text2.trim();
212621
+ if (!trimmed) return;
212622
+ window.worker.writePty(activeTabId, trimmed);
212623
+ focusTerminal(activeTabId);
212624
+ }, [activeTabId]);
212287
212625
  const cwdRef = reactExports.useRef(null);
212288
212626
  const handleSpawn = reactExports.useCallback(async (tabId, agent) => {
212289
212627
  setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "active" } : t));
@@ -212418,6 +212756,17 @@ function App() {
212418
212756
  window.addEventListener("keydown", handleKeyDown);
212419
212757
  return () => window.removeEventListener("keydown", handleKeyDown);
212420
212758
  }, [togglePane]);
212759
+ reactExports.useEffect(() => {
212760
+ const onOpen = (e) => {
212761
+ const url = e.detail?.url;
212762
+ if (!url) return;
212763
+ if (!findPaneIds(layout2).includes("ctlsurf")) togglePane("ctlsurf");
212764
+ ctlsurfNavCounter.current += 1;
212765
+ setCtlsurfNav({ url, n: ctlsurfNavCounter.current });
212766
+ };
212767
+ window.addEventListener("ctlsurf-open-url", onOpen);
212768
+ return () => window.removeEventListener("ctlsurf-open-url", onOpen);
212769
+ }, [layout2, togglePane]);
212421
212770
  const panes = [
212422
212771
  { id: "editor", label: "Editor", content: /* @__PURE__ */ jsxRuntimeExports.jsx(EditorPanel, { cwd: cwd2 }) },
212423
212772
  {
@@ -212477,7 +212826,7 @@ function App() {
212477
212826
  )) })
212478
212827
  ] })
212479
212828
  },
212480
- { id: "ctlsurf", label: "ctlsurf", content: /* @__PURE__ */ jsxRuntimeExports.jsx(CtlsurfPanel, {}) }
212829
+ { id: "ctlsurf", label: "ctlsurf", content: /* @__PURE__ */ jsxRuntimeExports.jsx(CtlsurfPanel, { navigate: ctlsurfNav }) }
212481
212830
  ];
212482
212831
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "app", children: [
212483
212832
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "titlebar", children: [
@@ -212534,6 +212883,7 @@ function App() {
212534
212883
  ]
212535
212884
  }
212536
212885
  ),
212886
+ /* @__PURE__ */ jsxRuntimeExports.jsx(VoiceInput, { onTranscript: handleVoiceTranscript }),
212537
212887
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
212538
212888
  agents.map((a) => {
212539
212889
  const activeTab = tabs.find((t) => t.id === activeTabId);