@phenx-inc/ctlsurf 0.5.2 → 0.7.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 (33) 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 +3 -0
  5. package/out/renderer/assets/{cssMode-D9-xaWSI.js → cssMode-eTXVdAkZ.js} +3 -3
  6. package/out/renderer/assets/{freemarker2-CoRAVxnv.js → freemarker2-B5BKaiK4.js} +1 -1
  7. package/out/renderer/assets/{handlebars-B0p9Wgkw.js → handlebars-BIdLd2wU.js} +1 -1
  8. package/out/renderer/assets/{html-D_XFJJtO.js → html-BXL4cnLS.js} +1 -1
  9. package/out/renderer/assets/{htmlMode-naWw6PWr.js → htmlMode-46N3XG2c.js} +3 -3
  10. package/out/renderer/assets/{index-ezC-iarf.css → index-Cf-RsxoC.css} +163 -0
  11. package/out/renderer/assets/{index-DBt_rov1.js → index-dRvutfbl.js} +572 -107
  12. package/out/renderer/assets/{javascript-DDLsFUr-.js → javascript-n_iZZzDX.js} +2 -2
  13. package/out/renderer/assets/{jsonMode-Ixhcm5I6.js → jsonMode-DXDczSNu.js} +3 -3
  14. package/out/renderer/assets/{liquid-BHgSYEHk.js → liquid-B1QweUh7.js} +1 -1
  15. package/out/renderer/assets/{lspLanguageFeatures-ClbEdD0U.js → lspLanguageFeatures-DqzMqkRk.js} +1 -1
  16. package/out/renderer/assets/{mdx-DMngMjHR.js → mdx-BCv8lm5e.js} +1 -1
  17. package/out/renderer/assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm +0 -0
  18. package/out/renderer/assets/{python-D_czoeY2.js → python-BLNzYwDv.js} +1 -1
  19. package/out/renderer/assets/{razor-CLMDGvL7.js → razor-CvAww8bG.js} +1 -1
  20. package/out/renderer/assets/transformers.web-DtSCnG36.js +33668 -0
  21. package/out/renderer/assets/{tsMode-EIuSGG42.js → tsMode-C7m6Kr5E.js} +1 -1
  22. package/out/renderer/assets/{typescript-DQkV4kKA.js → typescript-DhPw4VVg.js} +1 -1
  23. package/out/renderer/assets/{xml-DJ0OOQTu.js → xml-B0WLFJ2U.js} +1 -1
  24. package/out/renderer/assets/{yaml-DxX26XLN.js → yaml-BWyn9Wd7.js} +1 -1
  25. package/out/renderer/index.html +2 -2
  26. package/package.json +2 -1
  27. package/src/main/index.ts +7 -0
  28. package/src/renderer/App.tsx +41 -1
  29. package/src/renderer/components/FloatingMic.tsx +128 -0
  30. package/src/renderer/components/TerminalPanel.tsx +6 -0
  31. package/src/renderer/components/VoiceInput.tsx +321 -0
  32. package/src/renderer/lib/localWhisper.ts +88 -0
  33. package/src/renderer/styles.css +163 -0
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-D9-xaWSI.js","./lspLanguageFeatures-ClbEdD0U.js","./htmlMode-naWw6PWr.js","./jsonMode-Ixhcm5I6.js","./javascript-DDLsFUr-.js","./typescript-DQkV4kKA.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-eTXVdAkZ.js","./lspLanguageFeatures-DqzMqkRk.js","./htmlMode-46N3XG2c.js","./jsonMode-DXDczSNu.js","./javascript-n_iZZzDX.js","./typescript-DhPw4VVg.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
  }
@@ -18940,6 +18940,9 @@ function getOrCreateTerminal(tabId, onExit) {
18940
18940
  _terminals.set(tabId, state);
18941
18941
  return { terminal, fitAddon };
18942
18942
  }
18943
+ function focusTerminal(tabId) {
18944
+ _terminals.get(tabId)?.terminal.focus();
18945
+ }
18943
18946
  function destroyTerminal(tabId) {
18944
18947
  const state = _terminals.get(tabId);
18945
18948
  if (!state) return;
@@ -19073,6 +19076,497 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
19073
19076
  toast && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "term-toast", children: toast })
