@phenx-inc/ctlsurf 0.13.1 → 0.13.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/out/headless/index.mjs +9 -23
- package/out/headless/index.mjs.map +3 -3
- package/out/main/index.js +17 -22
- package/out/preload/index.js +1 -0
- package/out/renderer/assets/{cssMode-KhYyY-BM.js → cssMode-CAEObGNt.js} +3 -3
- package/out/renderer/assets/{freemarker2-BFsSgcWk.js → freemarker2-D63hnnDq.js} +1 -1
- package/out/renderer/assets/{handlebars-Be6_aBsm.js → handlebars-UYo_IYpi.js} +1 -1
- package/out/renderer/assets/{html-G8mvsyfc.js → html-2dvYDi3g.js} +1 -1
- package/out/renderer/assets/{htmlMode-Cfd5VCjw.js → htmlMode-B6fxDDPF.js} +3 -3
- package/out/renderer/assets/{index-Bi_MjBqs.css → index-BEekQe3-.css} +116 -0
- package/out/renderer/assets/{index-MqgBcJzy.js → index-CMb4AqkZ.js} +552 -411
- package/out/renderer/assets/{javascript-XyDWi6dC.js → javascript-8RyFDDdk.js} +2 -2
- package/out/renderer/assets/{jsonMode-DZT5gBVW.js → jsonMode-CqspkWCb.js} +3 -3
- package/out/renderer/assets/{liquid-BuajCNJ9.js → liquid-DaEnAsYY.js} +1 -1
- package/out/renderer/assets/{lspLanguageFeatures-BYkg2Yc-.js → lspLanguageFeatures-Czkp5sZb.js} +1 -1
- package/out/renderer/assets/{mdx-D0Qx2GMS.js → mdx-Q-qSZsFg.js} +1 -1
- package/out/renderer/assets/{python-ChncNy6S.js → python-Sw0S0BJV.js} +1 -1
- package/out/renderer/assets/{razor-C6MUtfaS.js → razor-C7zQmabz.js} +1 -1
- package/out/renderer/assets/{tsMode-C2aMdzdU.js → tsMode-BWdOxk6v.js} +1 -1
- package/out/renderer/assets/{typescript-CB71AtAT.js → typescript-BrdIiKZf.js} +1 -1
- package/out/renderer/assets/{xml-tHS1Ec9w.js → xml-DbKLNPOq.js} +1 -1
- package/out/renderer/assets/{yaml-CZcp5ueF.js → yaml-ehupJBsZ.js} +1 -1
- package/out/renderer/index.html +2 -2
- package/package.json +2 -2
- package/src/main/bridge.ts +3 -2
- package/src/main/headless.ts +9 -13
- package/src/main/index.ts +6 -0
- package/src/main/pty.ts +2 -5
- package/src/main/ticketStore.ts +2 -1
- package/src/main/timeTracker.ts +4 -1
- package/src/main/transcripts.ts +3 -2
- package/src/preload/index.ts +2 -0
- package/src/renderer/App.tsx +1 -0
- package/src/renderer/components/SettingsDialog.tsx +13 -0
- package/src/renderer/components/SpeakControls.tsx +5 -0
- package/src/renderer/components/TerminalPanel.tsx +151 -30
- package/src/renderer/lib/speech.ts +2 -1
- package/src/renderer/styles.css +116 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./cssMode-CAEObGNt.js","./lspLanguageFeatures-Czkp5sZb.js","./htmlMode-B6fxDDPF.js","./jsonMode-CqspkWCb.js","./javascript-8RyFDDdk.js","./typescript-BrdIiKZf.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
|
}
|
|
@@ -18818,16 +18818,412 @@ function convertToEmailTable(raw) {
|
|
|
18818
18818
|
cols: rows[0].length
|
|
18819
18819
|
};
|
|
18820
18820
|
}
|
|
18821
|
-
function
|
|
18822
|
-
const
|
|
18821
|
+
const scriptRel = /* @__PURE__ */ (function detectScriptRel() {
|
|
18822
|
+
const relList = typeof document !== "undefined" && document.createElement("link").relList;
|
|
18823
|
+
return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
|
|
18824
|
+
})();
|
|
18825
|
+
const assetsURL = function(dep, importerUrl) {
|
|
18826
|
+
return new URL(dep, importerUrl).href;
|
|
18827
|
+
};
|
|
18828
|
+
const seen = {};
|
|
18829
|
+
const __vitePreload = function preload(baseModule, deps, importerUrl) {
|
|
18830
|
+
let promise = Promise.resolve();
|
|
18831
|
+
if (deps && deps.length > 0) {
|
|
18832
|
+
let allSettled2 = function(promises) {
|
|
18833
|
+
return Promise.all(
|
|
18834
|
+
promises.map(
|
|
18835
|
+
(p) => Promise.resolve(p).then(
|
|
18836
|
+
(value) => ({ status: "fulfilled", value }),
|
|
18837
|
+
(reason) => ({ status: "rejected", reason })
|
|
18838
|
+
)
|
|
18839
|
+
)
|
|
18840
|
+
);
|
|
18841
|
+
};
|
|
18842
|
+
const links = document.getElementsByTagName("link");
|
|
18843
|
+
const cspNonceMeta = document.querySelector(
|
|
18844
|
+
"meta[property=csp-nonce]"
|
|
18845
|
+
);
|
|
18846
|
+
const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
|
|
18847
|
+
promise = allSettled2(
|
|
18848
|
+
deps.map((dep) => {
|
|
18849
|
+
dep = assetsURL(dep, importerUrl);
|
|
18850
|
+
if (dep in seen) return;
|
|
18851
|
+
seen[dep] = true;
|
|
18852
|
+
const isCss = dep.endsWith(".css");
|
|
18853
|
+
const cssSelector = isCss ? '[rel="stylesheet"]' : "";
|
|
18854
|
+
const isBaseRelative = !!importerUrl;
|
|
18855
|
+
if (isBaseRelative) {
|
|
18856
|
+
for (let i2 = links.length - 1; i2 >= 0; i2--) {
|
|
18857
|
+
const link22 = links[i2];
|
|
18858
|
+
if (link22.href === dep && (!isCss || link22.rel === "stylesheet")) {
|
|
18859
|
+
return;
|
|
18860
|
+
}
|
|
18861
|
+
}
|
|
18862
|
+
} else if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
|
|
18863
|
+
return;
|
|
18864
|
+
}
|
|
18865
|
+
const link2 = document.createElement("link");
|
|
18866
|
+
link2.rel = isCss ? "stylesheet" : scriptRel;
|
|
18867
|
+
if (!isCss) {
|
|
18868
|
+
link2.as = "script";
|
|
18869
|
+
}
|
|
18870
|
+
link2.crossOrigin = "";
|
|
18871
|
+
link2.href = dep;
|
|
18872
|
+
if (cspNonce) {
|
|
18873
|
+
link2.setAttribute("nonce", cspNonce);
|
|
18874
|
+
}
|
|
18875
|
+
document.head.appendChild(link2);
|
|
18876
|
+
if (isCss) {
|
|
18877
|
+
return new Promise((res, rej) => {
|
|
18878
|
+
link2.addEventListener("load", res);
|
|
18879
|
+
link2.addEventListener(
|
|
18880
|
+
"error",
|
|
18881
|
+
() => rej(new Error(`Unable to preload CSS for ${dep}`))
|
|
18882
|
+
);
|
|
18883
|
+
});
|
|
18884
|
+
}
|
|
18885
|
+
})
|
|
18886
|
+
);
|
|
18887
|
+
}
|
|
18888
|
+
function handlePreloadError(err) {
|
|
18889
|
+
const e = new Event("vite:preloadError", {
|
|
18890
|
+
cancelable: true
|
|
18891
|
+
});
|
|
18892
|
+
e.payload = err;
|
|
18893
|
+
window.dispatchEvent(e);
|
|
18894
|
+
if (!e.defaultPrevented) {
|
|
18895
|
+
throw err;
|
|
18896
|
+
}
|
|
18897
|
+
}
|
|
18898
|
+
return promise.then((res) => {
|
|
18899
|
+
for (const item of res || []) {
|
|
18900
|
+
if (item.status !== "rejected") continue;
|
|
18901
|
+
handlePreloadError(item.reason);
|
|
18902
|
+
}
|
|
18903
|
+
return baseModule().catch(handlePreloadError);
|
|
18904
|
+
});
|
|
18905
|
+
};
|
|
18906
|
+
const ENGINE_KEY = "ctlsurf.tts.engine";
|
|
18907
|
+
const VOICE_KEY = "ctlsurf.tts.voiceURI";
|
|
18908
|
+
const RATE_KEY = "ctlsurf.tts.rate";
|
|
18909
|
+
const MAX_SPEAK_CHARS = 1600;
|
|
18910
|
+
const MAX_CHUNK_CHARS = 280;
|
|
18911
|
+
function cleanForSpeech(input) {
|
|
18912
|
+
let t = input;
|
|
18913
|
+
t = t.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_m, body) => {
|
|
18914
|
+
const lines = body.replace(/\n+$/, "").split("\n").filter((l2) => l2.trim().length).length;
|
|
18915
|
+
return lines > 0 ? ` (code block, ${lines} ${lines === 1 ? "line" : "lines"}) ` : " (code) ";
|
|
18916
|
+
});
|
|
18917
|
+
t = t.replace(/```/g, " (code) ");
|
|
18918
|
+
t = t.replace(/`([^`]+)`/g, "$1");
|
|
18919
|
+
t = t.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
18920
|
+
t = t.replace(/https?:\/\/\S+/g, "link");
|
|
18921
|
+
t = t.replace(/(?:[\w.-]*\/){2,}([\w.-]+)/g, "$1");
|
|
18922
|
+
t = t.replace(/[*_#>]+/g, " ");
|
|
18923
|
+
t = t.replace(/^\s*[-•]\s+/gm, ", ");
|
|
18924
|
+
t = t.replace(/<[^>]+>/g, " ");
|
|
18925
|
+
t = t.replace(/\s+/g, " ").trim();
|
|
18926
|
+
if (t.length > MAX_SPEAK_CHARS) {
|
|
18927
|
+
t = t.slice(0, MAX_SPEAK_CHARS).replace(/\s+\S*$/, "") + "…";
|
|
18928
|
+
}
|
|
18929
|
+
return t;
|
|
18930
|
+
}
|
|
18931
|
+
function splitIntoChunks(text2) {
|
|
18932
|
+
if (text2.length <= MAX_CHUNK_CHARS) return text2 ? [text2] : [];
|
|
18933
|
+
const sentences = text2.match(/[^.!?]+[.!?]+|\S[^.!?]*$/g) || [text2];
|
|
18934
|
+
const chunks = [];
|
|
18935
|
+
let buf = "";
|
|
18936
|
+
for (const s of sentences) {
|
|
18937
|
+
if ((buf + s).length > MAX_CHUNK_CHARS && buf) {
|
|
18938
|
+
chunks.push(buf.trim());
|
|
18939
|
+
buf = "";
|
|
18940
|
+
}
|
|
18941
|
+
if (s.length > MAX_CHUNK_CHARS) {
|
|
18942
|
+
for (let i2 = 0; i2 < s.length; i2 += MAX_CHUNK_CHARS) chunks.push(s.slice(i2, i2 + MAX_CHUNK_CHARS).trim());
|
|
18943
|
+
} else {
|
|
18944
|
+
buf += s;
|
|
18945
|
+
}
|
|
18946
|
+
}
|
|
18947
|
+
if (buf.trim()) chunks.push(buf.trim());
|
|
18948
|
+
return chunks;
|
|
18949
|
+
}
|
|
18950
|
+
function getEngine() {
|
|
18951
|
+
try {
|
|
18952
|
+
return localStorage.getItem(ENGINE_KEY) === "web" ? "web" : "neural";
|
|
18953
|
+
} catch {
|
|
18954
|
+
return "neural";
|
|
18955
|
+
}
|
|
18956
|
+
}
|
|
18957
|
+
function setEngine(id) {
|
|
18958
|
+
try {
|
|
18959
|
+
localStorage.setItem(ENGINE_KEY, id);
|
|
18960
|
+
} catch {
|
|
18961
|
+
}
|
|
18962
|
+
}
|
|
18963
|
+
function getVoiceURI() {
|
|
18964
|
+
try {
|
|
18965
|
+
return localStorage.getItem(VOICE_KEY) || null;
|
|
18966
|
+
} catch {
|
|
18967
|
+
return null;
|
|
18968
|
+
}
|
|
18969
|
+
}
|
|
18970
|
+
function setVoiceURI(uri) {
|
|
18971
|
+
try {
|
|
18972
|
+
uri ? localStorage.setItem(VOICE_KEY, uri) : localStorage.removeItem(VOICE_KEY);
|
|
18973
|
+
} catch {
|
|
18974
|
+
}
|
|
18975
|
+
}
|
|
18976
|
+
function getRate() {
|
|
18977
|
+
try {
|
|
18978
|
+
const n2 = Number(localStorage.getItem(RATE_KEY));
|
|
18979
|
+
return Number.isFinite(n2) && n2 >= 0.5 && n2 <= 2 ? n2 : 1;
|
|
18980
|
+
} catch {
|
|
18981
|
+
return 1;
|
|
18982
|
+
}
|
|
18983
|
+
}
|
|
18984
|
+
function setRate(rate) {
|
|
18985
|
+
try {
|
|
18986
|
+
localStorage.setItem(RATE_KEY, String(rate));
|
|
18987
|
+
} catch {
|
|
18988
|
+
}
|
|
18989
|
+
}
|
|
18990
|
+
function listWebVoices() {
|
|
18991
|
+
if (typeof speechSynthesis === "undefined") return [];
|
|
18992
|
+
return speechSynthesis.getVoices();
|
|
18993
|
+
}
|
|
18994
|
+
const NEURAL_MODEL = "Xenova/mms-tts-eng";
|
|
18995
|
+
let synthPromise = null;
|
|
18996
|
+
function loadSynthesizer(onProgress) {
|
|
18997
|
+
if (!synthPromise) {
|
|
18998
|
+
synthPromise = (async () => {
|
|
18999
|
+
const { pipeline, env: env2 } = await __vitePreload(async () => {
|
|
19000
|
+
const { pipeline: pipeline2, env: env3 } = await import("./transformers.web-_PuZoIho.js");
|
|
19001
|
+
return { pipeline: pipeline2, env: env3 };
|
|
19002
|
+
}, true ? [] : void 0, import.meta.url);
|
|
19003
|
+
env2.allowLocalModels = false;
|
|
19004
|
+
const common = { progress_callback: onProgress };
|
|
19005
|
+
const hasWebGpu = typeof navigator !== "undefined" && "gpu" in navigator;
|
|
19006
|
+
if (hasWebGpu) {
|
|
19007
|
+
try {
|
|
19008
|
+
const s2 = await pipeline("text-to-speech", NEURAL_MODEL, { ...common, device: "webgpu" });
|
|
19009
|
+
console.info("[tts] neural backend: webgpu");
|
|
19010
|
+
return s2;
|
|
19011
|
+
} catch (err) {
|
|
19012
|
+
console.warn("[tts] WebGPU backend failed, falling back to WASM", err);
|
|
19013
|
+
}
|
|
19014
|
+
}
|
|
19015
|
+
const s = await pipeline("text-to-speech", NEURAL_MODEL, common);
|
|
19016
|
+
console.info("[tts] neural backend: wasm");
|
|
19017
|
+
return s;
|
|
19018
|
+
})();
|
|
19019
|
+
synthPromise.catch(() => {
|
|
19020
|
+
synthPromise = null;
|
|
19021
|
+
});
|
|
19022
|
+
}
|
|
19023
|
+
return synthPromise;
|
|
19024
|
+
}
|
|
19025
|
+
class SpeechController {
|
|
19026
|
+
queue = [];
|
|
19027
|
+
draining = false;
|
|
19028
|
+
audioCtx = null;
|
|
19029
|
+
currentSource = null;
|
|
19030
|
+
generation = 0;
|
|
19031
|
+
// bumped by stop() to abort in-flight work
|
|
19032
|
+
active = false;
|
|
19033
|
+
onModelProgress = null;
|
|
19034
|
+
// Fires true while a reply is being spoken/queued, false when idle — drives
|
|
19035
|
+
// the visible Stop button.
|
|
19036
|
+
onActivityChange = null;
|
|
19037
|
+
onError = null;
|
|
19038
|
+
// Multiple UI surfaces (titlebar SpeakControls + floating read button) need
|
|
19039
|
+
// the speaking state at once, so beyond the single onActivityChange slot we
|
|
19040
|
+
// also keep a listener set.
|
|
19041
|
+
activityListeners = /* @__PURE__ */ new Set();
|
|
19042
|
+
get isActive() {
|
|
19043
|
+
return this.active;
|
|
19044
|
+
}
|
|
19045
|
+
// Subscribe to speaking-state changes. Fires immediately with the current
|
|
19046
|
+
// state so a late subscriber syncs. Returns an unsubscribe function.
|
|
19047
|
+
onActivity(fn) {
|
|
19048
|
+
this.activityListeners.add(fn);
|
|
19049
|
+
fn(this.active);
|
|
19050
|
+
return () => {
|
|
19051
|
+
this.activityListeners.delete(fn);
|
|
19052
|
+
};
|
|
19053
|
+
}
|
|
19054
|
+
// Prime/resume the AudioContext from a user gesture so neural playback isn't
|
|
19055
|
+
// blocked by the browser autoplay policy (the System engine is unaffected).
|
|
19056
|
+
unlock() {
|
|
19057
|
+
try {
|
|
19058
|
+
const ctx = this.ensureCtx();
|
|
19059
|
+
if (ctx.state === "suspended") void ctx.resume();
|
|
19060
|
+
} catch {
|
|
19061
|
+
}
|
|
19062
|
+
}
|
|
19063
|
+
// Start loading the neural model in the background (e.g. when the user picks
|
|
19064
|
+
// the neural engine) so the first utterance doesn't pay download/compile time.
|
|
19065
|
+
warmup() {
|
|
19066
|
+
if (getEngine() !== "neural") return;
|
|
19067
|
+
void loadSynthesizer((p) => this.onModelProgress?.(p)).then(() => this.onModelProgress?.(null)).catch(() => {
|
|
19068
|
+
this.onModelProgress?.(null);
|
|
19069
|
+
});
|
|
19070
|
+
}
|
|
19071
|
+
enqueue(rawText) {
|
|
19072
|
+
const text2 = cleanForSpeech(rawText);
|
|
19073
|
+
if (!text2) return;
|
|
19074
|
+
this.queue.push(...splitIntoChunks(text2));
|
|
19075
|
+
void this.drain();
|
|
19076
|
+
}
|
|
19077
|
+
stop() {
|
|
19078
|
+
this.generation++;
|
|
19079
|
+
this.queue = [];
|
|
19080
|
+
this.draining = false;
|
|
19081
|
+
if (typeof speechSynthesis !== "undefined") {
|
|
19082
|
+
try {
|
|
19083
|
+
speechSynthesis.cancel();
|
|
19084
|
+
} catch {
|
|
19085
|
+
}
|
|
19086
|
+
}
|
|
19087
|
+
if (this.currentSource) {
|
|
19088
|
+
try {
|
|
19089
|
+
this.currentSource.stop();
|
|
19090
|
+
} catch {
|
|
19091
|
+
}
|
|
19092
|
+
this.currentSource = null;
|
|
19093
|
+
}
|
|
19094
|
+
this.onModelProgress?.(null);
|
|
19095
|
+
this.setActive(false);
|
|
19096
|
+
}
|
|
19097
|
+
ensureCtx() {
|
|
19098
|
+
if (!this.audioCtx) {
|
|
19099
|
+
const Ctx = window.AudioContext || window.webkitAudioContext;
|
|
19100
|
+
this.audioCtx = new Ctx();
|
|
19101
|
+
}
|
|
19102
|
+
return this.audioCtx;
|
|
19103
|
+
}
|
|
19104
|
+
setActive(a) {
|
|
19105
|
+
if (this.active === a) return;
|
|
19106
|
+
this.active = a;
|
|
19107
|
+
this.onActivityChange?.(a);
|
|
19108
|
+
this.activityListeners.forEach((fn) => fn(a));
|
|
19109
|
+
}
|
|
19110
|
+
async drain() {
|
|
19111
|
+
if (this.draining) return;
|
|
19112
|
+
this.draining = true;
|
|
19113
|
+
const gen = this.generation;
|
|
19114
|
+
this.setActive(true);
|
|
19115
|
+
while (this.queue.length && gen === this.generation) {
|
|
19116
|
+
const chunk = this.queue.shift();
|
|
19117
|
+
try {
|
|
19118
|
+
if (getEngine() === "neural") await this.speakNeural(chunk, gen);
|
|
19119
|
+
else await this.speakWeb(chunk, gen);
|
|
19120
|
+
} catch (err) {
|
|
19121
|
+
console.error("[tts] speak failed", err);
|
|
19122
|
+
this.onModelProgress?.(null);
|
|
19123
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
19124
|
+
this.onError?.(getEngine() === "neural" ? `Neural voice failed: ${detail}` : "Speech failed");
|
|
19125
|
+
}
|
|
19126
|
+
}
|
|
19127
|
+
if (gen === this.generation) {
|
|
19128
|
+
this.draining = false;
|
|
19129
|
+
this.setActive(false);
|
|
19130
|
+
}
|
|
19131
|
+
}
|
|
19132
|
+
speakWeb(text2, gen) {
|
|
19133
|
+
return new Promise((resolve2) => {
|
|
19134
|
+
if (typeof speechSynthesis === "undefined" || gen !== this.generation) return resolve2();
|
|
19135
|
+
const u = new SpeechSynthesisUtterance(text2);
|
|
19136
|
+
u.rate = getRate();
|
|
19137
|
+
const wantUri = getVoiceURI();
|
|
19138
|
+
if (wantUri) {
|
|
19139
|
+
const v2 = speechSynthesis.getVoices().find((vv) => vv.voiceURI === wantUri);
|
|
19140
|
+
if (v2) u.voice = v2;
|
|
19141
|
+
}
|
|
19142
|
+
u.onend = () => resolve2();
|
|
19143
|
+
u.onerror = () => resolve2();
|
|
19144
|
+
speechSynthesis.speak(u);
|
|
19145
|
+
});
|
|
19146
|
+
}
|
|
19147
|
+
async speakNeural(text2, gen) {
|
|
19148
|
+
console.info("[tts] neural: loading model…");
|
|
19149
|
+
this.onModelProgress?.({ status: "loading" });
|
|
19150
|
+
const synth = await loadSynthesizer((p) => this.onModelProgress?.(p));
|
|
19151
|
+
if (gen !== this.generation) return;
|
|
19152
|
+
console.info("[tts] neural: synthesizing", JSON.stringify(text2.slice(0, 60)));
|
|
19153
|
+
const out = await synth(text2);
|
|
19154
|
+
const raw = Array.isArray(out) ? out[0] : out;
|
|
19155
|
+
this.onModelProgress?.(null);
|
|
19156
|
+
if (gen !== this.generation) return;
|
|
19157
|
+
if (!raw?.audio?.length) throw new Error("neural synth returned no audio");
|
|
19158
|
+
console.info(`[tts] neural: playing ${raw.audio.length} samples @ ${raw.sampling_rate}Hz`);
|
|
19159
|
+
await this.playPcm(raw.audio, raw.sampling_rate, gen);
|
|
19160
|
+
}
|
|
19161
|
+
async playPcm(pcm, sampleRate, gen) {
|
|
19162
|
+
if (gen !== this.generation) return;
|
|
19163
|
+
const ctx = this.ensureCtx();
|
|
19164
|
+
if (ctx.state === "suspended") {
|
|
19165
|
+
try {
|
|
19166
|
+
await ctx.resume();
|
|
19167
|
+
} catch {
|
|
19168
|
+
}
|
|
19169
|
+
}
|
|
19170
|
+
if (gen !== this.generation) return;
|
|
19171
|
+
return new Promise((resolve2) => {
|
|
19172
|
+
const buffer = ctx.createBuffer(1, pcm.length, sampleRate);
|
|
19173
|
+
buffer.getChannelData(0).set(pcm);
|
|
19174
|
+
const source = ctx.createBufferSource();
|
|
19175
|
+
source.buffer = buffer;
|
|
19176
|
+
source.connect(ctx.destination);
|
|
19177
|
+
source.onended = () => {
|
|
19178
|
+
if (this.currentSource === source) this.currentSource = null;
|
|
19179
|
+
resolve2();
|
|
19180
|
+
};
|
|
19181
|
+
this.currentSource = source;
|
|
19182
|
+
source.start();
|
|
19183
|
+
});
|
|
19184
|
+
}
|
|
19185
|
+
}
|
|
19186
|
+
const speech = new SpeechController();
|
|
19187
|
+
function showTerminalToast(message) {
|
|
19188
|
+
window.dispatchEvent(new CustomEvent("terminal-toast", { detail: { message } }));
|
|
19189
|
+
}
|
|
19190
|
+
function copyTextValue(text2) {
|
|
19191
|
+
if (!text2) return;
|
|
19192
|
+
void window.worker.copyText(text2);
|
|
19193
|
+
showTerminalToast("Copied to clipboard");
|
|
19194
|
+
}
|
|
19195
|
+
function convertTextToTable(text2) {
|
|
19196
|
+
const result = convertToEmailTable(text2);
|
|
18823
19197
|
if (result) {
|
|
18824
19198
|
window.worker.copyEmailTable(result.html, result.text);
|
|
19199
|
+
showTerminalToast(`Copied ${result.rows}×${result.cols} table to clipboard`);
|
|
19200
|
+
} else {
|
|
19201
|
+
showTerminalToast("No table found in selection");
|
|
18825
19202
|
}
|
|
18826
|
-
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
|
|
18830
|
-
);
|
|
19203
|
+
}
|
|
19204
|
+
function speakTextValue(text2) {
|
|
19205
|
+
if (!text2.trim()) return;
|
|
19206
|
+
speech.unlock();
|
|
19207
|
+
speech.enqueue(text2);
|
|
19208
|
+
}
|
|
19209
|
+
function copySelectionPlain(terminal) {
|
|
19210
|
+
copyTextValue(terminal.getSelection());
|
|
19211
|
+
}
|
|
19212
|
+
function copySelectionAsEmailTable(terminal) {
|
|
19213
|
+
convertTextToTable(terminal.getSelection());
|
|
19214
|
+
}
|
|
19215
|
+
function speakSelection(terminal) {
|
|
19216
|
+
speakTextValue(terminal.getSelection());
|
|
19217
|
+
}
|
|
19218
|
+
function captureVisibleText(terminal) {
|
|
19219
|
+
const buf = terminal.buffer.active;
|
|
19220
|
+
const lines = [];
|
|
19221
|
+
for (let i2 = 0; i2 < terminal.rows; i2++) {
|
|
19222
|
+
const line = buf.getLine(buf.viewportY + i2);
|
|
19223
|
+
lines.push(line ? line.translateToString(true) : "");
|
|
19224
|
+
}
|
|
19225
|
+
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
|
19226
|
+
return lines.join("\n");
|
|
18831
19227
|
}
|
|
18832
19228
|
function handleTerminalLink(_event, uri) {
|
|
18833
19229
|
const openInPanel = (url) => window.dispatchEvent(new CustomEvent("ctlsurf-open-url", { detail: { url } }));
|
|
@@ -18910,11 +19306,23 @@ function getOrCreateTerminal(tabId, onExit) {
|
|
|
18910
19306
|
if (e.key === "PageUp" || e.key === "PageDown" || e.key === "Home" || e.key === "End") {
|
|
18911
19307
|
markGesture(tabId);
|
|
18912
19308
|
}
|
|
18913
|
-
const
|
|
18914
|
-
if (
|
|
18915
|
-
|
|
18916
|
-
|
|
18917
|
-
|
|
19309
|
+
const mod = (e.metaKey || e.ctrlKey) && e.shiftKey;
|
|
19310
|
+
if (mod && e.code === "KeyE") {
|
|
19311
|
+
e.preventDefault();
|
|
19312
|
+
copySelectionAsEmailTable(terminal);
|
|
19313
|
+
return false;
|
|
19314
|
+
}
|
|
19315
|
+
if (mod && e.code === "KeyC" && terminal.hasSelection()) {
|
|
19316
|
+
e.preventDefault();
|
|
19317
|
+
copySelectionPlain(terminal);
|
|
19318
|
+
return false;
|
|
19319
|
+
}
|
|
19320
|
+
if (mod && e.code === "KeyS" && terminal.getSelection().trim()) {
|
|
19321
|
+
e.preventDefault();
|
|
19322
|
+
speakSelection(terminal);
|
|
19323
|
+
return false;
|
|
19324
|
+
}
|
|
19325
|
+
return true;
|
|
18918
19326
|
});
|
|
18919
19327
|
const state = {
|
|
18920
19328
|
terminal,
|
|
@@ -18976,6 +19384,8 @@ function destroyTerminal(tabId) {
|
|
|
18976
19384
|
function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
18977
19385
|
const containerRef = reactExports.useRef(null);
|
|
18978
19386
|
const [menu, setMenu] = reactExports.useState(null);
|
|
19387
|
+
const [snapOpen, setSnapOpen] = reactExports.useState(false);
|
|
19388
|
+
const snapTextRef = reactExports.useRef("");
|
|
18979
19389
|
const [toast, setToast] = reactExports.useState(null);
|
|
18980
19390
|
const [followBottom, setFollowBottom] = reactExports.useState(true);
|
|
18981
19391
|
const jumpToLatest = () => {
|
|
@@ -18987,22 +19397,44 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
|
18987
19397
|
};
|
|
18988
19398
|
const handleContextMenu = (e) => {
|
|
18989
19399
|
const sel = _terminals.get(tabId)?.terminal.getSelection() ?? "";
|
|
18990
|
-
if (!
|
|
19400
|
+
if (!sel.trim()) return;
|
|
18991
19401
|
e.preventDefault();
|
|
18992
19402
|
setMenu({ x: e.clientX, y: e.clientY });
|
|
18993
19403
|
};
|
|
19404
|
+
const runCopy = () => {
|
|
19405
|
+
const term = _terminals.get(tabId)?.terminal;
|
|
19406
|
+
if (term) copySelectionPlain(term);
|
|
19407
|
+
setMenu(null);
|
|
19408
|
+
};
|
|
18994
19409
|
const runConvert = () => {
|
|
18995
19410
|
const term = _terminals.get(tabId)?.terminal;
|
|
18996
19411
|
if (term) copySelectionAsEmailTable(term);
|
|
18997
19412
|
setMenu(null);
|
|
18998
19413
|
};
|
|
19414
|
+
const runSpeak = () => {
|
|
19415
|
+
const term = _terminals.get(tabId)?.terminal;
|
|
19416
|
+
if (term) speakSelection(term);
|
|
19417
|
+
setMenu(null);
|
|
19418
|
+
};
|
|
19419
|
+
const openSnapshot = () => {
|
|
19420
|
+
const term = _terminals.get(tabId)?.terminal;
|
|
19421
|
+
snapTextRef.current = term ? captureVisibleText(term) : "";
|
|
19422
|
+
setSnapOpen(true);
|
|
19423
|
+
};
|
|
19424
|
+
const snapAction = (kind) => {
|
|
19425
|
+
const domSel = window.getSelection()?.toString() ?? "";
|
|
19426
|
+
const text2 = domSel.trim() ? domSel : snapTextRef.current;
|
|
19427
|
+
if (kind === "copy") copyTextValue(text2);
|
|
19428
|
+
else if (kind === "table") convertTextToTable(text2);
|
|
19429
|
+
else speakTextValue(text2);
|
|
19430
|
+
};
|
|
18999
19431
|
reactExports.useEffect(() => {
|
|
19000
|
-
const
|
|
19432
|
+
const onToast = (e) => {
|
|
19001
19433
|
const detail = e.detail;
|
|
19002
|
-
if (detail) setToast(detail.message);
|
|
19434
|
+
if (detail?.message) setToast(detail.message);
|
|
19003
19435
|
};
|
|
19004
|
-
window.addEventListener("
|
|
19005
|
-
return () => window.removeEventListener("
|
|
19436
|
+
window.addEventListener("terminal-toast", onToast);
|
|
19437
|
+
return () => window.removeEventListener("terminal-toast", onToast);
|
|
19006
19438
|
}, []);
|
|
19007
19439
|
reactExports.useEffect(() => {
|
|
19008
19440
|
if (!toast) return;
|
|
@@ -19126,100 +19558,75 @@ function TerminalPanel({ tabId, agent, onSpawn, onExit, isActive }) {
|
|
|
19126
19558
|
title: "Jump to latest output",
|
|
19127
19559
|
children: "↓ Latest"
|
|
19128
19560
|
}
|
|
19129
|
-
)
|
|
19561
|
+
),
|
|
19562
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
19563
|
+
"button",
|
|
19564
|
+
{
|
|
19565
|
+
type: "button",
|
|
19566
|
+
className: "term-util-btn",
|
|
19567
|
+
onMouseDown: (e) => {
|
|
19568
|
+
e.preventDefault();
|
|
19569
|
+
openSnapshot();
|
|
19570
|
+
},
|
|
19571
|
+
title: "Snapshot screen → copy / convert to table / read aloud",
|
|
19572
|
+
children: "⋮"
|
|
19573
|
+
}
|
|
19574
|
+
),
|
|
19575
|
+
snapOpen && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "term-snapshot", children: [
|
|
19576
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "term-snapshot-head", children: [
|
|
19577
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Select text below (or select nothing to use everything), then pick an action." }),
|
|
19578
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
19579
|
+
"button",
|
|
19580
|
+
{
|
|
19581
|
+
type: "button",
|
|
19582
|
+
className: "term-snapshot-x",
|
|
19583
|
+
onMouseDown: (e) => {
|
|
19584
|
+
e.preventDefault();
|
|
19585
|
+
setSnapOpen(false);
|
|
19586
|
+
},
|
|
19587
|
+
title: "Close",
|
|
19588
|
+
children: "✕"
|
|
19589
|
+
}
|
|
19590
|
+
)
|
|
19591
|
+
] }),
|
|
19592
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "term-snapshot-text", children: snapTextRef.current }),
|
|
19593
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "term-snapshot-actions", children: [
|
|
19594
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onMouseDown: (e) => {
|
|
19595
|
+
e.preventDefault();
|
|
19596
|
+
snapAction("copy");
|
|
19597
|
+
}, children: "⧉ Copy" }),
|
|
19598
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onMouseDown: (e) => {
|
|
19599
|
+
e.preventDefault();
|
|
19600
|
+
snapAction("table");
|
|
19601
|
+
}, children: "▦ Convert to table" }),
|
|
19602
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onMouseDown: (e) => {
|
|
19603
|
+
e.preventDefault();
|
|
19604
|
+
snapAction("speak");
|
|
19605
|
+
}, children: "🔊 Read aloud" }),
|
|
19606
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "term-snapshot-done", onMouseDown: (e) => {
|
|
19607
|
+
e.preventDefault();
|
|
19608
|
+
setSnapOpen(false);
|
|
19609
|
+
}, children: "Close" })
|
|
19610
|
+
] })
|
|
19611
|
+
] })
|
|
19612
|
+
] }),
|
|
19613
|
+
menu && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "term-context-menu", style: { position: "fixed", left: menu.x, top: menu.y }, children: [
|
|
19614
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onMouseDown: (e) => {
|
|
19615
|
+
e.preventDefault();
|
|
19616
|
+
runCopy();
|
|
19617
|
+
}, children: "Copy" }),
|
|
19618
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onMouseDown: (e) => {
|
|
19619
|
+
e.preventDefault();
|
|
19620
|
+
runConvert();
|
|
19621
|
+
}, children: "Convert to table" }),
|
|
19622
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onMouseDown: (e) => {
|
|
19623
|
+
e.preventDefault();
|
|
19624
|
+
runSpeak();
|
|
19625
|
+
}, children: "Read it" })
|
|
19130
19626
|
] }),
|
|
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) => {
|
|
19132
|
-
e.preventDefault();
|
|
19133
|
-
runConvert();
|
|
19134
|
-
}, children: "Copy as email table" }) }),
|
|
19135
19627
|
toast && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "term-toast", children: toast })
|
|
19136
19628
|
] });
|
|
19137
19629
|
}
|
|
19138
|
-
const scriptRel = /* @__PURE__ */ (function detectScriptRel() {
|
|
19139
|
-
const relList = typeof document !== "undefined" && document.createElement("link").relList;
|
|
19140
|
-
return relList && relList.supports && relList.supports("modulepreload") ? "modulepreload" : "preload";
|
|
19141
|
-
})();
|
|
19142
|
-
const assetsURL = function(dep, importerUrl) {
|
|
19143
|
-
return new URL(dep, importerUrl).href;
|
|
19144
|
-
};
|
|
19145
|
-
const seen = {};
|
|
19146
|
-
const __vitePreload = function preload(baseModule, deps, importerUrl) {
|
|
19147
|
-
let promise = Promise.resolve();
|
|
19148
|
-
if (deps && deps.length > 0) {
|
|
19149
|
-
let allSettled2 = function(promises) {
|
|
19150
|
-
return Promise.all(
|
|
19151
|
-
promises.map(
|
|
19152
|
-
(p) => Promise.resolve(p).then(
|
|
19153
|
-
(value) => ({ status: "fulfilled", value }),
|
|
19154
|
-
(reason) => ({ status: "rejected", reason })
|
|
19155
|
-
)
|
|
19156
|
-
)
|
|
19157
|
-
);
|
|
19158
|
-
};
|
|
19159
|
-
const links = document.getElementsByTagName("link");
|
|
19160
|
-
const cspNonceMeta = document.querySelector(
|
|
19161
|
-
"meta[property=csp-nonce]"
|
|
19162
|
-
);
|
|
19163
|
-
const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
|
|
19164
|
-
promise = allSettled2(
|
|
19165
|
-
deps.map((dep) => {
|
|
19166
|
-
dep = assetsURL(dep, importerUrl);
|
|
19167
|
-
if (dep in seen) return;
|
|
19168
|
-
seen[dep] = true;
|
|
19169
|
-
const isCss = dep.endsWith(".css");
|
|
19170
|
-
const cssSelector = isCss ? '[rel="stylesheet"]' : "";
|
|
19171
|
-
const isBaseRelative = !!importerUrl;
|
|
19172
|
-
if (isBaseRelative) {
|
|
19173
|
-
for (let i2 = links.length - 1; i2 >= 0; i2--) {
|
|
19174
|
-
const link22 = links[i2];
|
|
19175
|
-
if (link22.href === dep && (!isCss || link22.rel === "stylesheet")) {
|
|
19176
|
-
return;
|
|
19177
|
-
}
|
|
19178
|
-
}
|
|
19179
|
-
} else if (document.querySelector(`link[href="${dep}"]${cssSelector}`)) {
|
|
19180
|
-
return;
|
|
19181
|
-
}
|
|
19182
|
-
const link2 = document.createElement("link");
|
|
19183
|
-
link2.rel = isCss ? "stylesheet" : scriptRel;
|
|
19184
|
-
if (!isCss) {
|
|
19185
|
-
link2.as = "script";
|
|
19186
|
-
}
|
|
19187
|
-
link2.crossOrigin = "";
|
|
19188
|
-
link2.href = dep;
|
|
19189
|
-
if (cspNonce) {
|
|
19190
|
-
link2.setAttribute("nonce", cspNonce);
|
|
19191
|
-
}
|
|
19192
|
-
document.head.appendChild(link2);
|
|
19193
|
-
if (isCss) {
|
|
19194
|
-
return new Promise((res, rej) => {
|
|
19195
|
-
link2.addEventListener("load", res);
|
|
19196
|
-
link2.addEventListener(
|
|
19197
|
-
"error",
|
|
19198
|
-
() => rej(new Error(`Unable to preload CSS for ${dep}`))
|
|
19199
|
-
);
|
|
19200
|
-
});
|
|
19201
|
-
}
|
|
19202
|
-
})
|
|
19203
|
-
);
|
|
19204
|
-
}
|
|
19205
|
-
function handlePreloadError(err) {
|
|
19206
|
-
const e = new Event("vite:preloadError", {
|
|
19207
|
-
cancelable: true
|
|
19208
|
-
});
|
|
19209
|
-
e.payload = err;
|
|
19210
|
-
window.dispatchEvent(e);
|
|
19211
|
-
if (!e.defaultPrevented) {
|
|
19212
|
-
throw err;
|
|
19213
|
-
}
|
|
19214
|
-
}
|
|
19215
|
-
return promise.then((res) => {
|
|
19216
|
-
for (const item of res || []) {
|
|
19217
|
-
if (item.status !== "rejected") continue;
|
|
19218
|
-
handlePreloadError(item.reason);
|
|
19219
|
-
}
|
|
19220
|
-
return baseModule().catch(handlePreloadError);
|
|
19221
|
-
});
|
|
19222
|
-
};
|
|
19223
19630
|
const MODEL = "Xenova/whisper-base";
|
|
19224
19631
|
const TARGET_SAMPLE_RATE = 16e3;
|
|
19225
19632
|
const SILENCE_RMS = 8e-3;
|
|
@@ -19643,287 +20050,6 @@ function VoiceInput({ onTranscript, variant = "titlebar" }) {
|
|
|
19643
20050
|
chip && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `voice-chip ${chip.kind} ${floating ? "voice-chip-floating" : ""}`, children: chip.text })
|
|
19644
20051
|
] });
|
|
19645
20052
|
}
|
|
19646
|
-
const ENGINE_KEY = "ctlsurf.tts.engine";
|
|
19647
|
-
const VOICE_KEY = "ctlsurf.tts.voiceURI";
|
|
19648
|
-
const RATE_KEY = "ctlsurf.tts.rate";
|
|
19649
|
-
const MAX_SPEAK_CHARS = 1600;
|
|
19650
|
-
const MAX_CHUNK_CHARS = 280;
|
|
19651
|
-
function cleanForSpeech(input) {
|
|
19652
|
-
let t = input;
|
|
19653
|
-
t = t.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_m, body) => {
|
|
19654
|
-
const lines = body.replace(/\n+$/, "").split("\n").filter((l2) => l2.trim().length).length;
|
|
19655
|
-
return lines > 0 ? ` (code block, ${lines} ${lines === 1 ? "line" : "lines"}) ` : " (code) ";
|
|
19656
|
-
});
|
|
19657
|
-
t = t.replace(/```/g, " (code) ");
|
|
19658
|
-
t = t.replace(/`([^`]+)`/g, "$1");
|
|
19659
|
-
t = t.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
19660
|
-
t = t.replace(/https?:\/\/\S+/g, "link");
|
|
19661
|
-
t = t.replace(/(?:[\w.-]*\/){2,}([\w.-]+)/g, "$1");
|
|
19662
|
-
t = t.replace(/[*_#>]+/g, " ");
|
|
19663
|
-
t = t.replace(/^\s*[-•]\s+/gm, ", ");
|
|
19664
|
-
t = t.replace(/<[^>]+>/g, " ");
|
|
19665
|
-
t = t.replace(/\s+/g, " ").trim();
|
|
19666
|
-
if (t.length > MAX_SPEAK_CHARS) {
|
|
19667
|
-
t = t.slice(0, MAX_SPEAK_CHARS).replace(/\s+\S*$/, "") + "…";
|
|
19668
|
-
}
|
|
19669
|
-
return t;
|
|
19670
|
-
}
|
|
19671
|
-
function splitIntoChunks(text2) {
|
|
19672
|
-
if (text2.length <= MAX_CHUNK_CHARS) return text2 ? [text2] : [];
|
|
19673
|
-
const sentences = text2.match(/[^.!?]+[.!?]+|\S[^.!?]*$/g) || [text2];
|
|
19674
|
-
const chunks = [];
|
|
19675
|
-
let buf = "";
|
|
19676
|
-
for (const s of sentences) {
|
|
19677
|
-
if ((buf + s).length > MAX_CHUNK_CHARS && buf) {
|
|
19678
|
-
chunks.push(buf.trim());
|
|
19679
|
-
buf = "";
|
|
19680
|
-
}
|
|
19681
|
-
if (s.length > MAX_CHUNK_CHARS) {
|
|
19682
|
-
for (let i2 = 0; i2 < s.length; i2 += MAX_CHUNK_CHARS) chunks.push(s.slice(i2, i2 + MAX_CHUNK_CHARS).trim());
|
|
19683
|
-
} else {
|
|
19684
|
-
buf += s;
|
|
19685
|
-
}
|
|
19686
|
-
}
|
|
19687
|
-
if (buf.trim()) chunks.push(buf.trim());
|
|
19688
|
-
return chunks;
|
|
19689
|
-
}
|
|
19690
|
-
function getEngine() {
|
|
19691
|
-
try {
|
|
19692
|
-
return localStorage.getItem(ENGINE_KEY) === "neural" ? "neural" : "web";
|
|
19693
|
-
} catch {
|
|
19694
|
-
return "web";
|
|
19695
|
-
}
|
|
19696
|
-
}
|
|
19697
|
-
function setEngine(id) {
|
|
19698
|
-
try {
|
|
19699
|
-
localStorage.setItem(ENGINE_KEY, id);
|
|
19700
|
-
} catch {
|
|
19701
|
-
}
|
|
19702
|
-
}
|
|
19703
|
-
function getVoiceURI() {
|
|
19704
|
-
try {
|
|
19705
|
-
return localStorage.getItem(VOICE_KEY) || null;
|
|
19706
|
-
} catch {
|
|
19707
|
-
return null;
|
|
19708
|
-
}
|
|
19709
|
-
}
|
|
19710
|
-
function setVoiceURI(uri) {
|
|
19711
|
-
try {
|
|
19712
|
-
uri ? localStorage.setItem(VOICE_KEY, uri) : localStorage.removeItem(VOICE_KEY);
|
|
19713
|
-
} catch {
|
|
19714
|
-
}
|
|
19715
|
-
}
|
|
19716
|
-
function getRate() {
|
|
19717
|
-
try {
|
|
19718
|
-
const n2 = Number(localStorage.getItem(RATE_KEY));
|
|
19719
|
-
return Number.isFinite(n2) && n2 >= 0.5 && n2 <= 2 ? n2 : 1;
|
|
19720
|
-
} catch {
|
|
19721
|
-
return 1;
|
|
19722
|
-
}
|
|
19723
|
-
}
|
|
19724
|
-
function setRate(rate) {
|
|
19725
|
-
try {
|
|
19726
|
-
localStorage.setItem(RATE_KEY, String(rate));
|
|
19727
|
-
} catch {
|
|
19728
|
-
}
|
|
19729
|
-
}
|
|
19730
|
-
function listWebVoices() {
|
|
19731
|
-
if (typeof speechSynthesis === "undefined") return [];
|
|
19732
|
-
return speechSynthesis.getVoices();
|
|
19733
|
-
}
|
|
19734
|
-
const NEURAL_MODEL = "Xenova/mms-tts-eng";
|
|
19735
|
-
let synthPromise = null;
|
|
19736
|
-
function loadSynthesizer(onProgress) {
|
|
19737
|
-
if (!synthPromise) {
|
|
19738
|
-
synthPromise = (async () => {
|
|
19739
|
-
const { pipeline, env: env2 } = await __vitePreload(async () => {
|
|
19740
|
-
const { pipeline: pipeline2, env: env3 } = await import("./transformers.web-_PuZoIho.js");
|
|
19741
|
-
return { pipeline: pipeline2, env: env3 };
|
|
19742
|
-
}, true ? [] : void 0, import.meta.url);
|
|
19743
|
-
env2.allowLocalModels = false;
|
|
19744
|
-
const common = { progress_callback: onProgress };
|
|
19745
|
-
const hasWebGpu = typeof navigator !== "undefined" && "gpu" in navigator;
|
|
19746
|
-
if (hasWebGpu) {
|
|
19747
|
-
try {
|
|
19748
|
-
const s2 = await pipeline("text-to-speech", NEURAL_MODEL, { ...common, device: "webgpu" });
|
|
19749
|
-
console.info("[tts] neural backend: webgpu");
|
|
19750
|
-
return s2;
|
|
19751
|
-
} catch (err) {
|
|
19752
|
-
console.warn("[tts] WebGPU backend failed, falling back to WASM", err);
|
|
19753
|
-
}
|
|
19754
|
-
}
|
|
19755
|
-
const s = await pipeline("text-to-speech", NEURAL_MODEL, common);
|
|
19756
|
-
console.info("[tts] neural backend: wasm");
|
|
19757
|
-
return s;
|
|
19758
|
-
})();
|
|
19759
|
-
synthPromise.catch(() => {
|
|
19760
|
-
synthPromise = null;
|
|
19761
|
-
});
|
|
19762
|
-
}
|
|
19763
|
-
return synthPromise;
|
|
19764
|
-
}
|
|
19765
|
-
class SpeechController {
|
|
19766
|
-
queue = [];
|
|
19767
|
-
draining = false;
|
|
19768
|
-
audioCtx = null;
|
|
19769
|
-
currentSource = null;
|
|
19770
|
-
generation = 0;
|
|
19771
|
-
// bumped by stop() to abort in-flight work
|
|
19772
|
-
active = false;
|
|
19773
|
-
onModelProgress = null;
|
|
19774
|
-
// Fires true while a reply is being spoken/queued, false when idle — drives
|
|
19775
|
-
// the visible Stop button.
|
|
19776
|
-
onActivityChange = null;
|
|
19777
|
-
onError = null;
|
|
19778
|
-
// Multiple UI surfaces (titlebar SpeakControls + floating read button) need
|
|
19779
|
-
// the speaking state at once, so beyond the single onActivityChange slot we
|
|
19780
|
-
// also keep a listener set.
|
|
19781
|
-
activityListeners = /* @__PURE__ */ new Set();
|
|
19782
|
-
get isActive() {
|
|
19783
|
-
return this.active;
|
|
19784
|
-
}
|
|
19785
|
-
// Subscribe to speaking-state changes. Fires immediately with the current
|
|
19786
|
-
// state so a late subscriber syncs. Returns an unsubscribe function.
|
|
19787
|
-
onActivity(fn) {
|
|
19788
|
-
this.activityListeners.add(fn);
|
|
19789
|
-
fn(this.active);
|
|
19790
|
-
return () => {
|
|
19791
|
-
this.activityListeners.delete(fn);
|
|
19792
|
-
};
|
|
19793
|
-
}
|
|
19794
|
-
// Prime/resume the AudioContext from a user gesture so neural playback isn't
|
|
19795
|
-
// blocked by the browser autoplay policy (the System engine is unaffected).
|
|
19796
|
-
unlock() {
|
|
19797
|
-
try {
|
|
19798
|
-
const ctx = this.ensureCtx();
|
|
19799
|
-
if (ctx.state === "suspended") void ctx.resume();
|
|
19800
|
-
} catch {
|
|
19801
|
-
}
|
|
19802
|
-
}
|
|
19803
|
-
// Start loading the neural model in the background (e.g. when the user picks
|
|
19804
|
-
// the neural engine) so the first utterance doesn't pay download/compile time.
|
|
19805
|
-
warmup() {
|
|
19806
|
-
if (getEngine() !== "neural") return;
|
|
19807
|
-
void loadSynthesizer((p) => this.onModelProgress?.(p)).then(() => this.onModelProgress?.(null)).catch(() => {
|
|
19808
|
-
this.onModelProgress?.(null);
|
|
19809
|
-
});
|
|
19810
|
-
}
|
|
19811
|
-
enqueue(rawText) {
|
|
19812
|
-
const text2 = cleanForSpeech(rawText);
|
|
19813
|
-
if (!text2) return;
|
|
19814
|
-
this.queue.push(...splitIntoChunks(text2));
|
|
19815
|
-
void this.drain();
|
|
19816
|
-
}
|
|
19817
|
-
stop() {
|
|
19818
|
-
this.generation++;
|
|
19819
|
-
this.queue = [];
|
|
19820
|
-
this.draining = false;
|
|
19821
|
-
if (typeof speechSynthesis !== "undefined") {
|
|
19822
|
-
try {
|
|
19823
|
-
speechSynthesis.cancel();
|
|
19824
|
-
} catch {
|
|
19825
|
-
}
|
|
19826
|
-
}
|
|
19827
|
-
if (this.currentSource) {
|
|
19828
|
-
try {
|
|
19829
|
-
this.currentSource.stop();
|
|
19830
|
-
} catch {
|
|
19831
|
-
}
|
|
19832
|
-
this.currentSource = null;
|
|
19833
|
-
}
|
|
19834
|
-
this.onModelProgress?.(null);
|
|
19835
|
-
this.setActive(false);
|
|
19836
|
-
}
|
|
19837
|
-
ensureCtx() {
|
|
19838
|
-
if (!this.audioCtx) {
|
|
19839
|
-
const Ctx = window.AudioContext || window.webkitAudioContext;
|
|
19840
|
-
this.audioCtx = new Ctx();
|
|
19841
|
-
}
|
|
19842
|
-
return this.audioCtx;
|
|
19843
|
-
}
|
|
19844
|
-
setActive(a) {
|
|
19845
|
-
if (this.active === a) return;
|
|
19846
|
-
this.active = a;
|
|
19847
|
-
this.onActivityChange?.(a);
|
|
19848
|
-
this.activityListeners.forEach((fn) => fn(a));
|
|
19849
|
-
}
|
|
19850
|
-
async drain() {
|
|
19851
|
-
if (this.draining) return;
|
|
19852
|
-
this.draining = true;
|
|
19853
|
-
const gen = this.generation;
|
|
19854
|
-
this.setActive(true);
|
|
19855
|
-
while (this.queue.length && gen === this.generation) {
|
|
19856
|
-
const chunk = this.queue.shift();
|
|
19857
|
-
try {
|
|
19858
|
-
if (getEngine() === "neural") await this.speakNeural(chunk, gen);
|
|
19859
|
-
else await this.speakWeb(chunk, gen);
|
|
19860
|
-
} catch (err) {
|
|
19861
|
-
console.error("[tts] speak failed", err);
|
|
19862
|
-
this.onModelProgress?.(null);
|
|
19863
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
19864
|
-
this.onError?.(getEngine() === "neural" ? `Neural voice failed: ${detail}` : "Speech failed");
|
|
19865
|
-
}
|
|
19866
|
-
}
|
|
19867
|
-
if (gen === this.generation) {
|
|
19868
|
-
this.draining = false;
|
|
19869
|
-
this.setActive(false);
|
|
19870
|
-
}
|
|
19871
|
-
}
|
|
19872
|
-
speakWeb(text2, gen) {
|
|
19873
|
-
return new Promise((resolve2) => {
|
|
19874
|
-
if (typeof speechSynthesis === "undefined" || gen !== this.generation) return resolve2();
|
|
19875
|
-
const u = new SpeechSynthesisUtterance(text2);
|
|
19876
|
-
u.rate = getRate();
|
|
19877
|
-
const wantUri = getVoiceURI();
|
|
19878
|
-
if (wantUri) {
|
|
19879
|
-
const v2 = speechSynthesis.getVoices().find((vv) => vv.voiceURI === wantUri);
|
|
19880
|
-
if (v2) u.voice = v2;
|
|
19881
|
-
}
|
|
19882
|
-
u.onend = () => resolve2();
|
|
19883
|
-
u.onerror = () => resolve2();
|
|
19884
|
-
speechSynthesis.speak(u);
|
|
19885
|
-
});
|
|
19886
|
-
}
|
|
19887
|
-
async speakNeural(text2, gen) {
|
|
19888
|
-
console.info("[tts] neural: loading model…");
|
|
19889
|
-
this.onModelProgress?.({ status: "loading" });
|
|
19890
|
-
const synth = await loadSynthesizer((p) => this.onModelProgress?.(p));
|
|
19891
|
-
if (gen !== this.generation) return;
|
|
19892
|
-
console.info("[tts] neural: synthesizing", JSON.stringify(text2.slice(0, 60)));
|
|
19893
|
-
const out = await synth(text2);
|
|
19894
|
-
const raw = Array.isArray(out) ? out[0] : out;
|
|
19895
|
-
this.onModelProgress?.(null);
|
|
19896
|
-
if (gen !== this.generation) return;
|
|
19897
|
-
if (!raw?.audio?.length) throw new Error("neural synth returned no audio");
|
|
19898
|
-
console.info(`[tts] neural: playing ${raw.audio.length} samples @ ${raw.sampling_rate}Hz`);
|
|
19899
|
-
await this.playPcm(raw.audio, raw.sampling_rate, gen);
|
|
19900
|
-
}
|
|
19901
|
-
async playPcm(pcm, sampleRate, gen) {
|
|
19902
|
-
if (gen !== this.generation) return;
|
|
19903
|
-
const ctx = this.ensureCtx();
|
|
19904
|
-
if (ctx.state === "suspended") {
|
|
19905
|
-
try {
|
|
19906
|
-
await ctx.resume();
|
|
19907
|
-
} catch {
|
|
19908
|
-
}
|
|
19909
|
-
}
|
|
19910
|
-
if (gen !== this.generation) return;
|
|
19911
|
-
return new Promise((resolve2) => {
|
|
19912
|
-
const buffer = ctx.createBuffer(1, pcm.length, sampleRate);
|
|
19913
|
-
buffer.getChannelData(0).set(pcm);
|
|
19914
|
-
const source = ctx.createBufferSource();
|
|
19915
|
-
source.buffer = buffer;
|
|
19916
|
-
source.connect(ctx.destination);
|
|
19917
|
-
source.onended = () => {
|
|
19918
|
-
if (this.currentSource === source) this.currentSource = null;
|
|
19919
|
-
resolve2();
|
|
19920
|
-
};
|
|
19921
|
-
this.currentSource = source;
|
|
19922
|
-
source.start();
|
|
19923
|
-
});
|
|
19924
|
-
}
|
|
19925
|
-
}
|
|
19926
|
-
const speech = new SpeechController();
|
|
19927
20053
|
const POS_KEY = "ctlsurf.floatingMicPos";
|
|
19928
20054
|
const EDGE = 20;
|
|
19929
20055
|
const TOP_MIN = 46;
|
|
@@ -20102,6 +20228,9 @@ function SpeakControls({ remote = false }) {
|
|
|
20102
20228
|
alive = false;
|
|
20103
20229
|
};
|
|
20104
20230
|
}, []);
|
|
20231
|
+
reactExports.useEffect(() => {
|
|
20232
|
+
speech.warmup();
|
|
20233
|
+
}, []);
|
|
20105
20234
|
reactExports.useEffect(() => {
|
|
20106
20235
|
const off = window.worker.onAgentMessage((text2) => speech.enqueue(text2));
|
|
20107
20236
|
return off;
|
|
@@ -208022,7 +208151,7 @@ const lessDefaults = new LanguageServiceDefaultsImpl$3(
|
|
|
208022
208151
|
modeConfigurationDefault$2
|
|
208023
208152
|
);
|
|
208024
208153
|
function getMode$3() {
|
|
208025
|
-
return __vitePreload(() => import("./cssMode-
|
|
208154
|
+
return __vitePreload(() => import("./cssMode-CAEObGNt.js"), true ? __vite__mapDeps([0,1]) : void 0, import.meta.url);
|
|
208026
208155
|
}
|
|
208027
208156
|
languages.onLanguage("less", () => {
|
|
208028
208157
|
getMode$3().then((mode2) => mode2.setupMode(lessDefaults));
|
|
@@ -208127,7 +208256,7 @@ const razorLanguageService = registerHTMLLanguageService(
|
|
|
208127
208256
|
);
|
|
208128
208257
|
const razorDefaults = razorLanguageService.defaults;
|
|
208129
208258
|
function getMode$2() {
|
|
208130
|
-
return __vitePreload(() => import("./htmlMode-
|
|
208259
|
+
return __vitePreload(() => import("./htmlMode-B6fxDDPF.js"), true ? __vite__mapDeps([2,1]) : void 0, import.meta.url);
|
|
208131
208260
|
}
|
|
208132
208261
|
function registerHTMLLanguageService(languageId, options = optionsDefault, modeConfiguration = getConfigurationDefault(languageId)) {
|
|
208133
208262
|
const defaults = new LanguageServiceDefaultsImpl$2(languageId, options, modeConfiguration);
|
|
@@ -208211,7 +208340,7 @@ const jsonDefaults = new LanguageServiceDefaultsImpl$1(
|
|
|
208211
208340
|
);
|
|
208212
208341
|
const getWorker$1 = () => getMode$1().then((mode2) => mode2.getWorker());
|
|
208213
208342
|
function getMode$1() {
|
|
208214
|
-
return __vitePreload(() => import("./jsonMode-
|
|
208343
|
+
return __vitePreload(() => import("./jsonMode-CqspkWCb.js"), true ? __vite__mapDeps([3,1]) : void 0, import.meta.url);
|
|
208215
208344
|
}
|
|
208216
208345
|
languages.register({
|
|
208217
208346
|
id: "json",
|
|
@@ -208457,7 +208586,7 @@ const getJavaScriptWorker = () => {
|
|
|
208457
208586
|
return getMode().then((mode) => mode.getJavaScriptWorker());
|
|
208458
208587
|
};
|
|
208459
208588
|
function getMode() {
|
|
208460
|
-
return __vitePreload(() => import("./tsMode-
|
|
208589
|
+
return __vitePreload(() => import("./tsMode-BWdOxk6v.js"), true ? [] : void 0, import.meta.url);
|
|
208461
208590
|
}
|
|
208462
208591
|
languages.onLanguage("typescript", () => {
|
|
208463
208592
|
return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults));
|
|
@@ -208652,49 +208781,49 @@ registerLanguage({
|
|
|
208652
208781
|
extensions: [".ftl", ".ftlh", ".ftlx"],
|
|
208653
208782
|
aliases: ["FreeMarker2", "Apache FreeMarker2"],
|
|
208654
208783
|
loader: () => {
|
|
208655
|
-
return __vitePreload(() => import("./freemarker2-
|
|
208784
|
+
return __vitePreload(() => import("./freemarker2-D63hnnDq.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
|
|
208656
208785
|
}
|
|
208657
208786
|
});
|
|
208658
208787
|
registerLanguage({
|
|
208659
208788
|
id: "freemarker2.tag-angle.interpolation-dollar",
|
|
208660
208789
|
aliases: ["FreeMarker2 (Angle/Dollar)", "Apache FreeMarker2 (Angle/Dollar)"],
|
|
208661
208790
|
loader: () => {
|
|
208662
|
-
return __vitePreload(() => import("./freemarker2-
|
|
208791
|
+
return __vitePreload(() => import("./freemarker2-D63hnnDq.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationDollar);
|
|
208663
208792
|
}
|
|
208664
208793
|
});
|
|
208665
208794
|
registerLanguage({
|
|
208666
208795
|
id: "freemarker2.tag-bracket.interpolation-dollar",
|
|
208667
208796
|
aliases: ["FreeMarker2 (Bracket/Dollar)", "Apache FreeMarker2 (Bracket/Dollar)"],
|
|
208668
208797
|
loader: () => {
|
|
208669
|
-
return __vitePreload(() => import("./freemarker2-
|
|
208798
|
+
return __vitePreload(() => import("./freemarker2-D63hnnDq.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationDollar);
|
|
208670
208799
|
}
|
|
208671
208800
|
});
|
|
208672
208801
|
registerLanguage({
|
|
208673
208802
|
id: "freemarker2.tag-angle.interpolation-bracket",
|
|
208674
208803
|
aliases: ["FreeMarker2 (Angle/Bracket)", "Apache FreeMarker2 (Angle/Bracket)"],
|
|
208675
208804
|
loader: () => {
|
|
208676
|
-
return __vitePreload(() => import("./freemarker2-
|
|
208805
|
+
return __vitePreload(() => import("./freemarker2-D63hnnDq.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAngleInterpolationBracket);
|
|
208677
208806
|
}
|
|
208678
208807
|
});
|
|
208679
208808
|
registerLanguage({
|
|
208680
208809
|
id: "freemarker2.tag-bracket.interpolation-bracket",
|
|
208681
208810
|
aliases: ["FreeMarker2 (Bracket/Bracket)", "Apache FreeMarker2 (Bracket/Bracket)"],
|
|
208682
208811
|
loader: () => {
|
|
208683
|
-
return __vitePreload(() => import("./freemarker2-
|
|
208812
|
+
return __vitePreload(() => import("./freemarker2-D63hnnDq.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagBracketInterpolationBracket);
|
|
208684
208813
|
}
|
|
208685
208814
|
});
|
|
208686
208815
|
registerLanguage({
|
|
208687
208816
|
id: "freemarker2.tag-auto.interpolation-dollar",
|
|
208688
208817
|
aliases: ["FreeMarker2 (Auto/Dollar)", "Apache FreeMarker2 (Auto/Dollar)"],
|
|
208689
208818
|
loader: () => {
|
|
208690
|
-
return __vitePreload(() => import("./freemarker2-
|
|
208819
|
+
return __vitePreload(() => import("./freemarker2-D63hnnDq.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationDollar);
|
|
208691
208820
|
}
|
|
208692
208821
|
});
|
|
208693
208822
|
registerLanguage({
|
|
208694
208823
|
id: "freemarker2.tag-auto.interpolation-bracket",
|
|
208695
208824
|
aliases: ["FreeMarker2 (Auto/Bracket)", "Apache FreeMarker2 (Auto/Bracket)"],
|
|
208696
208825
|
loader: () => {
|
|
208697
|
-
return __vitePreload(() => import("./freemarker2-
|
|
208826
|
+
return __vitePreload(() => import("./freemarker2-D63hnnDq.js"), true ? [] : void 0, import.meta.url).then((m) => m.TagAutoInterpolationBracket);
|
|
208698
208827
|
}
|
|
208699
208828
|
});
|
|
208700
208829
|
registerLanguage({
|
|
@@ -208715,7 +208844,7 @@ registerLanguage({
|
|
|
208715
208844
|
extensions: [".handlebars", ".hbs"],
|
|
208716
208845
|
aliases: ["Handlebars", "handlebars", "hbs"],
|
|
208717
208846
|
mimetypes: ["text/x-handlebars-template"],
|
|
208718
|
-
loader: () => __vitePreload(() => import("./handlebars-
|
|
208847
|
+
loader: () => __vitePreload(() => import("./handlebars-UYo_IYpi.js"), true ? [] : void 0, import.meta.url)
|
|
208719
208848
|
});
|
|
208720
208849
|
registerLanguage({
|
|
208721
208850
|
id: "hcl",
|
|
@@ -208728,7 +208857,7 @@ registerLanguage({
|
|
|
208728
208857
|
extensions: [".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm"],
|
|
208729
208858
|
aliases: ["HTML", "htm", "html", "xhtml"],
|
|
208730
208859
|
mimetypes: ["text/html", "text/x-jshtm", "text/template", "text/ng-template"],
|
|
208731
|
-
loader: () => __vitePreload(() => import("./html-
|
|
208860
|
+
loader: () => __vitePreload(() => import("./html-2dvYDi3g.js"), true ? [] : void 0, import.meta.url)
|
|
208732
208861
|
});
|
|
208733
208862
|
registerLanguage({
|
|
208734
208863
|
id: "ini",
|
|
@@ -208751,7 +208880,7 @@ registerLanguage({
|
|
|
208751
208880
|
filenames: ["jakefile"],
|
|
208752
208881
|
aliases: ["JavaScript", "javascript", "js"],
|
|
208753
208882
|
mimetypes: ["text/javascript"],
|
|
208754
|
-
loader: () => __vitePreload(() => import("./javascript-
|
|
208883
|
+
loader: () => __vitePreload(() => import("./javascript-8RyFDDdk.js"), true ? __vite__mapDeps([4,5]) : void 0, import.meta.url)
|
|
208755
208884
|
});
|
|
208756
208885
|
registerLanguage({
|
|
208757
208886
|
id: "julia",
|
|
@@ -208790,7 +208919,7 @@ registerLanguage({
|
|
|
208790
208919
|
extensions: [".liquid", ".html.liquid"],
|
|
208791
208920
|
aliases: ["Liquid", "liquid"],
|
|
208792
208921
|
mimetypes: ["application/liquid"],
|
|
208793
|
-
loader: () => __vitePreload(() => import("./liquid-
|
|
208922
|
+
loader: () => __vitePreload(() => import("./liquid-DaEnAsYY.js"), true ? [] : void 0, import.meta.url)
|
|
208794
208923
|
});
|
|
208795
208924
|
registerLanguage({
|
|
208796
208925
|
id: "m3",
|
|
@@ -208808,7 +208937,7 @@ registerLanguage({
|
|
|
208808
208937
|
id: "mdx",
|
|
208809
208938
|
extensions: [".mdx"],
|
|
208810
208939
|
aliases: ["MDX", "mdx"],
|
|
208811
|
-
loader: () => __vitePreload(() => import("./mdx-
|
|
208940
|
+
loader: () => __vitePreload(() => import("./mdx-Q-qSZsFg.js"), true ? [] : void 0, import.meta.url)
|
|
208812
208941
|
});
|
|
208813
208942
|
registerLanguage({
|
|
208814
208943
|
id: "mips",
|
|
@@ -208907,7 +209036,7 @@ registerLanguage({
|
|
|
208907
209036
|
extensions: [".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi"],
|
|
208908
209037
|
aliases: ["Python", "py"],
|
|
208909
209038
|
firstLine: "^#!/.*\\bpython[0-9.-]*\\b",
|
|
208910
|
-
loader: () => __vitePreload(() => import("./python-
|
|
209039
|
+
loader: () => __vitePreload(() => import("./python-Sw0S0BJV.js"), true ? [] : void 0, import.meta.url)
|
|
208911
209040
|
});
|
|
208912
209041
|
registerLanguage({
|
|
208913
209042
|
id: "qsharp",
|
|
@@ -208926,7 +209055,7 @@ registerLanguage({
|
|
|
208926
209055
|
extensions: [".cshtml"],
|
|
208927
209056
|
aliases: ["Razor", "razor"],
|
|
208928
209057
|
mimetypes: ["text/x-cshtml"],
|
|
208929
|
-
loader: () => __vitePreload(() => import("./razor-
|
|
209058
|
+
loader: () => __vitePreload(() => import("./razor-C7zQmabz.js"), true ? [] : void 0, import.meta.url)
|
|
208930
209059
|
});
|
|
208931
209060
|
registerLanguage({
|
|
208932
209061
|
id: "redis",
|
|
@@ -209059,7 +209188,7 @@ registerLanguage({
|
|
|
209059
209188
|
aliases: ["TypeScript", "ts", "typescript"],
|
|
209060
209189
|
mimetypes: ["text/typescript"],
|
|
209061
209190
|
loader: () => {
|
|
209062
|
-
return __vitePreload(() => import("./typescript-
|
|
209191
|
+
return __vitePreload(() => import("./typescript-BrdIiKZf.js"), true ? [] : void 0, import.meta.url);
|
|
209063
209192
|
}
|
|
209064
209193
|
});
|
|
209065
209194
|
registerLanguage({
|
|
@@ -209104,14 +209233,14 @@ registerLanguage({
|
|
|
209104
209233
|
firstLine: "(\\<\\?xml.*)|(\\<svg)|(\\<\\!doctype\\s+svg)",
|
|
209105
209234
|
aliases: ["XML", "xml"],
|
|
209106
209235
|
mimetypes: ["text/xml", "application/xml", "application/xaml+xml", "application/xml-dtd"],
|
|
209107
|
-
loader: () => __vitePreload(() => import("./xml-
|
|
209236
|
+
loader: () => __vitePreload(() => import("./xml-DbKLNPOq.js"), true ? [] : void 0, import.meta.url)
|
|
209108
209237
|
});
|
|
209109
209238
|
registerLanguage({
|
|
209110
209239
|
id: "yaml",
|
|
209111
209240
|
extensions: [".yaml", ".yml"],
|
|
209112
209241
|
aliases: ["YAML", "yaml", "YML", "yml"],
|
|
209113
209242
|
mimetypes: ["application/x-yaml", "text/x-yaml"],
|
|
209114
|
-
loader: () => __vitePreload(() => import("./yaml-
|
|
209243
|
+
loader: () => __vitePreload(() => import("./yaml-ehupJBsZ.js"), true ? [] : void 0, import.meta.url)
|
|
209115
209244
|
});
|
|
209116
209245
|
var __defProp = Object.defineProperty;
|
|
209117
209246
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -213403,6 +213532,7 @@ function SettingsDialog({ open, onClose }) {
|
|
|
213403
213532
|
const [trackTime, setTrackTime] = reactExports.useState(false);
|
|
213404
213533
|
const [idleTimeoutMin, setIdleTimeoutMin] = reactExports.useState(15);
|
|
213405
213534
|
const [saved, setSaved] = reactExports.useState(false);
|
|
213535
|
+
const [version, setVersion] = reactExports.useState("");
|
|
213406
213536
|
const loadProfiles = async () => {
|
|
213407
213537
|
const data = await window.worker.listProfiles();
|
|
213408
213538
|
setProfileList(data);
|
|
@@ -213413,6 +213543,8 @@ function SettingsDialog({ open, onClose }) {
|
|
|
213413
213543
|
loadProfiles();
|
|
213414
213544
|
setEditingId(null);
|
|
213415
213545
|
setSaved(false);
|
|
213546
|
+
window.worker.getVersion().then(setVersion).catch(() => {
|
|
213547
|
+
});
|
|
213416
213548
|
}
|
|
213417
213549
|
}, [open]);
|
|
213418
213550
|
const startEditing = (profile) => {
|
|
@@ -213583,7 +213715,16 @@ function SettingsDialog({ open, onClose }) {
|
|
|
213583
213715
|
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "btn-secondary", onClick: onClose, children: "Close" })
|
|
213584
213716
|
] })
|
|
213585
213717
|
] })
|
|
213586
|
-
)
|
|
213718
|
+
),
|
|
213719
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "settings-section settings-about", children: [
|
|
213720
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h4", { children: "About" }),
|
|
213721
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "about-name", children: [
|
|
213722
|
+
"ctlsurf",
|
|
213723
|
+
version ? ` · v${version}` : ""
|
|
213724
|
+
] }),
|
|
213725
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "about-copyright", children: "© 2026 Phenx, Inc. All rights reserved." }),
|
|
213726
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "settings-hint", children: "Proprietary and confidential. No license to use, copy, modify, or distribute is granted except under a separate written agreement with Phenx, Inc." })
|
|
213727
|
+
] })
|
|
213587
213728
|
] }) });
|
|
213588
213729
|
}
|
|
213589
213730
|
const STATUS_OPTIONS = [
|