lookatstudy-mobile 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/install-termux.sh +6 -14
- package/package.json +1 -1
- package/server.cjs +194 -23
- package/web/assets/{abnfDiagram-N423BO3Z-wFTVk74C.js → abnfDiagram-N423BO3Z-BXpGdNuJ.js} +1 -1
- package/web/assets/api-web-CgR5Mh04.js +1 -0
- package/web/assets/{arc-DrtveQeQ.js → arc-D3nd0e6x.js} +1 -1
- package/web/assets/{architectureDiagram-T3A2C74G-iSGM_chc.js → architectureDiagram-T3A2C74G-_KSRK-eF.js} +1 -1
- package/web/assets/{blockDiagram-VBNYF7ZC-Cormy30B.js → blockDiagram-VBNYF7ZC-CTH5HFTT.js} +1 -1
- package/web/assets/{c4Diagram-5PPSVZJV-D1Y3B0gV.js → c4Diagram-5PPSVZJV-A0wHAp7g.js} +1 -1
- package/web/assets/channel-DUHQbrVw.js +1 -0
- package/web/assets/{chunk-2GRJ4B5K-J_o-lKOF.js → chunk-2GRJ4B5K-DqNyC6-z.js} +1 -1
- package/web/assets/{chunk-2Q5K7J3B-gPWW6y-A.js → chunk-2Q5K7J3B-CdnPfIqd.js} +1 -1
- package/web/assets/{chunk-5RXB4S5H-yigaUOuY.js → chunk-5RXB4S5H-BGhsRWsR.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-DEnbC3cS.js → chunk-5VM5RSS4-B52gSt0J.js} +1 -1
- package/web/assets/{chunk-6Q2QTUOP-ComVOTYh.js → chunk-6Q2QTUOP-sn207b1U.js} +1 -1
- package/web/assets/{chunk-GF5L2VYU-DIOiMyrX.js → chunk-GF5L2VYU-DQ35exrC.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-m78qF4H_.js → chunk-JWPE2WC7-V2anUJlE.js} +1 -1
- package/web/assets/{chunk-KBJHAD2P-Clh4Yr08.js → chunk-KBJHAD2P-f5DAZkkL.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-BMETLjTZ.js → chunk-RYQCIY6F-C4aIfT0J.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-DyGYyX9Z.js → chunk-XXDRQBXY-BnihJuYl.js} +1 -1
- package/web/assets/classDiagram-JCYQIIEL-CRVgeWuu.js +1 -0
- package/web/assets/classDiagram-v2-OCEON4UE-CRVgeWuu.js +1 -0
- package/web/assets/{cose-bilkent-JH36ORCC-BO1RXGb2.js → cose-bilkent-JH36ORCC-BnJjiie5.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-DGYmCJ7v.js → cynefin-VYW2F7L2-CG6JQnIq.js} +1 -1
- package/web/assets/{cynefinDiagram-MW4NZA55-iSEBLol5.js → cynefinDiagram-MW4NZA55-Bpm2ec5V.js} +1 -1
- package/web/assets/{dagre-VZM6K2ZE-ByPA0378.js → dagre-VZM6K2ZE-Dp-oDNQg.js} +1 -1
- package/web/assets/{diagram-7IWD3JNH-CUCAodFK.js → diagram-7IWD3JNH-B7UEiNnb.js} +1 -1
- package/web/assets/{diagram-B4RE2ZJO-DIFgvaCt.js → diagram-B4RE2ZJO-DHSLHS46.js} +1 -1
- package/web/assets/{diagram-LBJQPF4R-XBahy00D.js → diagram-LBJQPF4R-V8wA5qd1.js} +1 -1
- package/web/assets/{diagram-Q27KOJAE-DwqZKJhm.js → diagram-Q27KOJAE-BExt6RO_.js} +1 -1
- package/web/assets/{diagram-UB23O5K3-BXMt7Op8.js → diagram-UB23O5K3-C5IsI8Wd.js} +1 -1
- package/web/assets/{ebnfDiagram-BXEA7PRR-Cpz6ljuD.js → ebnfDiagram-BXEA7PRR-worbNlxi.js} +1 -1
- package/web/assets/{erDiagram-JOGREHBK-BLsnp8Sa.js → erDiagram-JOGREHBK-SmQLtE55.js} +1 -1
- package/web/assets/{flowDiagram-UKHOOZJN-DdGtlHt6.js → flowDiagram-UKHOOZJN-Dlj-KsqU.js} +1 -1
- package/web/assets/{ganttDiagram-PKOTCBZU-BA83m21H.js → ganttDiagram-PKOTCBZU-wrhVphV1.js} +1 -1
- package/web/assets/{gitGraphDiagram-DS77QQ5N-PHvv3GCK.js → gitGraphDiagram-DS77QQ5N-x5RHkYuw.js} +1 -1
- package/web/assets/index-D0a3MQ1T.css +1 -0
- package/web/assets/index-DjwNXkwE.js +563 -0
- package/web/assets/{infoDiagram-6WML65LV-KHrgwc8I.js → infoDiagram-6WML65LV-QSObfGHr.js} +1 -1
- package/web/assets/{ishikawaDiagram-WSZJBQD7-DX5VKU6v.js → ishikawaDiagram-WSZJBQD7-DnFs9iM-.js} +1 -1
- package/web/assets/{journeyDiagram-NVQOT4AX-DYR1w7M7.js → journeyDiagram-NVQOT4AX-DRBcWglZ.js} +1 -1
- package/web/assets/{kanban-definition-27J2QSJJ-YdTG5_IH.js → kanban-definition-27J2QSJJ-DJ4YDBq2.js} +1 -1
- package/web/assets/{linear-BG8Ya1Ih.js → linear-CakPBKKz.js} +1 -1
- package/web/assets/{mermaid.core-DtRZ8QZA.js → mermaid.core-ChTdgwuo.js} +4 -4
- package/web/assets/{mindmap-definition-FAOFIHXS-fo77Y4vS.js → mindmap-definition-FAOFIHXS-CheoRsDG.js} +1 -1
- package/web/assets/{pegDiagram-VL7TDLO6-DNcbtY8y.js → pegDiagram-VL7TDLO6-CZaM-7KZ.js} +1 -1
- package/web/assets/{pieDiagram-7S7Q4E2Y-DwCsKwsz.js → pieDiagram-7S7Q4E2Y-C5M0AvL-.js} +1 -1
- package/web/assets/{quadrantDiagram-CIZ2JOQS-DBsVlNC3.js → quadrantDiagram-CIZ2JOQS-COeP6vBt.js} +1 -1
- package/web/assets/{railroadDiagram-AXF67PYL-DF2bC_5D.js → railroadDiagram-AXF67PYL-C4o9Lel-.js} +1 -1
- package/web/assets/{requirementDiagram-LRYGKXZP-BP7teWQ3.js → requirementDiagram-LRYGKXZP-GMDyeWVe.js} +1 -1
- package/web/assets/{sankeyDiagram-W5VNT64P-DNoGqQqk.js → sankeyDiagram-W5VNT64P-DSZwyoSj.js} +1 -1
- package/web/assets/{sequenceDiagram-SI44F4Z6-DJtGHgQX.js → sequenceDiagram-SI44F4Z6-DGBuqaHP.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-CR8vipze.js → sizeCapture-X5ZJPWSS-B-zxFv3_.js} +1 -1
- package/web/assets/{stateDiagram-OKZ733FA-Bk1VizvY.js → stateDiagram-OKZ733FA-D7G84Iof.js} +1 -1
- package/web/assets/stateDiagram-v2-UEYNNEHI-DFfmMQ2j.js +1 -0
- package/web/assets/{swimlanes-SLNWSIFB-6eb6Yq1y.js → swimlanes-SLNWSIFB-DzSBg8NJ.js} +2 -2
- package/web/assets/swimlanesDiagram-ULZ7WXOC-DvlFN_gb.js +8 -0
- package/web/assets/{timeline-definition-Z64GVDOM-CNhXrkJs.js → timeline-definition-Z64GVDOM-CsGorQl9.js} +1 -1
- package/web/assets/{vennDiagram-T6HMQDX7-NX3AZCBh.js → vennDiagram-T6HMQDX7-DDwrAPDB.js} +1 -1
- package/web/assets/{wardleyDiagram-T6FBY63Y-swaO6e6e.js → wardleyDiagram-T6FBY63Y-CcclmHqH.js} +1 -1
- package/web/assets/{xychartDiagram-ELKLHX3M-CL4seKwn.js → xychartDiagram-ELKLHX3M-CJORQave.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/api-web-CfrRrKkd.js +0 -1
- package/web/assets/channel-CO81t0iZ.js +0 -1
- package/web/assets/classDiagram-JCYQIIEL--cirGlAO.js +0 -1
- package/web/assets/classDiagram-v2-OCEON4UE--cirGlAO.js +0 -1
- package/web/assets/index-BvQFXLZ2.js +0 -553
- package/web/assets/index-C6xbHOXP.css +0 -1
- package/web/assets/stateDiagram-v2-UEYNNEHI-BuuaxVSs.js +0 -1
- package/web/assets/swimlanesDiagram-ULZ7WXOC-DEN6Tg7V.js +0 -8
package/install-termux.sh
CHANGED
|
@@ -111,7 +111,7 @@ install_voice() {
|
|
|
111
111
|
tar -xzf voice.tgz --strip-components=1 -C "$APP_DIR/node_modules"
|
|
112
112
|
rm -f voice.tgz
|
|
113
113
|
ok "语音引擎就位(npm 镜像): $APP_DIR/node_modules/sherpa-onnx-node"
|
|
114
|
-
info "
|
|
114
|
+
info "语音模型在应用内按需下载(设置 → 语音能力):朗读本地档约 430MB;听写建议云档(Groq/Azure),本地 Whisper 约 360MB~1GB"
|
|
115
115
|
info "装完模型重启服务生效: bash ~/lookatstudy/start.sh"
|
|
116
116
|
return 0
|
|
117
117
|
fi
|
|
@@ -304,19 +304,11 @@ case "$(getprop ro.product.manufacturer 2>/dev/null | tr '[:upper:]' '[:lower:]'
|
|
|
304
304
|
;;
|
|
305
305
|
esac
|
|
306
306
|
|
|
307
|
-
# ── 6.5 语音引擎(Termux
|
|
308
|
-
# 引擎包 ~12MB
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
echo ''
|
|
313
|
-
info "可选:Termux 语音朗读/听写(引擎包约 12MB,语音模型在应用内另下)。"
|
|
314
|
-
read -r -p "[*] 现在安装吗?[y/N] " ans </dev/tty 2>/dev/null || ans="n"
|
|
315
|
-
case "$ans" in
|
|
316
|
-
y|Y) install_voice || true ;;
|
|
317
|
-
*) info "跳过(以后可: bash install-termux.sh --voice)" ;;
|
|
318
|
-
esac
|
|
319
|
-
fi
|
|
307
|
+
# ── 6.5 语音引擎(Termux 专属交叉编译包,默认安装) ────────────
|
|
308
|
+
# 引擎包 ~12MB 一次到位(相对 Node+系统升级的体量可忽略;语音朗读/听写默认能力)。
|
|
309
|
+
# 全程零交互:失败不阻断安装(朗读在线档/听写云档不依赖本地引擎),重跑安装命令即补装。
|
|
310
|
+
# 兼容旧写法:--voice 参数仍被接受(现为默认,传不传一样)。
|
|
311
|
+
install_voice || true
|
|
320
312
|
|
|
321
313
|
# ── 7. 启动 + 打印访问链接 ───────────────────────────────────
|
|
322
314
|
info "启动 LookatStudy..."
|
package/package.json
CHANGED
package/server.cjs
CHANGED
|
@@ -12440,6 +12440,10 @@ var init_schema = __esm({
|
|
|
12440
12440
|
id: text("id").primaryKey(),
|
|
12441
12441
|
/** 用户起的名字,如"智谱 CodingPlan CN" */
|
|
12442
12442
|
label: text("label").notNull(),
|
|
12443
|
+
/** 用途分区:主模型 / 看图覆盖 / 朗读 / 听写(v0.15 前的行全部是 llm) */
|
|
12444
|
+
kind: text("kind", {
|
|
12445
|
+
enum: ["llm", "vision", "tts", "asr"]
|
|
12446
|
+
}).notNull().default("llm"),
|
|
12443
12447
|
/** 协议:openai-compatible(默认,覆盖 90% 场景)/ anthropic / google */
|
|
12444
12448
|
protocol: text("protocol", {
|
|
12445
12449
|
enum: ["openai-compatible", "anthropic", "google"]
|
|
@@ -12758,6 +12762,8 @@ CREATE INDEX IF NOT EXISTS idx_memory_category ON memory(category);
|
|
|
12758
12762
|
CREATE TABLE IF NOT EXISTS custom_providers (
|
|
12759
12763
|
id TEXT PRIMARY KEY, -- 'custom-' + uuid\uFF0C\u4E5F\u4F5C\u4E3A active_provider \u7684\u503C
|
|
12760
12764
|
label TEXT NOT NULL, -- \u7528\u6237\u8D77\u7684\u540D\u5B57\uFF0C\u5982 "\u667A\u8C31 CodingPlan CN"
|
|
12765
|
+
kind TEXT NOT NULL DEFAULT 'llm' -- \u7528\u9014\u5206\u533A\uFF1A\u4E3B\u6A21\u578B/\u770B\u56FE/\u6717\u8BFB/\u542C\u5199\uFF08v0.15 \u524D\u7684\u884C\u5168\u90E8\u662F llm\uFF09
|
|
12766
|
+
CHECK (kind IN ('llm', 'vision', 'tts', 'asr')),
|
|
12761
12767
|
protocol TEXT NOT NULL DEFAULT 'openai-compatible'
|
|
12762
12768
|
CHECK (protocol IN ('openai-compatible', 'anthropic', 'google')),
|
|
12763
12769
|
base_url TEXT NOT NULL, -- \u5982 https://api.z.ai/api/coding/paas/v4
|
|
@@ -12961,6 +12967,7 @@ function runMigrations(db) {
|
|
|
12961
12967
|
addColumnIfMissing("content_nodes", "knowledge_points", "TEXT");
|
|
12962
12968
|
addColumnIfMissing("exercises", "kc_title", "TEXT");
|
|
12963
12969
|
addColumnIfMissing("courses", "source_lang", "TEXT");
|
|
12970
|
+
addColumnIfMissing("custom_providers", "kind", "TEXT NOT NULL DEFAULT 'llm'");
|
|
12964
12971
|
addColumnIfMissing("memory", "course_id", "TEXT");
|
|
12965
12972
|
db.run("CREATE INDEX IF NOT EXISTS idx_memory_course ON memory(course_id);");
|
|
12966
12973
|
addColumnIfMissing("canvas_items", "source_type", "TEXT");
|
|
@@ -59167,6 +59174,7 @@ function rowToCustomProvider(row) {
|
|
|
59167
59174
|
return {
|
|
59168
59175
|
id: row.id,
|
|
59169
59176
|
label: row.label,
|
|
59177
|
+
kind: row.kind ?? "llm",
|
|
59170
59178
|
protocol: row.protocol,
|
|
59171
59179
|
baseUrl: row.baseUrl,
|
|
59172
59180
|
defaultModel: row.defaultModel,
|
|
@@ -59189,6 +59197,7 @@ function createCustomProvider(db, input) {
|
|
|
59189
59197
|
db.insert(customProviders).values({
|
|
59190
59198
|
id,
|
|
59191
59199
|
label: input.label,
|
|
59200
|
+
kind: input.kind ?? "llm",
|
|
59192
59201
|
protocol: input.protocol,
|
|
59193
59202
|
baseUrl: input.baseUrl,
|
|
59194
59203
|
apiKey: input.apiKey ?? null,
|
|
@@ -59205,6 +59214,8 @@ function updateCustomProvider(db, id, input) {
|
|
|
59205
59214
|
const patch = {};
|
|
59206
59215
|
if (input.label !== void 0)
|
|
59207
59216
|
patch.label = input.label;
|
|
59217
|
+
if (input.kind !== void 0)
|
|
59218
|
+
patch.kind = input.kind;
|
|
59208
59219
|
if (input.protocol !== void 0)
|
|
59209
59220
|
patch.protocol = input.protocol;
|
|
59210
59221
|
if (input.baseUrl !== void 0)
|
|
@@ -59228,6 +59239,7 @@ function getCustomProviderRaw(db, id) {
|
|
|
59228
59239
|
return null;
|
|
59229
59240
|
return {
|
|
59230
59241
|
id: row.id,
|
|
59242
|
+
kind: row.kind ?? "llm",
|
|
59231
59243
|
protocol: row.protocol,
|
|
59232
59244
|
baseUrl: row.baseUrl,
|
|
59233
59245
|
apiKey: row.apiKey,
|
|
@@ -491056,17 +491068,27 @@ function speedToRatePercent(speed) {
|
|
|
491056
491068
|
return `${pct >= 0 ? "+" : ""}${pct}%`;
|
|
491057
491069
|
}
|
|
491058
491070
|
function resolveTtsTier(settings2) {
|
|
491059
|
-
const engineRaw = settings2.tts_engine;
|
|
491060
|
-
const
|
|
491071
|
+
const engineRaw = settings2.tts_engine?.trim() ?? "";
|
|
491072
|
+
const isCustom = engineRaw.startsWith("custom-");
|
|
491073
|
+
const engine = isCustom || engineRaw === "azure" || engineRaw === "local" || engineRaw === "edge" ? isCustom ? "custom" : engineRaw : DEFAULT_TTS_ENGINE;
|
|
491061
491074
|
const voice = engine === "azure" ? settings2.azure_tts_voice?.trim() || DEFAULT_AZURE_TTS_VOICE : settings2.tts_voice_edge?.trim() || DEFAULT_EDGE_VOICE;
|
|
491062
491075
|
return {
|
|
491063
491076
|
engine,
|
|
491077
|
+
customProviderId: isCustom ? engineRaw : null,
|
|
491064
491078
|
voice,
|
|
491079
|
+
customVoice: settings2.tts_custom_voice?.trim() || null,
|
|
491065
491080
|
speed: clampSpeed(settings2.tts_speed),
|
|
491066
491081
|
azureKey: settings2.azure_tts_api_key?.trim() || null,
|
|
491067
491082
|
azureRegion: settings2.azure_tts_region?.trim() || null
|
|
491068
491083
|
};
|
|
491069
491084
|
}
|
|
491085
|
+
function speedToOpenaiSpeed(speed) {
|
|
491086
|
+
if (speed <= 0.85)
|
|
491087
|
+
return "slow";
|
|
491088
|
+
if (speed >= 1.15)
|
|
491089
|
+
return "fast";
|
|
491090
|
+
return "normal";
|
|
491091
|
+
}
|
|
491070
491092
|
function azureTtsMissing(cfg) {
|
|
491071
491093
|
if (cfg.engine !== "azure")
|
|
491072
491094
|
return null;
|
|
@@ -491156,6 +491178,39 @@ async function synthesizeAzureWav(text3, opts) {
|
|
|
491156
491178
|
return buf;
|
|
491157
491179
|
}
|
|
491158
491180
|
|
|
491181
|
+
// src/main/services/speech/openai-tts-client.ts
|
|
491182
|
+
function openaiTtsUrl(baseUrl) {
|
|
491183
|
+
return `${baseUrl.replace(/\/+$/, "")}/audio/speech`;
|
|
491184
|
+
}
|
|
491185
|
+
async function synthesizeOpenaiTts(opts, timeoutMs = 3e4) {
|
|
491186
|
+
const headers = { "Content-Type": "application/json" };
|
|
491187
|
+
if (opts.apiKey)
|
|
491188
|
+
headers.Authorization = `Bearer ${opts.apiKey}`;
|
|
491189
|
+
const body = {
|
|
491190
|
+
model: opts.model,
|
|
491191
|
+
input: opts.text,
|
|
491192
|
+
response_format: "mp3"
|
|
491193
|
+
};
|
|
491194
|
+
if (opts.voice)
|
|
491195
|
+
body.voice = opts.voice;
|
|
491196
|
+
if (opts.speed && opts.speed !== "normal")
|
|
491197
|
+
body.speed = opts.speed;
|
|
491198
|
+
const res = await fetch(openaiTtsUrl(opts.baseUrl), {
|
|
491199
|
+
method: "POST",
|
|
491200
|
+
headers,
|
|
491201
|
+
body: JSON.stringify(body),
|
|
491202
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
491203
|
+
});
|
|
491204
|
+
if (!res.ok) {
|
|
491205
|
+
const detail = await res.text().catch(() => "");
|
|
491206
|
+
throw new Error(`tts HTTP ${res.status}${detail ? `: ${detail.slice(0, 200)}` : ""}`);
|
|
491207
|
+
}
|
|
491208
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
491209
|
+
if (buf.length === 0)
|
|
491210
|
+
throw new Error("tts empty response");
|
|
491211
|
+
return buf;
|
|
491212
|
+
}
|
|
491213
|
+
|
|
491159
491214
|
// src/main/services/speech/tts-service.ts
|
|
491160
491215
|
var TTS_ENTRY = SPEECH_MODELS_MANIFEST.models.find((m) => m.id === "tts-kokoro");
|
|
491161
491216
|
var active = null;
|
|
@@ -491163,13 +491218,15 @@ function localModelReady(dataDir) {
|
|
|
491163
491218
|
return isSpeechEngineLoadable() && readSpeechModelStatus(dataDir, TTS_ENTRY).state === "ready";
|
|
491164
491219
|
}
|
|
491165
491220
|
async function synthSentence(dataDir, cfg, engine, sentence, opts) {
|
|
491221
|
+
const isMp3 = engine === "edge" || engine === "custom";
|
|
491166
491222
|
const key = ttsCacheKey({
|
|
491167
|
-
engine,
|
|
491168
|
-
|
|
491223
|
+
engine: engine === "custom" ? cfg.customProviderId ?? "custom" : engine,
|
|
491224
|
+
// custom 缓存键把模型并进 voice 段(同 provider 换模型/音色不串味)
|
|
491225
|
+
voice: engine === "local" ? `sid-${opts.sid}` : engine === "custom" ? `${opts.custom?.model ?? "?"}|${opts.custom?.voice ?? ""}` : cfg.voice,
|
|
491169
491226
|
speed: cfg.speed,
|
|
491170
491227
|
sentence
|
|
491171
491228
|
});
|
|
491172
|
-
const mime =
|
|
491229
|
+
const mime = isMp3 ? "audio/mpeg" : "audio/wav";
|
|
491173
491230
|
const cached2 = readCachedAudio(dataDir, key, mime);
|
|
491174
491231
|
if (cached2)
|
|
491175
491232
|
return { bytes: cached2, mime, sampleRate: 24e3 };
|
|
@@ -491182,6 +491239,21 @@ async function synthSentence(dataDir, cfg, engine, sentence, opts) {
|
|
|
491182
491239
|
await writeCachedAudio(dataDir, key, mime, bytes2);
|
|
491183
491240
|
return { bytes: bytes2, mime, sampleRate: 24e3 };
|
|
491184
491241
|
}
|
|
491242
|
+
if (engine === "custom") {
|
|
491243
|
+
if (!opts.custom)
|
|
491244
|
+
throw new Error("custom tts provider not resolved");
|
|
491245
|
+
const mp3 = await synthesizeOpenaiTts({
|
|
491246
|
+
baseUrl: opts.custom.baseUrl,
|
|
491247
|
+
apiKey: opts.custom.apiKey,
|
|
491248
|
+
model: opts.custom.model,
|
|
491249
|
+
voice: opts.custom.voice ?? cfg.customVoice,
|
|
491250
|
+
text: sentence,
|
|
491251
|
+
speed: speedToOpenaiSpeed(cfg.speed)
|
|
491252
|
+
});
|
|
491253
|
+
const bytes2 = bufferToArrayBuffer(mp3);
|
|
491254
|
+
await writeCachedAudio(dataDir, key, mime, bytes2);
|
|
491255
|
+
return { bytes: bytes2, mime, sampleRate: 24e3 };
|
|
491256
|
+
}
|
|
491185
491257
|
if (engine === "azure") {
|
|
491186
491258
|
const wav = await synthesizeAzureWav(sentence, {
|
|
491187
491259
|
key: cfg.azureKey ?? "",
|
|
@@ -491216,6 +491288,9 @@ async function speakMessage(emit2, dataDir, settings2, messageId, rawText, deps
|
|
|
491216
491288
|
return { ok: false, reason: "azure-key-missing" };
|
|
491217
491289
|
if (missing === "region")
|
|
491218
491290
|
return { ok: false, reason: "azure-region-missing" };
|
|
491291
|
+
} else if (cfg.engine === "custom") {
|
|
491292
|
+
if (!deps.custom)
|
|
491293
|
+
return { ok: false, reason: "custom-provider-missing" };
|
|
491219
491294
|
}
|
|
491220
491295
|
const { sentences } = splitSentences(normalizeSpeechText(rawText), { flush: true });
|
|
491221
491296
|
if (sentences.length === 0)
|
|
@@ -491234,7 +491309,8 @@ async function speakMessage(emit2, dataDir, settings2, messageId, rawText, deps
|
|
|
491234
491309
|
const sentence = sentences[i];
|
|
491235
491310
|
const p = synth(dataDir, cfg, engine, sentence, {
|
|
491236
491311
|
sid,
|
|
491237
|
-
onChunk: () => !mine.stopped && active === mine
|
|
491312
|
+
onChunk: () => !mine.stopped && active === mine,
|
|
491313
|
+
custom: deps.custom ?? void 0
|
|
491238
491314
|
}).finally(() => inflight.delete(i));
|
|
491239
491315
|
inflight.set(i, p);
|
|
491240
491316
|
return p;
|
|
@@ -491322,10 +491398,12 @@ function speechModelsStatusSnapshot(dataDir) {
|
|
|
491322
491398
|
// src/main/services/speech/asr-tiers.ts
|
|
491323
491399
|
var DEFAULT_ASR_ENGINE = "local";
|
|
491324
491400
|
function resolveAsrTier(settings2) {
|
|
491325
|
-
const raw = settings2.asr_engine;
|
|
491326
|
-
const
|
|
491401
|
+
const raw = settings2.asr_engine?.trim() ?? "";
|
|
491402
|
+
const isCustom = raw.startsWith("custom-");
|
|
491403
|
+
const engine = isCustom || raw === "groq" || raw === "azure" || raw === "local" ? isCustom ? "custom" : raw : DEFAULT_ASR_ENGINE;
|
|
491327
491404
|
return {
|
|
491328
491405
|
engine,
|
|
491406
|
+
customProviderId: isCustom ? raw : null,
|
|
491329
491407
|
groqKey: settings2.groq_api_key?.trim() || null,
|
|
491330
491408
|
azureKey: settings2.azure_stt_api_key?.trim() || null,
|
|
491331
491409
|
azureRegion: settings2.azure_stt_region?.trim() || null
|
|
@@ -491373,31 +491451,40 @@ function asrCloudMissing(cfg) {
|
|
|
491373
491451
|
|
|
491374
491452
|
// src/main/services/speech/cloud-asr-client.ts
|
|
491375
491453
|
var GROQ_TRANSCRIBE_MODEL = "whisper-large-v3-turbo";
|
|
491376
|
-
function
|
|
491377
|
-
return "
|
|
491454
|
+
function openaiTranscribeUrl(baseUrl) {
|
|
491455
|
+
return `${baseUrl.replace(/\/+$/, "")}/audio/transcriptions`;
|
|
491378
491456
|
}
|
|
491379
491457
|
function azureSttUrl(region, language) {
|
|
491380
491458
|
return `https://${region}.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?language=${encodeURIComponent(language)}&format=simple`;
|
|
491381
491459
|
}
|
|
491382
|
-
async function
|
|
491460
|
+
async function openaiTranscribe(opts, timeoutMs = 3e4) {
|
|
491383
491461
|
const form = new FormData();
|
|
491384
|
-
form.append("file", new Blob([wav], { type: "audio/wav" }), "dictation.wav");
|
|
491385
|
-
form.append("model",
|
|
491386
|
-
form.append("language", language);
|
|
491462
|
+
form.append("file", new Blob([opts.wav], { type: "audio/wav" }), "dictation.wav");
|
|
491463
|
+
form.append("model", opts.model);
|
|
491464
|
+
form.append("language", opts.language);
|
|
491387
491465
|
form.append("response_format", "json");
|
|
491388
|
-
const
|
|
491466
|
+
const headers = {};
|
|
491467
|
+
if (opts.apiKey)
|
|
491468
|
+
headers.Authorization = `Bearer ${opts.apiKey}`;
|
|
491469
|
+
const res = await fetch(openaiTranscribeUrl(opts.baseUrl), {
|
|
491389
491470
|
method: "POST",
|
|
491390
|
-
headers
|
|
491471
|
+
headers,
|
|
491391
491472
|
body: form,
|
|
491392
491473
|
signal: AbortSignal.timeout(timeoutMs)
|
|
491393
491474
|
});
|
|
491394
491475
|
if (!res.ok) {
|
|
491395
491476
|
const detail = await res.text().catch(() => "");
|
|
491396
|
-
throw new Error(`
|
|
491477
|
+
throw new Error(`asr HTTP ${res.status}${detail ? `: ${detail.slice(0, 200)}` : ""}`);
|
|
491397
491478
|
}
|
|
491398
491479
|
const j = await res.json();
|
|
491399
491480
|
return (j.text ?? "").trim();
|
|
491400
491481
|
}
|
|
491482
|
+
async function groqTranscribe(key, wav, language, timeoutMs = 3e4) {
|
|
491483
|
+
return openaiTranscribe(
|
|
491484
|
+
{ baseUrl: "https://api.groq.com/openai/v1", apiKey: key, model: GROQ_TRANSCRIBE_MODEL, wav, language },
|
|
491485
|
+
timeoutMs
|
|
491486
|
+
);
|
|
491487
|
+
}
|
|
491401
491488
|
async function azureSttTranscribe(key, region, wav, language, timeoutMs = 3e4) {
|
|
491402
491489
|
const res = await fetch(azureSttUrl(region, language), {
|
|
491403
491490
|
method: "POST",
|
|
@@ -491421,8 +491508,13 @@ async function azureSttTranscribe(key, region, wav, language, timeoutMs = 3e4) {
|
|
|
491421
491508
|
}
|
|
491422
491509
|
|
|
491423
491510
|
// src/main/services/speech/asr-service.ts
|
|
491424
|
-
function pickLocalWhisperEntry(dataDir) {
|
|
491425
|
-
|
|
491511
|
+
function pickLocalWhisperEntry(dataDir, settings2) {
|
|
491512
|
+
let order = ["asr-whisper-turbo", "asr-whisper-small"];
|
|
491513
|
+
const preferred = settings2?.asr_local_model;
|
|
491514
|
+
if (preferred === "asr-whisper-turbo" || preferred === "asr-whisper-small") {
|
|
491515
|
+
order = [preferred, ...order.filter((id) => id !== preferred)];
|
|
491516
|
+
}
|
|
491517
|
+
for (const id of order) {
|
|
491426
491518
|
const entry = SPEECH_MODELS_MANIFEST.models.find((m) => m.id === id);
|
|
491427
491519
|
if (entry && readSpeechModelStatus(dataDir, entry).state === "ready")
|
|
491428
491520
|
return entry;
|
|
@@ -491450,8 +491542,24 @@ function normalizeChineseScript(text3, locale) {
|
|
|
491450
491542
|
return text3;
|
|
491451
491543
|
}
|
|
491452
491544
|
}
|
|
491453
|
-
async function transcribeAudio(dataDir, settings2, wavBytes, locale) {
|
|
491545
|
+
async function transcribeAudio(dataDir, settings2, wavBytes, locale, custom3) {
|
|
491454
491546
|
const cfg = resolveAsrTier(settings2);
|
|
491547
|
+
if (cfg.engine === "custom") {
|
|
491548
|
+
if (!custom3)
|
|
491549
|
+
return { ok: false, reason: "custom-provider-missing" };
|
|
491550
|
+
try {
|
|
491551
|
+
const text3 = await openaiTranscribe({
|
|
491552
|
+
baseUrl: custom3.baseUrl,
|
|
491553
|
+
apiKey: custom3.apiKey,
|
|
491554
|
+
model: custom3.model,
|
|
491555
|
+
wav: wavBytes,
|
|
491556
|
+
language: localeToBcp47(locale)
|
|
491557
|
+
});
|
|
491558
|
+
return { ok: true, text: normalizeChineseScript(text3, locale) };
|
|
491559
|
+
} catch (e) {
|
|
491560
|
+
return { ok: false, reason: "asr-failed", detail: e instanceof Error ? e.message : String(e) };
|
|
491561
|
+
}
|
|
491562
|
+
}
|
|
491455
491563
|
if (cfg.engine === "groq" || cfg.engine === "azure") {
|
|
491456
491564
|
const missing = asrCloudMissing(cfg);
|
|
491457
491565
|
if (missing === "groq-key")
|
|
@@ -491470,7 +491578,7 @@ async function transcribeAudio(dataDir, settings2, wavBytes, locale) {
|
|
|
491470
491578
|
}
|
|
491471
491579
|
if (!isSpeechEngineLoadable())
|
|
491472
491580
|
return { ok: false, reason: "engine-unavailable" };
|
|
491473
|
-
const entry = pickLocalWhisperEntry(dataDir);
|
|
491581
|
+
const entry = pickLocalWhisperEntry(dataDir, settings2);
|
|
491474
491582
|
if (!entry)
|
|
491475
491583
|
return { ok: false, reason: "model-missing" };
|
|
491476
491584
|
return runLocal(async () => {
|
|
@@ -491500,6 +491608,9 @@ async function transcribeAudio(dataDir, settings2, wavBytes, locale) {
|
|
|
491500
491608
|
});
|
|
491501
491609
|
}
|
|
491502
491610
|
|
|
491611
|
+
// src/main/ipc/index.ts
|
|
491612
|
+
init_custom_provider_service();
|
|
491613
|
+
|
|
491503
491614
|
// src/main/services/progress-service.ts
|
|
491504
491615
|
init_drizzle_orm();
|
|
491505
491616
|
init_schema();
|
|
@@ -496071,7 +496182,12 @@ function registerSpeechHandlers(deps) {
|
|
|
496071
496182
|
});
|
|
496072
496183
|
handle("speech:ttsSpeak", async (_e, text3, messageId) => {
|
|
496073
496184
|
const settings2 = readSettingsMap(getDb());
|
|
496074
|
-
|
|
496185
|
+
let custom3 = null;
|
|
496186
|
+
if (settings2.tts_engine?.startsWith("custom-")) {
|
|
496187
|
+
const row = getCustomProviderRaw(getDb(), settings2.tts_engine);
|
|
496188
|
+
custom3 = row ? { baseUrl: row.baseUrl, apiKey: row.apiKey, model: row.defaultModel, voice: settings2.tts_custom_voice?.trim() || null } : null;
|
|
496189
|
+
}
|
|
496190
|
+
const result = await speakMessage(emit2, deps.dataDir, settings2, messageId, text3, { custom: custom3 });
|
|
496075
496191
|
if (result.ok && result.engine === "edge" && settings2.tts_edge_disclosed !== "1") {
|
|
496076
496192
|
const db = getDb();
|
|
496077
496193
|
db.insert(settings).values({ key: "tts_edge_disclosed", value: "1", isSecret: false }).onConflictDoUpdate({ target: settings.key, set: { value: "1" } }).run();
|
|
@@ -496085,7 +496201,62 @@ function registerSpeechHandlers(deps) {
|
|
|
496085
496201
|
});
|
|
496086
496202
|
handle("speech:asrTranscribe", async (_e, wavBytes, locale) => {
|
|
496087
496203
|
const settings2 = readSettingsMap(getDb());
|
|
496088
|
-
|
|
496204
|
+
let custom3 = null;
|
|
496205
|
+
if (settings2.asr_engine?.startsWith("custom-")) {
|
|
496206
|
+
const row = getCustomProviderRaw(getDb(), settings2.asr_engine);
|
|
496207
|
+
custom3 = row ? { baseUrl: row.baseUrl, apiKey: row.apiKey, model: row.defaultModel } : null;
|
|
496208
|
+
}
|
|
496209
|
+
return transcribeAudio(deps.dataDir, settings2, wavBytes, locale, custom3);
|
|
496210
|
+
});
|
|
496211
|
+
handle("speech:testCustomTts", async (_e, input) => {
|
|
496212
|
+
let baseUrl = input.baseUrl ?? "";
|
|
496213
|
+
let apiKey = input.apiKey ?? null;
|
|
496214
|
+
let model = input.model ?? "";
|
|
496215
|
+
let voice = input.voice ?? null;
|
|
496216
|
+
if (input.providerId) {
|
|
496217
|
+
const row = getCustomProviderRaw(getDb(), input.providerId);
|
|
496218
|
+
if (!row)
|
|
496219
|
+
return { ok: false, detail: "provider \u4E0D\u5B58\u5728" };
|
|
496220
|
+
baseUrl = row.baseUrl;
|
|
496221
|
+
apiKey = row.apiKey;
|
|
496222
|
+
model = row.defaultModel;
|
|
496223
|
+
voice = readSettingsMap(getDb()).tts_custom_voice?.trim() || null;
|
|
496224
|
+
}
|
|
496225
|
+
if (!baseUrl || !model)
|
|
496226
|
+
return { ok: false, detail: "\u7F3A baseUrl/\u6A21\u578B" };
|
|
496227
|
+
try {
|
|
496228
|
+
const buf = await synthesizeOpenaiTts({ baseUrl, apiKey, model, voice, text: "\u4F60\u597D" });
|
|
496229
|
+
return { ok: buf.length > 0, detail: buf.length > 0 ? `${buf.length}B` : "empty" };
|
|
496230
|
+
} catch (e) {
|
|
496231
|
+
return { ok: false, detail: e instanceof Error ? e.message : String(e) };
|
|
496232
|
+
}
|
|
496233
|
+
});
|
|
496234
|
+
handle("speech:testCustomAsr", async (_e, input) => {
|
|
496235
|
+
let baseUrl = input.baseUrl ?? "";
|
|
496236
|
+
let apiKey = input.apiKey ?? null;
|
|
496237
|
+
if (input.providerId) {
|
|
496238
|
+
const row = getCustomProviderRaw(getDb(), input.providerId);
|
|
496239
|
+
if (!row)
|
|
496240
|
+
return { ok: false, detail: "provider \u4E0D\u5B58\u5728" };
|
|
496241
|
+
baseUrl = row.baseUrl;
|
|
496242
|
+
apiKey = row.apiKey;
|
|
496243
|
+
}
|
|
496244
|
+
if (!baseUrl)
|
|
496245
|
+
return { ok: false, detail: "\u7F3A baseUrl" };
|
|
496246
|
+
const url2 = `${baseUrl.replace(/\/+$/, "")}/models`;
|
|
496247
|
+
const headers = {};
|
|
496248
|
+
if (apiKey)
|
|
496249
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
496250
|
+
try {
|
|
496251
|
+
const res = await fetch(url2, { headers, signal: AbortSignal.timeout(15e3) });
|
|
496252
|
+
if (res.ok)
|
|
496253
|
+
return { ok: true, detail: String(res.status) };
|
|
496254
|
+
if (res.status === 401 || res.status === 403)
|
|
496255
|
+
return { ok: false, detail: `HTTP ${res.status}(\u5BC6\u94A5\u65E0\u6548)` };
|
|
496256
|
+
return { ok: false, detail: `HTTP ${res.status}(\u7AEF\u70B9\u53EF\u8FBE,\u672A\u63D0\u4F9B\u6A21\u578B\u5217\u8868;\u4FDD\u5B58\u540E\u5B9E\u9645\u8BF4\u4E00\u53E5\u9A8C\u8BC1)` };
|
|
496257
|
+
} catch (e) {
|
|
496258
|
+
return { ok: false, detail: e instanceof Error ? e.message : String(e) };
|
|
496259
|
+
}
|
|
496089
496260
|
});
|
|
496090
496261
|
}
|
|
496091
496262
|
function registerAllHandlers(deps) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-sn207b1U.js";import{p as f}from"./chunk-JWPE2WC7-V2anUJlE.js";import{_ as n,l as o}from"./mermaid.core-ChTdgwuo.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CG6JQnIq.js";import"./index-DjwNXkwE.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var f=Object.defineProperty;var P=(s,e,t)=>e in s?f(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var g=(s,e,t)=>P(s,typeof e!="symbol"?e+"":e,t);const S={listCourses:"course:list",getCourseTree:"course:getTree",importCourseFromRepo:"course:importFromRepo",detectLanguages:"course:detectLanguages",getCourseLanguages:"course:getLanguages",setCourseLanguage:"course:setLanguage",getCourseLanguage:"course:getLanguage",analyzeRepo:"course:analyzeRepo",importAnalyzed:"course:importAnalyzed",importLocalFolder:"import:localFolder",importGithub:"import:github",importCancel:"import:cancel",importResume:"import:resume",importPack:"import:importPack",exportPack:"import:exportPack",generateCourseFromMarkdown:"course:generateFromMarkdown",deleteCourse:"course:delete",restructureCourse:"course:restructure",generateSummaries:"course:generateSummaries",getStarterPrompts:"course:getStarterPrompts",getNodeContent:"course:getNodeContent",getNodeSummary:"course:getNodeSummary",getPracticeForLesson:"course:getPracticeForLesson",getLessonForPractice:"course:getLessonForPractice",listAssetsByNode:"asset:listByNode",listAssetsByCourse:"asset:listByCourse",getAssetDataUrl:"asset:getDataUrl",getProgress:"progress:get",updateProgress:"progress:update",markNodeAttempted:"progress:markAttempted",generateExercise:"exercise:generate",listExercises:"exercise:list",submitExerciseAnswer:"exercise:submit",examPrepare:"exam:prepare",examRegenerate:"exam:regenerate",examGetStatus:"exam:getStatus",examStartAttempt:"exam:startAttempt",examRecordAnswer:"exam:recordAnswer",examSubmitAttempt:"exam:submitAttempt",getDueReviews:"srs:getDue",getAllSrsItems:"srs:getAll",recordReview:"srs:record",getStreak:"streak:get",touchStreakToday:"streak:touchToday",listSouls:"soul:list",getSoul:"soul:get",createSoul:"soul:create",setActiveSoul:"soul:setActive",getActiveSoul:"soul:getActive",agentChat:"agent:chat",abortAgentChat:"agent:abort",getChatHistory:"agent:getHistory",clearChatHistory:"agent:clearHistory",agentChatThread:"agent:chatThread",abortAgentChatThread:"agent:abortThread",getContextUsage:"agent:getContextUsage",getSpeechModelStatus:"speech:getModelStatus",ensureSpeechModel:"speech:ensureModel",deleteSpeechModel:"speech:deleteModel",ttsSpeak:"speech:ttsSpeak",ttsStop:"speech:ttsStop",asrTranscribe:"speech:asrTranscribe",testCustomTts:"speech:testCustomTts",testCustomAsr:"speech:testCustomAsr",getAttachmentDataUrl:"attachment:getDataUrl",isAgentReady:"agent:isReady",getProviderPresets:"agent:getProviderPresets",testLlmConnection:"agent:testConnection",testCustomProvider:"agent:testCustomProvider",discoverModels:"agent:discoverModels",discoverProviderModels:"agent:discoverProviderModels",listCustomProviders:"customProvider:list",createCustomProvider:"customProvider:create",updateCustomProvider:"customProvider:update",deleteCustomProvider:"customProvider:delete",listPendingProposals:"proposal:listPending",applyProposal:"proposal:apply",rejectProposal:"proposal:reject",recordQuizAnswer:"quiz:recordAnswer",logFriction:"friction:log",getDashboard:"dashboard:get",searchContent:"search:content",updateMemory:"memory:update",getMemory:"memory:get",consolidateMemory:"consolidate:run",getSetting:"settings:get",setSetting:"settings:set",getXpStatus:"xp:getStatus",exportCourse:"course:export",canvasList:"canvas:list",canvasSave:"canvas:save",canvasDelete:"canvas:delete",canvasTogglePin:"canvas:togglePin",canvasSaveUserNote:"canvas:saveUserNote",canvasRecordQuizResult:"canvas:recordQuizResult",canvasUpdateUserNoteComment:"canvas:updateUserNoteComment",threadList:"thread:list",threadCreate:"thread:create",threadUpdate:"thread:update",threadDelete:"thread:delete",threadGetMessages:"thread:getMessages",threadFindRecentByNode:"thread:findRecentByNode"},h=1,m="ls-serve-token";function y(){const s=new URLSearchParams(window.location.search).get("token");if(s){try{window.localStorage.setItem(m,s)}catch{}return s}try{return window.localStorage.getItem(m)??""}catch{return""}}class p{constructor(){g(this,"ws",null);g(this,"everOpened",!1);g(this,"pending",new Map);g(this,"listeners",new Map);g(this,"connectPromise",null)}connect(){return this.ws&&this.ws.readyState===WebSocket.OPEN?Promise.resolve(this.ws):this.connectPromise?this.connectPromise:(this.connectPromise=new Promise((e,t)=>{const c=window.location.protocol==="https:"?"wss:":"ws:",o=y(),a=`${c}//${window.location.host}/?token=${encodeURIComponent(o)}`;let i=!1;const d=new WebSocket(a);this.ws=d;const l=window.setTimeout(()=>{i||(i=!0,this.connectPromise=null,t(new Error("连接学习服务超时(serve 未启动?)")))},5e3);d.addEventListener("open",()=>{this.everOpened=!0,window.clearTimeout(l),i||(i=!0,this.connectPromise=null,e(d))}),d.addEventListener("message",u=>{let r=null;try{r=JSON.parse(String(u.data))}catch{return}if(!(!r||r.v!==h)){if(r.type==="res"){const n=this.pending.get(r.id);if(!n)return;this.pending.delete(r.id),window.clearTimeout(n.timer),r.ok?n.resolve(r.result):n.reject(new Error(r.error))}else if(r.type==="event"){const n=this.listeners.get(r.channel);if(n)for(const v of n)v(...r.args)}}}),d.addEventListener("close",u=>{if(this.connectPromise=null,this.ws===d&&(this.ws=null),!i){i=!0,window.clearTimeout(l),t(new Error(A(u)));return}const r=u.code===4001?"学习服务拒绝了连接(token 无效)——请用启动时打印的带 token 链接打开":"与服务端的连接已断开";for(const[,n]of this.pending)window.clearTimeout(n.timer),n.reject(new Error(r));if(u.code===4001&&(this.everOpened=!1),this.pending.clear(),u.code===4001){window.dispatchEvent(new CustomEvent(C));return}this.everOpened&&window.setTimeout(()=>void this.connect().catch(()=>{}),2e3)}),d.addEventListener("error",()=>{})}),this.connectPromise)}static bytesToB64(e){const t=new Uint8Array(e);let c="";const o=32768;for(let a=0;a<t.length;a+=o)c+=String.fromCharCode(...t.subarray(a,a+o));return btoa(c)}async invoke(e,...t){var a,i;const c=await this.connect(),o=((i=(a=globalThis.crypto)==null?void 0:a.randomUUID)==null?void 0:i.call(a))??String(Math.random()).slice(2);return new Promise((d,l)=>{const u=window.setTimeout(()=>{this.pending.delete(o),l(new Error(`调用 ${e} 超时`))},3e5);this.pending.set(o,{resolve:d,reject:l,timer:u});const r=e==="speech:asrTranscribe"&&t[0]instanceof ArrayBuffer?[p.bytesToB64(t[0]),...t.slice(1)]:t;c.send(JSON.stringify({v:h,type:"req",id:o,channel:e,args:r}))})}on(e,t){const c=e==="speech:ttsAudio"?(...a)=>{const i=a[0];if(i&&typeof i.wavBase64=="string"){const d=atob(i.wavBase64),l=new Uint8Array(d.length);for(let n=0;n<d.length;n++)l[n]=d.charCodeAt(n);const{wavBase64:u,...r}=i;t({...r,wavBytes:l.buffer})}else t(...a)}:t;let o=this.listeners.get(e);return o||(o=new Set,this.listeners.set(e,o)),o.add(c),this.connect().catch(()=>{}),()=>{o.delete(c)}}}function b(){if(typeof window>"u")return!1;if(new URLSearchParams(window.location.search).get("token"))return!0;try{return!!window.localStorage.getItem(m)}catch{return!1}}function k(s){try{window.localStorage.setItem(m,s)}catch{}const e=new URL(window.location.href);e.searchParams.set("token",s),window.location.replace(e.toString())}const C="ls-web-token-rejected";function A(s){return s.code===4001?"学习服务拒绝了连接(token 无效)——请用启动时打印的带 token 链接打开":"无法连接学习服务(serve 未启动?)"}let w=!1;async function x(){if(w||window.api)return;w=!0;const s=new p,e={};for(const[t,c]of Object.entries(S))e[t]=(...o)=>s.invoke(c,...o);e.on=(t,c)=>s.on(t,c),window.api=e}function E(){return new p}export{C as TOKEN_REJECTED_EVENT,E as createWebTransportForTest,b as hasWebToken,x as installWebApi,k as setWebToken};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-
|
|
1
|
+
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-ChTdgwuo.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|