From 455059eebe8a3df018c7eda925ab49179b4a46d7 Mon Sep 17 00:00:00 2001 From: yhy Date: Sun, 30 Aug 2026 08:46:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3windows=E4=B8=8B=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E4=BF=9D=E5=AD=98=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addon/content/preferences.xhtml | 17 ++++ addon/locale/en-US/addon.ftl | 1 + addon/locale/en-US/preferences.ftl | 2 + addon/locale/zh-CN/addon.ftl | 1 + addon/locale/zh-CN/preferences.ftl | 2 + addon/prefs.js | 1 + .../lecture/infrastructure/platform/paths.ts | 37 ++++++++ .../infrastructure/storage/lectureStore.ts | 14 +-- .../lecture/infrastructure/storage/paths.ts | 93 ++++++++++++++----- src/modules/ui/lecturePrefs.ts | 7 ++ typings/i10n.d.ts | 3 + typings/prefs.d.ts | 1 + 12 files changed, 147 insertions(+), 32 deletions(-) diff --git a/addon/content/preferences.xhtml b/addon/content/preferences.xhtml index 5e3f456..4443a0c 100644 --- a/addon/content/preferences.xhtml +++ b/addon/content/preferences.xhtml @@ -177,6 +177,23 @@ preference="mineruPath" > + + + + + + diff --git a/addon/locale/en-US/addon.ftl b/addon/locale/en-US/addon.ftl index d3f3d7c..29590db 100644 --- a/addon/locale/en-US/addon.ftl +++ b/addon/locale/en-US/addon.ftl @@ -135,3 +135,4 @@ lecture-tts-provider = TTS: { $provider } lecture-tts-test = Test voice lecture-tts-testing = Synthesizing test speech… lecture-tts-test-done = Speech ready — playing +lecture-storage-write-failed = Cannot write voice lecture data to { $path }. { $detail } Set “Data directory” under Edit → Settings → ChatPapers → Voice lecture (must be writable). diff --git a/addon/locale/en-US/preferences.ftl b/addon/locale/en-US/preferences.ftl index 4586908..25576fc 100644 --- a/addon/locale/en-US/preferences.ftl +++ b/addon/locale/en-US/preferences.ftl @@ -34,4 +34,6 @@ prefs-tts-voice = Voice (optional) prefs-piper-path = Piper executable prefs-piper-model = Piper model path (.onnx) prefs-mineru-path = MinerU path +prefs-lecture-data-dir = Data directory +prefs-lecture-data-dir-hint = Leave empty to use chatpapers/lecture under your Zotero profile. If writes fail, set a writable absolute path, e.g. D:\ChatPapers\lecture-data prefs-tts-hint-empty = Choose a TTS engine. Local engines are fully offline — no API key needed. diff --git a/addon/locale/zh-CN/addon.ftl b/addon/locale/zh-CN/addon.ftl index d7ebf13..a2ef57d 100644 --- a/addon/locale/zh-CN/addon.ftl +++ b/addon/locale/zh-CN/addon.ftl @@ -135,3 +135,4 @@ lecture-tts-provider = TTS 引擎:{ $provider } lecture-tts-test = 试听语音 lecture-tts-testing = 正在合成测试语音… lecture-tts-test-done = 语音合成完成,正在播放 +lecture-storage-write-failed = 无法写入语音伴读数据目录:{ $path }。{ $detail } 请在 编辑 → 设置 → ChatPapers → 语音伴读 中配置「数据存储目录」(需有写入权限)。 diff --git a/addon/locale/zh-CN/preferences.ftl b/addon/locale/zh-CN/preferences.ftl index 8d950a3..1603f19 100644 --- a/addon/locale/zh-CN/preferences.ftl +++ b/addon/locale/zh-CN/preferences.ftl @@ -34,4 +34,6 @@ prefs-tts-voice = 音色(可选) prefs-piper-path = Piper 可执行文件 prefs-piper-model = Piper 模型路径 (.onnx) prefs-mineru-path = MinerU 路径 +prefs-lecture-data-dir = 数据存储目录 +prefs-lecture-data-dir-hint = 留空则使用 Zotero 配置文件夹下的 chatpapers/lecture。若写入失败,请填写有写入权限的绝对路径,例如 D:\ChatPapers\lecture-data prefs-tts-hint-empty = 请选择 TTS 引擎。本地引擎完全离线,无需 API Key。 diff --git a/addon/prefs.js b/addon/prefs.js index d9540e9..7fa47c5 100644 --- a/addon/prefs.js +++ b/addon/prefs.js @@ -20,5 +20,6 @@ pref("lectureTeacherStyle", "balanced"); pref("mineruPath", ""); pref("piperPath", ""); pref("piperModelPath", ""); +pref("lectureDataDir", ""); pref("chatPaneAdaptiveHeight", true); pref("lecturePaneAdaptiveHeight", true); diff --git a/src/modules/lecture/infrastructure/platform/paths.ts b/src/modules/lecture/infrastructure/platform/paths.ts index f4f40ca..c3a167e 100644 --- a/src/modules/lecture/infrastructure/platform/paths.ts +++ b/src/modules/lecture/infrastructure/platform/paths.ts @@ -22,6 +22,43 @@ export function joinPath(...parts: string[]): string { return PathUtils.join(...parts.filter(Boolean)); } +/** Normalize a filesystem path for the current OS. */ +export function normalizeStoragePath(input: string): string { + const trimmed = (input || "").trim(); + if (!trimmed) return ""; + const joined = trimmed.replace(/[\\/]+/g, "/"); + if (/^[A-Za-z]:\//.test(joined)) { + const drive = joined.slice(0, 2); + const rest = joined + .slice(2) + .split("/") + .filter((part) => part && part !== ".") + .join("\\"); + return `${drive}\\${rest}`.replace(/\\+$/, ""); + } + return joined + .split("/") + .filter((part) => part && part !== ".") + .join("/") + .replace(/\/+$/, ""); +} + +/** Safe single path segment for file or directory names (cross-platform). */ +export function sanitizePathSegment(input: string, maxLen = 120): string { + const cleaned = String(input || "") + .replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") + .replace(/[.\s]+$/, "") + .trim(); + if (!cleaned) return "_"; + if (cleaned.length <= maxLen) return cleaned; + let hash = 0; + for (let i = 0; i < input.length; i++) { + hash = (hash * 31 + input.charCodeAt(i)) | 0; + } + const tail = Math.abs(hash).toString(36); + return `${cleaned.slice(0, Math.max(1, maxLen - tail.length - 1))}_${tail}`; +} + /** Convert local file path to file:// URL for