19074
19077
  ] });
19075
19078
  }
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, variant = "titlebar" }) {
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
+ const floating = variant === "floating";
19445
+ const btnClass = floating ? `voice-btn voice-btn-floating ${listening ? "listening" : ""} ${busy ? "busy" : ""}` : `titlebar-btn titlebar-icon-btn voice-btn ${listening ? "listening" : ""} ${busy ? "busy" : ""}`;
19446
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "voice-input-wrap", children: [
19447
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
19448
+ "button",
19449
+ {
19450
+ type: "button",
19451
+ className: btnClass,
19452
+ disabled: !ANY_SUPPORTED,
19453
+ onPointerDown: handlePointerDown,
19454
+ onPointerUp: handlePointerUp,
19455
+ onPointerCancel: handlePointerUp,
19456
+ onContextMenu: (e) => e.preventDefault(),
19457
+ title,
19458
+ "aria-label": "Voice typing (hold to talk)",
19459
+ children: [
19460
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "voice-icon", "aria-hidden": "true", children: "🎤" }),
19461
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `voice-dot ${listening ? "on" : busy ? "busy" : "off"}` })
19462
+ ]
19463
+ }
19464
+ ),
19465
+ chip && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `voice-chip ${chip.kind} ${floating ? "voice-chip-floating" : ""}`, children: chip.text })
19466
+ ] });
19467
+ }
19468
+ const POS_KEY = "ctlsurf.floatingMicPos";
19469
+ const EDGE = 20;
19470
+ const TOP_MIN = 46;
19471
+ const BOTTOM_GAP = 36;
19472
+ function loadPos() {
19473
+ try {
19474
+ const raw = localStorage.getItem(POS_KEY);
19475
+ if (raw) {
19476
+ const p = JSON.parse(raw);
19477
+ if (typeof p.x === "number" && typeof p.y === "number") return { x: p.x, y: p.y };
19478
+ }
19479
+ } catch {
19480
+ }
19481
+ return null;
19482
+ }
19483
+ function FloatingMic({ onTranscript, onHide }) {
19484
+ const [pos, setPos] = reactExports.useState(loadPos);
19485
+ const elRef = reactExports.useRef(null);
19486
+ const dragRef = reactExports.useRef(null);
19487
+ const clamp2 = reactExports.useCallback((x, y) => {
19488
+ const el = elRef.current;
19489
+ const w = el?.offsetWidth ?? 64;
19490
+ const h2 = el?.offsetHeight ?? 90;
19491
+ return {
19492
+ x: Math.max(EDGE, Math.min(x, window.innerWidth - w - EDGE)),
19493
+ y: Math.max(TOP_MIN, Math.min(y, window.innerHeight - h2 - BOTTOM_GAP))
19494
+ };
19495
+ }, []);
19496
+ reactExports.useEffect(() => {
19497
+ if (pos) return;
19498
+ const el = elRef.current;
19499
+ const w = el?.offsetWidth ?? 64;
19500
+ const h2 = el?.offsetHeight ?? 90;
19501
+ setPos({
19502
+ x: window.innerWidth - w - EDGE,
19503
+ y: window.innerHeight - h2 - BOTTOM_GAP
19504
+ });
19505
+ }, [pos]);
19506
+ reactExports.useEffect(() => {
19507
+ const onResize = () => setPos((p) => p ? clamp2(p.x, p.y) : p);
19508
+ window.addEventListener("resize", onResize);
19509
+ return () => window.removeEventListener("resize", onResize);
19510
+ }, [clamp2]);
19511
+ const onHandleDown = reactExports.useCallback((e) => {
19512
+ const el = elRef.current;
19513
+ if (!el) return;
19514
+ e.preventDefault();
19515
+ const rect = el.getBoundingClientRect();
19516
+ dragRef.current = { dx: e.clientX - rect.left, dy: e.clientY - rect.top };
19517
+ e.currentTarget.setPointerCapture?.(e.pointerId);
19518
+ }, []);
19519
+ const onHandleMove = reactExports.useCallback((e) => {
19520
+ const d = dragRef.current;
19521
+ if (!d) return;
19522
+ setPos(clamp2(e.clientX - d.dx, e.clientY - d.dy));
19523
+ }, [clamp2]);
19524
+ const onHandleUp = reactExports.useCallback((e) => {
19525
+ if (!dragRef.current) return;
19526
+ dragRef.current = null;
19527
+ e.currentTarget.releasePointerCapture?.(e.pointerId);
19528
+ setPos((p) => {
19529
+ if (p) {
19530
+ try {
19531
+ localStorage.setItem(POS_KEY, JSON.stringify(p));
19532
+ } catch {
19533
+ }
19534
+ }
19535
+ return p;
19536
+ });
19537
+ }, []);
19538
+ const style = pos ? { left: pos.x, top: pos.y } : { left: -9999, top: -9999, visibility: "hidden" };
19539
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: elRef, className: "floating-mic", style, children: [
19540
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
19541
+ "div",
19542
+ {
19543
+ className: "floating-mic-handle",
19544
+ onPointerDown: onHandleDown,
19545
+ onPointerMove: onHandleMove,
19546
+ onPointerUp: onHandleUp,
19547
+ onPointerCancel: onHandleUp,
19548
+ title: "Drag to move",
19549
+ "aria-label": "Drag floating mic",
19550
+ children: [
19551
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "floating-mic-grip", "aria-hidden": "true", children: "⠿" }),
19552
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
19553
+ "button",
19554
+ {
19555
+ type: "button",
19556
+ className: "floating-mic-hide",
19557
+ onPointerDown: (e) => e.stopPropagation(),
19558
+ onClick: onHide,
19559
+ title: "Hide floating mic",
19560
+ "aria-label": "Hide floating mic",
19561
+ children: "×"
19562
+ }
19563
+ )
19564
+ ]
19565
+ }
19566
+ ),
19567
+ /* @__PURE__ */ jsxRuntimeExports.jsx(VoiceInput, { variant: "floating", onTranscript })
19568
+ ] });
19569
+ }
19076
19570
  function CtlsurfPanel({ navigate }) {
19077
19571
  const webviewRef = reactExports.useRef(null);
19078
19572
  const [url, setUrl] = reactExports.useState(null);
@@ -19725,91 +20219,6 @@ function Ve({ defaultValue: e, defaultLanguage: r, defaultPath: n2, value: t, la
19725
20219
  var fe = Ve;
19726
20220
  var de = reactExports.memo(fe);
19727
20221
  var Ft = de;
19728
- const scriptRel = /* @__PURE__ */ (function detectScriptRel() {
19729
- const relList = typeof document !== "undefined" && document.createElement("link").relList;
19730
- return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
19731
- })();
19732
- const assetsURL = function(dep, importerUrl) {
19733
- return new URL(dep, importerUrl).href;
19734
- };
19735
- const seen = {};
19736
- const __vitePreload = function preload(baseModule, deps, importerUrl) {
19737
- let promise = Promise.resolve();
19738
- if (deps && deps.length > 0) {
19739
- let allSettled2 = function(promises) {
19740
- return Promise.all(
19741
- promises.map(
19742
- (p) => Promise.resolve(p).then(
19743
- (value) => ({ status: "fulfilled", value }),
19744
- (reason) => ({ status: "rejected", reason })
19745
- )
19746
- )
19747
- );
19748
- };
19749
- const links = document.getElementsByTagName("link");
19750
- const cspNonceMeta = document.querySelector(
19751
- "meta[property=csp-nonce]"
19752
- );
19753
- const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
19754
- promise = allSettled2(
19755
- deps.map((dep) => {
19756
- dep = assetsURL(dep, importerUrl);
19757
- if (dep in seen) return;
19758
- seen[dep] = true;
19759
- const isCss = dep.endsWith(".css");
19760
- const cssSelector = isCss ? '[rel="stylesheet"]' : "";
19761
- const isBaseRelative = !!importerUrl;
19762
- if (isBaseRelative) {
19763
- for (let i2 = links.length - 1; i2 >= 0; i2--) {
19764
- const link22 = links[i2];
19765
- if (link22.href === dep && (!isCss || link22.rel === "stylesheet")) {
19766
- return;
19767
- }
19768
- }
19769
- } else if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
19770
- return;
19771
- }
19772
- const link2 = document.createElement("link");
19773
- link2.rel = isCss ? "stylesheet" : scriptRel;
19774
- if (!isCss) {
19775
- link2.as = "script";
19776
- }
19777
- link2.crossOrigin = "";
19778
- link2.href = dep;
19779
- if (cspNonce) {
19780
- link2.setAttribute("nonce", cspNonce);
19781
- }
19782
- document.head.appendChild(link2);
19783
- if (isCss) {
19784
- return new Promise((res, rej) => {
19785
- link2.addEventListener("load", res);
19786
- link2.addEventListener(
19787
- "error",
19788
- () => rej(new Error(`Unable to preload CSS for ${dep}`))
19789
- );
19790
- });
19791
- }
19792
- })
19793
- );
19794
- }
19795
- function handlePreloadError(err) {
19796
- const e = new Event("vite:preloadError", {
19797
- cancelable: true
19798
- });
19799
- e.payload = err;
19800
- window.dispatchEvent(e);
19801
- if (!e.defaultPrevented) {
19802
- throw err;
19803
- }
19804
- }
19805
- return promise.then((res) => {
19806
- for (const item of res || []) {
19807
- if (item.status !== "rejected") continue;
19808
- handlePreloadError(item.reason);
19809
- }
19810
- return baseModule().catch(handlePreloadError);
19811
- });
19812
- };
19813
20222
  function getNLSMessages() {
19814
20223
  return globalThis._VSCODE_NLS_MESSAGES;
19815
20224
  }
@@ -206752,7 +207161,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
206752
207161
  modeConfigurationDefault$2
206753
207162
  );
206754
207163
  function getMode$3() {
206755
- return __vitePreload(() => import("./cssMode-D9-xaWSI.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
207164
+ return __vitePreload(() => import("./cssMode-eTXVdAkZ.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
206756
207165
  }
206757
207166
  languages.onLanguage("less", () => {
206758
207167
  getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
@@ -206857,7 +207266,7 @@ const razorLanguageService = registerHTMLLanguageService(
206857
207266
  );
206858
207267
  const razorDefaults = razorLanguageService.defaults;
206859
207268
  function getMode$2() {
206860
- return __vitePreload(() => import("./htmlMode-naWw6PWr.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
207269
+ return __vitePreload(() => import("./htmlMode-46N3XG2c.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
206861
207270
  }
206862
207271
  function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
206863
207272
  const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
@@ -206941,7 +207350,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
206941
207350
  );
206942
207351
  const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
206943
207352
  function getMode$1() {
206944
- return __vitePreload(() => import("./jsonMode-Ixhcm5I6.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
207353
+ return __vitePreload(() => import("./jsonMode-DXDczSNu.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
206945
207354
  }
206946
207355
  languages.register({
206947
207356
  id: "json",
@@ -207187,7 +207596,7 @@ const getJavaScriptWorker = () => {
207187
207596
  return getMode().then((mode) => mode.getJavaScriptWorker());
207188
207597
  };
207189
207598
  function getMode() {
207190
- return __vitePreload(() => import("./tsMode-EIuSGG42.js"), true ? [] : void 0, import.meta.url);
207599
+ return __vitePreload(() => import("./tsMode-C7m6Kr5E.js"), true ? [] : void 0, import.meta.url);
207191
207600
  }
207192
207601
  languages.onLanguage("typescript", () => {
207193
207602
  return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
@@ -207382,49 +207791,49 @@ registerLanguage({
207382
207791
  extensions: [".ftl", ".ftlh", ".ftlx"],
207383
207792
  aliases: ["FreeMarker2", "Apache FreeMarker2"],
207384
207793
  loader: () => {
207385
- return __vitePreload(() => import("./freemarker2-CoRAVxnv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207794
+ return __vitePreload(() => import("./freemarker2-B5BKaiK4.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207386
207795
  }
207387
207796
  });
207388
207797
  registerLanguage({
207389
207798
  id: "freemarker2.tag-angle.interpolation-dollar",
207390
207799
  aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
207391
207800
  loader: () => {
207392
- return __vitePreload(() => import("./freemarker2-CoRAVxnv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
207801
+ return __vitePreload(() => import("./freemarker2-B5BKaiK4.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
207393
207802
  }
207394
207803
  });
207395
207804
  registerLanguage({
207396
207805
  id: "freemarker2.tag-bracket.interpolation-dollar",
207397
207806
  aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
207398
207807
  loader: () => {
207399
- return __vitePreload(() => import("./freemarker2-CoRAVxnv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
207808
+ return __vitePreload(() => import("./freemarker2-B5BKaiK4.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
207400
207809
  }
207401
207810
  });
207402
207811
  registerLanguage({
207403
207812
  id: "freemarker2.tag-angle.interpolation-bracket",
207404
207813
  aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
207405
207814
  loader: () => {
207406
- return __vitePreload(() => import("./freemarker2-CoRAVxnv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
207815
+ return __vitePreload(() => import("./freemarker2-B5BKaiK4.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
207407
207816
  }
207408
207817
  });
207409
207818
  registerLanguage({
207410
207819
  id: "freemarker2.tag-bracket.interpolation-bracket",
207411
207820
  aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
207412
207821
  loader: () => {
207413
- return __vitePreload(() => import("./freemarker2-CoRAVxnv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
207822
+ return __vitePreload(() => import("./freemarker2-B5BKaiK4.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
207414
207823
  }
207415
207824
  });
207416
207825
  registerLanguage({
207417
207826
  id: "freemarker2.tag-auto.interpolation-dollar",
207418
207827
  aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
207419
207828
  loader: () => {
207420
- return __vitePreload(() => import("./freemarker2-CoRAVxnv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207829
+ return __vitePreload(() => import("./freemarker2-B5BKaiK4.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
207421
207830
  }
207422
207831
  });
207423
207832
  registerLanguage({
207424
207833
  id: "freemarker2.tag-auto.interpolation-bracket",
207425
207834
  aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
207426
207835
  loader: () => {
207427
- return __vitePreload(() => import("./freemarker2-CoRAVxnv.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
207836
+ return __vitePreload(() => import("./freemarker2-B5BKaiK4.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
207428
207837
  }
207429
207838
  });
207430
207839
  registerLanguage({
@@ -207445,7 +207854,7 @@ registerLanguage({
207445
207854
  extensions: [".handlebars", ".hbs"],
207446
207855
  aliases: ["Handlebars", "handlebars", "hbs"],
207447
207856
  mimetypes: ["text/x-handlebars-template"],
207448
- loader: () => __vitePreload(() => import("./handlebars-B0p9Wgkw.js"), true ? [] : void 0, import.meta.url)
207857
+ loader: () => __vitePreload(() => import("./handlebars-BIdLd2wU.js"), true ? [] : void 0, import.meta.url)
207449
207858
  });
207450
207859
  registerLanguage({
207451
207860
  id: "hcl",
@@ -207458,7 +207867,7 @@ registerLanguage({
207458
207867
  extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
207459
207868
  aliases: ["HTML", "htm", "html", "xhtml"],
207460
207869
  mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
207461
- loader: () => __vitePreload(() => import("./html-D_XFJJtO.js"), true ? [] : void 0, import.meta.url)
207870
+ loader: () => __vitePreload(() => import("./html-BXL4cnLS.js"), true ? [] : void 0, import.meta.url)
207462
207871
  });
207463
207872
  registerLanguage({
207464
207873
  id: "ini",
@@ -207481,7 +207890,7 @@ registerLanguage({
207481
207890
  filenames: ["jakefile"],
207482
207891
  aliases: ["JavaScript", "javascript", "js"],
207483
207892
  mimetypes: ["text/javascript"],
207484
- loader: () => __vitePreload(() => import("./javascript-DDLsFUr-.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
207893
+ loader: () => __vitePreload(() => import("./javascript-n_iZZzDX.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
207485
207894
  });
207486
207895
  registerLanguage({
207487
207896
  id: "julia",
@@ -207520,7 +207929,7 @@ registerLanguage({
207520
207929
  extensions: [".liquid", ".html.liquid"],
207521
207930
  aliases: ["Liquid", "liquid"],
207522
207931
  mimetypes: ["application/liquid"],
207523
- loader: () => __vitePreload(() => import("./liquid-BHgSYEHk.js"), true ? [] : void 0, import.meta.url)
207932
+ loader: () => __vitePreload(() => import("./liquid-B1QweUh7.js"), true ? [] : void 0, import.meta.url)
207524
207933
  });
207525
207934
  registerLanguage({
207526
207935
  id: "m3",
@@ -207538,7 +207947,7 @@ registerLanguage({
207538
207947
  id: "mdx",
207539
207948
  extensions: [".mdx"],
207540
207949
  aliases: ["MDX", "mdx"],
207541
- loader: () => __vitePreload(() => import("./mdx-DMngMjHR.js"), true ? [] : void 0, import.meta.url)
207950
+ loader: () => __vitePreload(() => import("./mdx-BCv8lm5e.js"), true ? [] : void 0, import.meta.url)
207542
207951
  });
207543
207952
  registerLanguage({
207544
207953
  id: "mips",
@@ -207637,7 +208046,7 @@ registerLanguage({
207637
208046
  extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
207638
208047
  aliases: ["Python", "py"],
207639
208048
  firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
207640
- loader: () => __vitePreload(() => import("./python-D_czoeY2.js"), true ? [] : void 0, import.meta.url)
208049
+ loader: () => __vitePreload(() => import("./python-BLNzYwDv.js"), true ? [] : void 0, import.meta.url)
207641
208050
  });
207642
208051
  registerLanguage({
207643
208052
  id: "qsharp",
@@ -207656,7 +208065,7 @@ registerLanguage({
207656
208065
  extensions: [".cshtml"],
207657
208066
  aliases: ["Razor", "razor"],
207658
208067
  mimetypes: ["text/x-cshtml"],
207659
- loader: () => __vitePreload(() => import("./razor-CLMDGvL7.js"), true ? [] : void 0, import.meta.url)
208068
+ loader: () => __vitePreload(() => import("./razor-CvAww8bG.js"), true ? [] : void 0, import.meta.url)
207660
208069
  });
207661
208070
  registerLanguage({
207662
208071
  id: "redis",
@@ -207789,7 +208198,7 @@ registerLanguage({
207789
208198
  aliases: ["TypeScript", "ts", "typescript"],
207790
208199
  mimetypes: ["text/typescript"],
207791
208200
  loader: () => {
207792
- return __vitePreload(() => import("./typescript-DQkV4kKA.js"), true ? [] : void 0, import.meta.url);
208201
+ return __vitePreload(() => import("./typescript-DhPw4VVg.js"), true ? [] : void 0, import.meta.url);
207793
208202
  }
207794
208203
  });
207795
208204
  registerLanguage({
@@ -207834,14 +208243,14 @@ registerLanguage({
207834
208243
  firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
207835
208244
  aliases: ["XML", "xml"],
207836
208245
  mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
207837
- loader: () => __vitePreload(() => import("./xml-DJ0OOQTu.js"), true ? [] : void 0, import.meta.url)
208246
+ loader: () => __vitePreload(() => import("./xml-B0WLFJ2U.js"), true ? [] : void 0, import.meta.url)
207838
208247
  });
207839
208248
  registerLanguage({
207840
208249
  id: "yaml",
207841
208250
  extensions: [".yaml", ".yml"],
207842
208251
  aliases: ["YAML", "yaml", "YML", "yml"],
207843
208252
  mimetypes: ["application/x-yaml", "text/x-yaml"],
207844
- loader: () => __vitePreload(() => import("./yaml-DxX26XLN.js"), true ? [] : void 0, import.meta.url)
208253
+ loader: () => __vitePreload(() => import("./yaml-BWyn9Wd7.js"), true ? [] : void 0, import.meta.url)
207845
208254
  });
207846
208255
  var __defProp = Object.defineProperty;
207847
208256
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -212244,6 +212653,20 @@ function App() {
212244
212653
  const [activeTabId, setActiveTabId] = reactExports.useState(tabs[0].id);
212245
212654
  const [trackingActive, setTrackingActive] = reactExports.useState(false);
212246
212655
  const [showTicketPanel, setShowTicketPanel] = reactExports.useState(false);
212656
+ const [showFloatingMic, setShowFloatingMic] = reactExports.useState(() => {
212657
+ try {
212658
+ return localStorage.getItem("ctlsurf.floatingMicVisible") !== "false";
212659
+ } catch {
212660
+ return true;
212661
+ }
212662
+ });
212663
+ const setFloatingMicVisible = reactExports.useCallback((v2) => {
212664
+ setShowFloatingMic(v2);
212665
+ try {
212666
+ localStorage.setItem("ctlsurf.floatingMicVisible", String(v2));
212667
+ } catch {
212668
+ }
212669
+ }, []);
212247
212670
  const [pickerTargetTabId, setPickerTargetTabId] = reactExports.useState(tabs[0].id);
212248
212671
  const [showAgentPicker, setShowAgentPicker] = reactExports.useState(true);
212249
212672
  const visiblePaneIds = findPaneIds(layout2);
@@ -212311,6 +212734,12 @@ function App() {
212311
212734
  console.error("[tracking] toggle failed", err);
212312
212735
  }
212313
212736
  }, [trackingActive]);
212737
+ const handleVoiceTranscript = reactExports.useCallback((text2) => {
212738
+ const trimmed = text2.trim();
212739
+ if (!trimmed) return;
212740
+ window.worker.writePty(activeTabId, trimmed + "\r");
212741
+ focusTerminal(activeTabId);
212742
+ }, [activeTabId]);
212314
212743
  const cwdRef = reactExports.useRef(null);
212315
212744
  const handleSpawn = reactExports.useCallback(async (tabId, agent) => {
212316
212745
  setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, agentStatus: "active" } : t));
@@ -212572,6 +213001,35 @@ function App() {
212572
213001
  ]
212573
213002
  }
212574
213003
  ),
213004
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
213005
+ "button",
213006
+ {
213007
+ className: `titlebar-btn titlebar-icon-btn ${showFloatingMic ? "active" : ""}`,
213008
+ onClick: () => setFloatingMicVisible(!showFloatingMic),
213009
+ title: showFloatingMic ? "Hide floating mic" : "Show floating mic",
213010
+ "aria-label": "Toggle floating mic",
213011
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
213012
+ "svg",
213013
+ {
213014
+ viewBox: "0 0 24 24",
213015
+ width: "13",
213016
+ height: "13",
213017
+ fill: "none",
213018
+ stroke: "currentColor",
213019
+ strokeWidth: "2",
213020
+ strokeLinecap: "round",
213021
+ strokeLinejoin: "round",
213022
+ "aria-hidden": "true",
213023
+ children: [
213024
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
213025
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
213026
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
213027
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
213028
+ ]
213029
+ }
213030
+ )
213031
+ }
213032
+ ),
212575
213033
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "titlebar-separator" }),
212576
213034
  agents.map((a) => {
212577
213035
  const activeTab = tabs.find((t) => t.id === activeTabId);
@@ -212625,6 +213083,13 @@ function App() {
212625
213083
  }
212626
213084
  }
212627
213085
  }
213086
+ ),
213087
+ showFloatingMic && /* @__PURE__ */ jsxRuntimeExports.jsx(
213088
+ FloatingMic,
213089
+ {
213090
+ onTranscript: handleVoiceTranscript,
213091
+ onHide: () => setFloatingMicVisible(false)
213092
+ }
212628
213093
  )
212629
213094
  ] });
212630
213095
  }