From 50e98423f89dcb834d1b3c106186eeb7de5ada1d Mon Sep 17 00:00:00 2001 From: yamachu Date: Thu, 6 Apr 2023 19:40:45 +0900 Subject: [PATCH 01/75] =?UTF-8?q?browser=E7=89=88=E3=81=AE=E3=83=99?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AE=E3=82=B3?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/browser/background.ts | 1 + src/browser/preload.ts | 5 + src/browser/sandbox.ts | 189 ++++++++++++++++++++++++++++++++++++++ src/index.html | 1 + vite.config.ts | 18 +++- 6 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 src/browser/background.ts create mode 100644 src/browser/preload.ts create mode 100644 src/browser/sandbox.ts diff --git a/package.json b/package.json index 59096be755..5dae34ad12 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "scripts": { "serve": "cross-env VITE_IS_ELECTRON=false vite", + "serve:browser": "cross-env VITE_IS_ELECTRON=false VITE_IS_BROWSER=true vite", "build": "cross-env VITE_IS_ELECTRON=false vite build", "test:unit": "vitest --run", "test-watch:unit": "vitest --watch", diff --git a/src/browser/background.ts b/src/browser/background.ts new file mode 100644 index 0000000000..696e111432 --- /dev/null +++ b/src/browser/background.ts @@ -0,0 +1 @@ +console.log("Hello Worker!"); diff --git a/src/browser/preload.ts b/src/browser/preload.ts new file mode 100644 index 0000000000..f0743f43e0 --- /dev/null +++ b/src/browser/preload.ts @@ -0,0 +1,5 @@ +import { SandboxKey } from "../type/preload"; +import { api } from "./sandbox"; + +// @ts-expect-error readonlyになっているが、初期化処理はここで行うので問題ない +window[SandboxKey] = api; diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts new file mode 100644 index 0000000000..c0473be72e --- /dev/null +++ b/src/browser/sandbox.ts @@ -0,0 +1,189 @@ +import { IpcSOData } from "@/type/ipc"; +import { + ElectronStoreType, + EngineId, + EngineSetting, + HotkeySetting, + NativeThemeType, + SandboxKey, +} from "@/type/preload"; + +const worker = new Worker(new URL("./background.ts", import.meta.url)); + +export const api: typeof window[typeof SandboxKey] = { + getAppInfos() { + throw new Error("Not Implemented"); + }, + getHowToUseText() { + throw new Error("Not Implemented"); + }, + getPolicyText() { + throw new Error("Not Implemented"); + }, + getOssLicenses() { + throw new Error("Not Implemented"); + }, + getUpdateInfos() { + throw new Error("Not Implemented"); + }, + getOssCommunityInfos() { + throw new Error("Not Implemented"); + }, + getQAndAText() { + throw new Error("Not Implemented"); + }, + getContactText() { + throw new Error("Not Implemented"); + }, + getPrivacyPolicyText() { + throw new Error("Not Implemented"); + }, + getAltPortInfos() { + throw new Error("Not Implemented"); + }, + saveTempAudioFile(obj: { relativePath: string; buffer: ArrayBuffer }) { + throw new Error("Not Implemented"); + }, + loadTempFile() { + throw new Error("Not Implemented"); + }, + showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { + throw new Error("Not Implemented"); + }, + showTextSaveDialog(obj: { title: string; defaultPath?: string }) { + throw new Error("Not Implemented"); + }, + showVvppOpenDialog(obj: { title: string; defaultPath?: string }) { + throw new Error("Not Implemented"); + }, + showOpenDirectoryDialog(obj: { title: string }) { + throw new Error("Not Implemented"); + }, + showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { + throw new Error("Not Implemented"); + }, + showProjectLoadDialog(obj: { title: string }) { + throw new Error("Not Implemented"); + }, + showMessageDialog(obj: { + type: "none" | "info" | "error" | "question" | "warning"; + title: string; + message: string; + }) { + throw new Error("Not Implemented"); + }, + showQuestionDialog(obj: { + type: "none" | "info" | "error" | "question" | "warning"; + title: string; + message: string; + buttons: string[]; + cancelId?: number; + defaultId?: number; + }) { + throw new Error("Not Implemented"); + }, + showImportFileDialog(obj: { title: string }) { + throw new Error("Not Implemented"); + }, + writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { + throw new Error("Not Implemented"); + }, + readFile(obj: { filePath: string }) { + throw new Error("Not Implemented"); + }, + openTextEditContextMenu() { + throw new Error("Not Implemented"); + }, + isAvailableGPUMode() { + throw new Error("Not Implemented"); + }, + isMaximizedWindow() { + throw new Error("Not Implemented"); + }, + onReceivedIPCMsg( + channel: T, + listener: (_: unknown, ...args: IpcSOData[T]["args"]) => void + ) { + console.dir(`channel: ${channel}, listener: ${listener}`); + window.addEventListener("message", (event) => { + if (event.data.channel === channel) { + listener(event.data.args); + } + }); + }, + closeWindow() { + throw new Error("Not Implemented"); + }, + minimizeWindow() { + throw new Error("Not Implemented"); + }, + maximizeWindow() { + throw new Error("Not Implemented"); + }, + logError(...params: unknown[]) { + throw new Error("Not Implemented"); + }, + logWarn(...params: unknown[]) { + throw new Error("Not Implemented"); + }, + logInfo(...params: unknown[]) { + throw new Error("Not Implemented"); + }, + engineInfos() { + throw new Error("Not Implemented"); + }, + restartEngine(engineId: EngineId) { + throw new Error("Not Implemented"); + }, + openEngineDirectory(engineId: EngineId) { + throw new Error("Not Implemented"); + }, + hotkeySettings(newData?: HotkeySetting) { + throw new Error("Not Implemented"); + }, + checkFileExists(file: string) { + throw new Error("Not Implemented"); + }, + changePinWindow() { + throw new Error("Not Implemented"); + }, + getDefaultHotkeySettings() { + throw new Error("Not Implemented"); + }, + getDefaultToolbarSetting() { + throw new Error("Not Implemented"); + }, + setNativeTheme(source: NativeThemeType) { + throw new Error("Not Implemented"); + }, + theme(newData?: string) { + throw new Error("Not Implemented"); + }, + vuexReady() { + throw new Error("Not Implemented"); + }, + getSetting(key: Key) { + throw new Error("Not Implemented"); + }, + setSetting( + key: Key, + newValue: ElectronStoreType[Key] + ) { + throw new Error("Not Implemented"); + }, + setEngineSetting(engineId: EngineId, engineSetting: EngineSetting) { + throw new Error("Not Implemented"); + }, + installVvppEngine(path: string) { + throw new Error("Not Implemented"); + }, + uninstallVvppEngine(engineId: EngineId) { + throw new Error("Not Implemented"); + }, + validateEngineDir(engineDir: string) { + throw new Error("Not Implemented"); + }, + restartApp(obj: { isMultiEngineOffMode: boolean }) { + throw new Error("Not Implemented"); + }, +}; diff --git a/src/index.html b/src/index.html index 304eca4856..fc36562e03 100644 --- a/src/index.html +++ b/src/index.html @@ -17,6 +17,7 @@ import { Buffer } from "buffer"; window.Buffer = Buffer; + diff --git a/vite.config.ts b/vite.config.ts index 2123d34ac6..b745a9903d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,12 +8,13 @@ import tsconfigPaths from "vite-tsconfig-paths"; import vue from "@vitejs/plugin-vue"; import checker from "vite-plugin-checker"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import { BuildOptions, defineConfig } from "vite"; +import { BuildOptions, defineConfig, Plugin } from "vite"; import { quasar } from "@quasar/vite-plugin"; rmSync(path.resolve(__dirname, "dist"), { recursive: true, force: true }); const isElectron = process.env.VITE_IS_ELECTRON === "true"; +const isBrowser = process.env.VITE_IS_BROWSER === "true"; export default defineConfig((options) => { const shouldEmitSourcemap = ["development", "test"].includes(options.mode); @@ -94,6 +95,21 @@ export default defineConfig((options) => { }, }, }), + isBrowser && injectBrowserPreloadPlugin(), ], }; }); + +const injectBrowserPreloadPlugin = (): Plugin => { + return { + name: "inject-browser-preload", + transformIndexHtml: { + enforce: "pre" as const, + transform: (html: string) => + html.replace( + "", + `` + ), + }, + }; +}; From b8092b3396113f49ef67f832aa49102db498a75e Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 10 Jun 2023 10:06:47 +0900 Subject: [PATCH 02/75] =?UTF-8?q?Worker=E9=96=93=E3=81=AEmessaging?= =?UTF-8?q?=E3=81=AE=E5=9E=8B=E3=82=92=E5=AE=9A=E7=BE=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/type.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/browser/type.ts diff --git a/src/browser/type.ts b/src/browser/type.ts new file mode 100644 index 0000000000..59313624d5 --- /dev/null +++ b/src/browser/type.ts @@ -0,0 +1,17 @@ +import { IpcIHData } from "@/type/ipc"; + +export type WorkerToMainMessage = { + [K in keyof IpcIHData]: { + type: K; + return: IpcIHData[K]["return"]; + eventId: string; + }; +}[keyof IpcIHData]; + +export type MainToWorkerMessage = { + [K in keyof IpcIHData]: { + type: K; + args: IpcIHData[K]["args"]; + eventId: string; + }; +}[keyof IpcIHData]; From 991f26d9a8ee8c78f4677c406837adea184a8c12 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 10 Jun 2023 10:12:08 +0900 Subject: [PATCH 03/75] =?UTF-8?q?=E4=B8=80=E6=97=A6electron=E3=81=A8?= =?UTF-8?q?=E5=90=8C=E6=A7=98=E3=81=AE=E5=AE=9F=E8=A3=85=E3=81=A7sandbox?= =?UTF-8?q?=E3=81=AEinvoke=E9=83=A8=E5=88=86=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 166 +++++++++++++++++++++++++++-------------- 1 file changed, 109 insertions(+), 57 deletions(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index c0473be72e..671e79861d 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -1,4 +1,6 @@ -import { IpcSOData } from "@/type/ipc"; +import { v4 } from "uuid"; +import type { WorkerToMainMessage } from "./type"; +import { IpcIHData, IpcSOData } from "@/type/ipc"; import { ElectronStoreType, EngineId, @@ -10,67 +12,112 @@ import { const worker = new Worker(new URL("./background.ts", import.meta.url)); +const invoker = ( + type: K, + args: IpcIHData[K]["args"] +): Promise => { + return new Promise((resolve) => { + const eventId = v4(); + const cb = (ev: MessageEvent) => { + if (ev.data.type === type && ev.data.eventId === eventId) { + worker.removeEventListener("message", cb); + resolve(ev.data.return); + } + }; + worker.addEventListener("message", cb); // 他のeventが届いた時にresolveしない様に、onceは使用していない + worker.postMessage({ type, args, eventId } /** MainToWorkerMessage */); + }); +}; + +let tempDir: string; + export const api: typeof window[typeof SandboxKey] = { getAppInfos() { - throw new Error("Not Implemented"); + return invoker("GET_APP_INFOS", []); }, getHowToUseText() { - throw new Error("Not Implemented"); + return invoker("GET_HOW_TO_USE_TEXT", []); }, getPolicyText() { - throw new Error("Not Implemented"); + return invoker("GET_POLICY_TEXT", []); }, getOssLicenses() { - throw new Error("Not Implemented"); + return invoker("GET_OSS_LICENSES", []); }, getUpdateInfos() { - throw new Error("Not Implemented"); + return invoker("GET_UPDATE_INFOS", []); }, getOssCommunityInfos() { - throw new Error("Not Implemented"); + return invoker("GET_OSS_COMMUNITY_INFOS", []); }, getQAndAText() { - throw new Error("Not Implemented"); + return invoker("GET_Q_AND_A_TEXT", []); }, getContactText() { - throw new Error("Not Implemented"); + return invoker("GET_CONTACT_TEXT", []); }, getPrivacyPolicyText() { - throw new Error("Not Implemented"); + return invoker("GET_PRIVACY_POLICY_TEXT", []); }, getAltPortInfos() { - throw new Error("Not Implemented"); - }, - saveTempAudioFile(obj: { relativePath: string; buffer: ArrayBuffer }) { - throw new Error("Not Implemented"); - }, - loadTempFile() { - throw new Error("Not Implemented"); + return invoker("GET_ALT_PORT_INFOS", []); + }, + async saveTempAudioFile(obj: { relativePath: string; buffer: ArrayBuffer }) { + if (!tempDir) { + tempDir = await invoker("GET_TEMP_DIR", []); + } + const tempFilePath = await invoker("JOIN_PATH", [ + { + pathArray: [tempDir, obj.relativePath], + }, + ]); + await invoker("WRITE_FILE", [ + { + filePath: tempFilePath, + buffer: obj.buffer, + }, + ]); + }, + async loadTempFile() { + if (!tempDir) { + tempDir = await invoker("GET_TEMP_DIR", []); + } + const tempFilePath = await invoker("JOIN_PATH", [ + { + pathArray: [tempDir, "hoge.txt"], + }, + ]); + const buf = await invoker("READ_FILE", [ + { + filePath: tempFilePath, + }, + ]); + return new TextDecoder().decode(buf); }, showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { - throw new Error("Not Implemented"); + return invoker("SHOW_AUDIO_SAVE_DIALOG", [obj]); }, showTextSaveDialog(obj: { title: string; defaultPath?: string }) { - throw new Error("Not Implemented"); + return invoker("SHOW_TEXT_SAVE_DIALOG", [obj]); }, showVvppOpenDialog(obj: { title: string; defaultPath?: string }) { - throw new Error("Not Implemented"); + return invoker("SHOW_VVPP_OPEN_DIALOG", [obj]); }, showOpenDirectoryDialog(obj: { title: string }) { - throw new Error("Not Implemented"); + return invoker("SHOW_OPEN_DIRECTORY_DIALOG", [obj]); }, showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { - throw new Error("Not Implemented"); + return invoker("SHOW_PROJECT_SAVE_DIALOG", [obj]); }, showProjectLoadDialog(obj: { title: string }) { - throw new Error("Not Implemented"); + return invoker("SHOW_PROJECT_LOAD_DIALOG", [obj]); }, showMessageDialog(obj: { type: "none" | "info" | "error" | "question" | "warning"; title: string; message: string; }) { - throw new Error("Not Implemented"); + return invoker("SHOW_MESSAGE_DIALOG", [obj]); }, showQuestionDialog(obj: { type: "none" | "info" | "error" | "question" | "warning"; @@ -80,25 +127,25 @@ export const api: typeof window[typeof SandboxKey] = { cancelId?: number; defaultId?: number; }) { - throw new Error("Not Implemented"); + return invoker("SHOW_QUESTION_DIALOG", [obj]); }, showImportFileDialog(obj: { title: string }) { - throw new Error("Not Implemented"); + return invoker("SHOW_IMPORT_FILE_DIALOG", [obj]); }, writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { - throw new Error("Not Implemented"); + return invoker("WRITE_FILE", [obj]); }, readFile(obj: { filePath: string }) { - throw new Error("Not Implemented"); + return invoker("READ_FILE", [obj]); }, openTextEditContextMenu() { - throw new Error("Not Implemented"); + return invoker("OPEN_TEXT_EDIT_CONTEXT_MENU", []); }, isAvailableGPUMode() { - throw new Error("Not Implemented"); + return invoker("IS_AVAILABLE_GPU_MODE", []); }, isMaximizedWindow() { - throw new Error("Not Implemented"); + return invoker("IS_MAXIMIZED_WINDOW", []); }, onReceivedIPCMsg( channel: T, @@ -112,78 +159,83 @@ export const api: typeof window[typeof SandboxKey] = { }); }, closeWindow() { - throw new Error("Not Implemented"); + return invoker("CLOSE_WINDOW", []); }, minimizeWindow() { - throw new Error("Not Implemented"); + return invoker("MINIMIZE_WINDOW", []); }, maximizeWindow() { - throw new Error("Not Implemented"); + return invoker("MAXIMIZE_WINDOW", []); }, logError(...params: unknown[]) { - throw new Error("Not Implemented"); + console.error(...params); + return invoker("LOG_ERROR", [params]); }, logWarn(...params: unknown[]) { - throw new Error("Not Implemented"); + console.warn(...params); + return invoker("LOG_WARN", [params]); }, logInfo(...params: unknown[]) { - throw new Error("Not Implemented"); + console.info(...params); + return invoker("LOG_INFO", [params]); }, engineInfos() { - throw new Error("Not Implemented"); + return invoker("ENGINE_INFOS", []); }, restartEngine(engineId: EngineId) { - throw new Error("Not Implemented"); + return invoker("RESTART_ENGINE", [{ engineId }]); }, openEngineDirectory(engineId: EngineId) { - throw new Error("Not Implemented"); + return invoker("OPEN_ENGINE_DIRECTORY", [{ engineId }]); }, hotkeySettings(newData?: HotkeySetting) { - throw new Error("Not Implemented"); + return invoker("HOTKEY_SETTINGS", [{ newData }]); }, checkFileExists(file: string) { - throw new Error("Not Implemented"); + return invoker("CHECK_FILE_EXISTS", [{ file }]); }, changePinWindow() { - throw new Error("Not Implemented"); + return invoker("CHANGE_PIN_WINDOW", []); }, getDefaultHotkeySettings() { - throw new Error("Not Implemented"); + return invoker("GET_DEFAULT_HOTKEY_SETTINGS", []); }, getDefaultToolbarSetting() { - throw new Error("Not Implemented"); + return invoker("GET_DEFAULT_TOOLBAR_SETTING", []); }, setNativeTheme(source: NativeThemeType) { - throw new Error("Not Implemented"); + return invoker("SET_NATIVE_THEME", [source]); }, theme(newData?: string) { - throw new Error("Not Implemented"); + return invoker("THEME", [{ newData }]); }, vuexReady() { - throw new Error("Not Implemented"); + return invoker("ON_VUEX_READY", []); }, getSetting(key: Key) { - throw new Error("Not Implemented"); + return invoker("GET_SETTING", [key]) as Promise< + ElectronStoreType[typeof key] + >; }, setSetting( key: Key, newValue: ElectronStoreType[Key] ) { - throw new Error("Not Implemented"); + return invoker("SET_SETTING", [key, newValue]) as Promise; }, setEngineSetting(engineId: EngineId, engineSetting: EngineSetting) { - throw new Error("Not Implemented"); + return invoker("SET_ENGINE_SETTING", [engineId, engineSetting]); }, - installVvppEngine(path: string) { - throw new Error("Not Implemented"); + async installVvppEngine(path: string) { + return invoker("INSTALL_VVPP_ENGINE", [path]); }, - uninstallVvppEngine(engineId: EngineId) { - throw new Error("Not Implemented"); + async uninstallVvppEngine(engineId: EngineId) { + return invoker("UNINSTALL_VVPP_ENGINE", [engineId]); }, validateEngineDir(engineDir: string) { - throw new Error("Not Implemented"); + return invoker("VALIDATE_ENGINE_DIR", [{ engineDir }]); }, restartApp(obj: { isMultiEngineOffMode: boolean }) { - throw new Error("Not Implemented"); + return invoker("RESTART_APP", [obj]); }, }; From 13d92d05c0305bd633f763d067b4345239475c33 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 10 Jun 2023 10:56:38 +0900 Subject: [PATCH 04/75] =?UTF-8?q?module=E3=81=AEimport=E3=81=8C=E5=87=BA?= =?UTF-8?q?=E6=9D=A5=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F=E3=81=AE=E3=81=A7?= =?UTF-8?q?=E3=80=81Worker=E3=81=AE=E7=94=9F=E6=88=90=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 671e79861d..314c0afcbe 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -10,7 +10,9 @@ import { SandboxKey, } from "@/type/preload"; -const worker = new Worker(new URL("./background.ts", import.meta.url)); +const worker = new Worker(new URL("./background.ts", import.meta.url), { + type: "module", +}); const invoker = ( type: K, From e96870de53f368473fd96950ef2b549281f9711e Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 13 Jun 2023 00:46:29 +0900 Subject: [PATCH 05/75] =?UTF-8?q?temp=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=81=AF=E7=8F=BE=E5=9C=A8=E4=BD=BF=E3=81=A3=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=AA=E3=81=9D=E3=81=86=E3=81=AA=E3=81=AE=E3=81=A7=E3=80=81?= =?UTF-8?q?=E3=82=82=E3=81=A8=E3=82=82=E3=81=A8=E3=81=82=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=82=92=E5=89=8A=E9=99=A4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 314c0afcbe..a5d62c5cf5 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -31,8 +31,6 @@ const invoker = ( }); }; -let tempDir: string; - export const api: typeof window[typeof SandboxKey] = { getAppInfos() { return invoker("GET_APP_INFOS", []); @@ -65,36 +63,16 @@ export const api: typeof window[typeof SandboxKey] = { return invoker("GET_ALT_PORT_INFOS", []); }, async saveTempAudioFile(obj: { relativePath: string; buffer: ArrayBuffer }) { - if (!tempDir) { - tempDir = await invoker("GET_TEMP_DIR", []); - } - const tempFilePath = await invoker("JOIN_PATH", [ - { - pathArray: [tempDir, obj.relativePath], - }, - ]); - await invoker("WRITE_FILE", [ - { - filePath: tempFilePath, - buffer: obj.buffer, - }, - ]); + // DELETE_ME: もう使ってなさそう + throw new Error( + `not implemented: saveTempAudioFile is already obsoleted: ${JSON.stringify( + obj + )}` + ); }, async loadTempFile() { - if (!tempDir) { - tempDir = await invoker("GET_TEMP_DIR", []); - } - const tempFilePath = await invoker("JOIN_PATH", [ - { - pathArray: [tempDir, "hoge.txt"], - }, - ]); - const buf = await invoker("READ_FILE", [ - { - filePath: tempFilePath, - }, - ]); - return new TextDecoder().decode(buf); + // DELETE_ME: もう使ってなさそう + throw new Error(`not implemented: loadTempFile is already obsoleted`); }, showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { return invoker("SHOW_AUDIO_SAVE_DIALOG", [obj]); From 6b1c9044c79d2e9d82dcf17fcec9408292609687 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 13 Jun 2023 01:02:36 +0900 Subject: [PATCH 06/75] =?UTF-8?q?Browser=E3=81=A7Messaging=E3=82=92?= =?UTF-8?q?=E3=81=99=E3=82=8B=E7=AC=AC=E4=B8=80=E6=AD=A9=E3=81=BF=E3=81=9F?= =?UTF-8?q?=E3=81=84=E3=81=AA=E5=AE=9F=E8=A3=85=E3=82=92=E5=85=A5=E3=82=8C?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit viteで定数埋め込みにするかは要検討 --- src/browser/background.ts | 25 ++++++++++++++++++++++++- src/browser/backgroundImpl.ts | 17 +++++++++++++++++ vite.config.ts | 6 ++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/browser/backgroundImpl.ts diff --git a/src/browser/background.ts b/src/browser/background.ts index 696e111432..7dd7c55f16 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -1 +1,24 @@ -console.log("Hello Worker!"); +import { getAppInfosImpl } from "./backgroundImpl"; +import type { MainToWorkerMessage } from "./type"; +import type { IpcIHData } from "@/type/ipc"; + +type MessageReturnTypes = { [K in keyof IpcIHData]: IpcIHData[K]["return"] }; + +const typedPostMessage = ( + message: MessageReturnTypes[K] +) => { + postMessage(message); +}; + +onmessage = (e: MessageEvent) => { + switch (e.data.type) { + case "GET_APP_INFOS": + return getAppInfosImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + default: + console.dir(e.data); + postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); + break; + } +}; diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts new file mode 100644 index 0000000000..5e9d0e214a --- /dev/null +++ b/src/browser/backgroundImpl.ts @@ -0,0 +1,17 @@ +import type { IpcIHData } from "@/type/ipc"; + +type SandboxImpl = { + [K in keyof IpcIHData]: ( + args: IpcIHData[K]["args"] + ) => Promise; +}; + +export const getAppInfosImpl: SandboxImpl["GET_APP_INFOS"] = () => { + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + const appInfo = { + name: process.env.APP_NAME!, + version: process.env.APP_VERSION!, + }; + /* eslint-enable @typescript-eslint/no-non-null-assertion */ + return Promise.resolve(appInfo); +}; diff --git a/vite.config.ts b/vite.config.ts index b745a9903d..6f077edee7 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -97,6 +97,12 @@ export default defineConfig((options) => { }), isBrowser && injectBrowserPreloadPlugin(), ], + define: { + [`process.env`]: { + APP_NAME: JSON.stringify(process.env.npm_package_name), + APP_VERSION: JSON.stringify(process.env.npm_package_version), + }, + }, }; }); From bb81c5d2b3c46a9c90e8db229a8ef048010b7bb0 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 13 Jun 2023 01:07:48 +0900 Subject: [PATCH 07/75] =?UTF-8?q?resources=E3=82=92=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E3=81=99=E3=82=8B=E5=87=A6=E7=90=86=E3=81=AE=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 92 ++++++++++++++++++++++++++++++++++- src/browser/backgroundImpl.ts | 47 ++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index 7dd7c55f16..edde95282f 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -1,4 +1,14 @@ -import { getAppInfosImpl } from "./backgroundImpl"; +import { + getAppInfosImpl, + getContactTextImpl, + getHowToUseTextImpl, + getOssCommunityInfosImpl, + getOssLicensesImpl, + getPolicyTextImpl, + getPrivacyPolicyTextImpl, + getQAndATextImpl, + getUpdateInfosImpl, +} from "./backgroundImpl"; import type { MainToWorkerMessage } from "./type"; import type { IpcIHData } from "@/type/ipc"; @@ -16,7 +26,85 @@ onmessage = (e: MessageEvent) => { return getAppInfosImpl(e.data.args).then((v) => typedPostMessage(v) ); - default: + case "GET_TEMP_DIR": + console.dir(e.data); + postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); + break; + case "GET_HOW_TO_USE_TEXT": + return getHowToUseTextImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + case "GET_POLICY_TEXT": + return getPolicyTextImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + case "GET_OSS_LICENSES": + return getOssLicensesImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + case "GET_UPDATE_INFOS": + return getUpdateInfosImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + case "GET_OSS_COMMUNITY_INFOS": + return getOssCommunityInfosImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + case "GET_CONTACT_TEXT": + return getContactTextImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + case "GET_Q_AND_A_TEXT": + return getQAndATextImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + case "GET_PRIVACY_POLICY_TEXT": + return getPrivacyPolicyTextImpl(e.data.args).then((v) => + typedPostMessage(v) + ); + case "GET_ALT_PORT_INFOS": + case "SHOW_AUDIO_SAVE_DIALOG": + case "SHOW_TEXT_SAVE_DIALOG": + case "SHOW_VVPP_OPEN_DIALOG": + case "SHOW_OPEN_DIRECTORY_DIALOG": + case "SHOW_IMPORT_FILE_DIALOG": + case "SHOW_PROJECT_SAVE_DIALOG": + case "SHOW_PROJECT_LOAD_DIALOG": + case "SHOW_MESSAGE_DIALOG": + case "SHOW_QUESTION_DIALOG": + case "SHOW_WARNING_DIALOG": + case "SHOW_ERROR_DIALOG": + case "OPEN_TEXT_EDIT_CONTEXT_MENU": + case "IS_AVAILABLE_GPU_MODE": + case "IS_MAXIMIZED_WINDOW": + case "CLOSE_WINDOW": + case "MINIMIZE_WINDOW": + case "MAXIMIZE_WINDOW": + case "LOG_ERROR": + case "LOG_WARN": + case "LOG_INFO": + case "ENGINE_INFOS": + case "RESTART_ENGINE_ALL": + case "RESTART_ENGINE": + case "OPEN_ENGINE_DIRECTORY": + case "CHECK_FILE_EXISTS": + case "CHANGE_PIN_WINDOW": + case "HOTKEY_SETTINGS": + case "GET_DEFAULT_HOTKEY_SETTINGS": + case "GET_DEFAULT_TOOLBAR_SETTING": + case "THEME": + case "ON_VUEX_READY": + case "GET_SETTING": + case "SET_SETTING": + case "SET_ENGINE_SETTING": + case "SET_NATIVE_THEME": + case "INSTALL_VVPP_ENGINE": + case "UNINSTALL_VVPP_ENGINE": + case "VALIDATE_ENGINE_DIR": + case "RESTART_APP": + case "JOIN_PATH": + case "WRITE_FILE": + case "READ_FILE": console.dir(e.data); postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); break; diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 5e9d0e214a..67843f8398 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -1,4 +1,14 @@ import type { IpcIHData } from "@/type/ipc"; +import { + ContactTextFileName, + HowToUseTextFileName, + OssCommunityInfosFileName, + OssLicensesJsonFileName, + PolicyTextFileName, + PrivacyPolicyTextFileName, + QAndATextFileName, + UpdateInfosJsonFileName, +} from "@/type/staticResources"; type SandboxImpl = { [K in keyof IpcIHData]: ( @@ -15,3 +25,40 @@ export const getAppInfosImpl: SandboxImpl["GET_APP_INFOS"] = () => { /* eslint-enable @typescript-eslint/no-non-null-assertion */ return Promise.resolve(appInfo); }; + +// TODO: base pathを設定できるようにするか、ビルド時埋め込みにする +const toStaticPath = (fileName: string) => `/${fileName}`; + +export const getHowToUseTextImpl: SandboxImpl["GET_HOW_TO_USE_TEXT"] = () => { + return fetch(toStaticPath(HowToUseTextFileName)).then((v) => v.text()); +}; + +export const getPolicyTextImpl: SandboxImpl["GET_POLICY_TEXT"] = () => { + return fetch(toStaticPath(PolicyTextFileName)).then((v) => v.text()); +}; + +export const getOssCommunityInfosImpl: SandboxImpl["GET_OSS_COMMUNITY_INFOS"] = + () => { + return fetch(toStaticPath(OssCommunityInfosFileName)).then((v) => v.text()); + }; + +export const getContactTextImpl: SandboxImpl["GET_CONTACT_TEXT"] = () => { + return fetch(toStaticPath(ContactTextFileName)).then((v) => v.text()); +}; + +export const getQAndATextImpl: SandboxImpl["GET_Q_AND_A_TEXT"] = () => { + return fetch(toStaticPath(QAndATextFileName)).then((v) => v.text()); +}; + +export const getPrivacyPolicyTextImpl: SandboxImpl["GET_PRIVACY_POLICY_TEXT"] = + () => { + return fetch(toStaticPath(PrivacyPolicyTextFileName)).then((v) => v.text()); + }; + +export const getOssLicensesImpl: SandboxImpl["GET_OSS_LICENSES"] = () => { + return fetch(toStaticPath(OssLicensesJsonFileName)).then((v) => v.json()); +}; + +export const getUpdateInfosImpl: SandboxImpl["GET_UPDATE_INFOS"] = () => { + return fetch(toStaticPath(UpdateInfosJsonFileName)).then((v) => v.json()); +}; From f21588998143814674cfa23c510132df3e62f501 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 13 Jun 2023 01:08:58 +0900 Subject: [PATCH 08/75] =?UTF-8?q?=E5=91=BC=E3=81=B0=E3=82=8C=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=82=E3=81=AE=E3=81=AFconsole.error=E5=90=90?= =?UTF-8?q?=E3=81=8D=E5=87=BA=E3=81=97=E3=81=A6=E3=81=8A=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index edde95282f..f7f076f5f8 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -27,7 +27,7 @@ onmessage = (e: MessageEvent) => { typedPostMessage(v) ); case "GET_TEMP_DIR": - console.dir(e.data); + console.error("Not Implemented, it should not be called from VOICEVOX"); postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); break; case "GET_HOW_TO_USE_TEXT": From b2807686a5d854a72a2f16aebd1810c6a69e2d7b Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 13 Jun 2023 01:14:40 +0900 Subject: [PATCH 09/75] =?UTF-8?q?=E5=9B=BA=E5=AE=9A=E5=80=A4=E3=82=92?= =?UTF-8?q?=E5=90=AB=E3=82=80Theme=E3=81=AE=E5=87=A6=E7=90=86=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 設定の保持を実装したら、それと組み合わせる --- src/browser/background.ts | 8 ++++++++ src/browser/backgroundImpl.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/browser/background.ts b/src/browser/background.ts index f7f076f5f8..06e29403b6 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -8,6 +8,7 @@ import { getPrivacyPolicyTextImpl, getQAndATextImpl, getUpdateInfosImpl, + themeImpl, } from "./backgroundImpl"; import type { MainToWorkerMessage } from "./type"; import type { IpcIHData } from "@/type/ipc"; @@ -92,7 +93,14 @@ onmessage = (e: MessageEvent) => { case "HOTKEY_SETTINGS": case "GET_DEFAULT_HOTKEY_SETTINGS": case "GET_DEFAULT_TOOLBAR_SETTING": + console.dir(e.data); + postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); + break; case "THEME": + // TODO: 実際のcurrentThemeを設定して返す + return themeImpl(e.data.args).then((v) => + typedPostMessage(v) + ); case "ON_VUEX_READY": case "GET_SETTING": case "SET_SETTING": diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 67843f8398..d990a322e7 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -62,3 +62,17 @@ export const getOssLicensesImpl: SandboxImpl["GET_OSS_LICENSES"] = () => { export const getUpdateInfosImpl: SandboxImpl["GET_UPDATE_INFOS"] = () => { return fetch(toStaticPath(UpdateInfosJsonFileName)).then((v) => v.json()); }; + +// FIXME: 簡単のため、currentThemeを固定値にしている +// 呼び出し元で実際の値でoverrideする +export const themeImpl: SandboxImpl["THEME"] = () => { + return Promise.all( + // FIXME: themeファイルのいい感じのパスの設定 + ["/themes/default.json", "/themes/dark.json"].map((url) => + fetch(url).then((res) => res.json()) + ) + ).then((v) => ({ + currentTheme: "Default", + availableThemes: v, + })); +}; From ee3eaf92032e3bcd190e9be04c737caf881c9159 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 13 Jun 2023 02:36:51 +0900 Subject: [PATCH 10/75] =?UTF-8?q?IndexedDB=E3=81=ABsettings=E3=82=92?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E3=81=97=E3=81=A6=E8=AA=AD=E3=82=81=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 11 +++++ src/browser/backgroundImpl.ts | 89 +++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/browser/background.ts b/src/browser/background.ts index 06e29403b6..4a9b10cf06 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -7,7 +7,9 @@ import { getPolicyTextImpl, getPrivacyPolicyTextImpl, getQAndATextImpl, + getSettingImpl, getUpdateInfosImpl, + setSettingImpl, themeImpl, } from "./backgroundImpl"; import type { MainToWorkerMessage } from "./type"; @@ -102,8 +104,17 @@ onmessage = (e: MessageEvent) => { typedPostMessage(v) ); case "ON_VUEX_READY": + console.dir(e.data); + postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); + break; case "GET_SETTING": + return getSettingImpl(e.data.args).then((v) => + typedPostMessage(v) + ); case "SET_SETTING": + return setSettingImpl(e.data.args).then((v) => + typedPostMessage(v) + ); case "SET_ENGINE_SETTING": case "SET_NATIVE_THEME": case "INSTALL_VVPP_ENGINE": diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index d990a322e7..aaadafcb80 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -1,4 +1,5 @@ import type { IpcIHData } from "@/type/ipc"; +import { electronStoreSchema } from "@/type/preload"; import { ContactTextFileName, HowToUseTextFileName, @@ -76,3 +77,91 @@ export const themeImpl: SandboxImpl["THEME"] = () => { availableThemes: v, })); }; + +const dbName = "voicevox-web"; +// FIXME: DBのバージョンを何かしらの形で行いたい +const dbVersion = 1; + +const openDB = () => + new Promise((resolve, reject) => { + const request = indexedDB.open(dbName, dbVersion); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + // TODO: handling + reject(request.error); + }; + request.onupgradeneeded = (ev) => { + if (ev.oldVersion === 0) { + // Initialize + const db = request.result; + const baseSchema = electronStoreSchema.parse({}); + Object.entries(baseSchema).forEach(([key, value]) => { + db.createObjectStore(key, { autoIncrement: true }).add(value); + }); + } + // TODO: migrate + }; + }); + +let db: IDBDatabase | null = null; + +export const getSettingImpl: SandboxImpl["GET_SETTING"] = async ([key]) => { + if (db === null) { + db = await openDB(); + } + + if (db === null) { + throw new Error("db is null"); + } + + return new Promise((resolve, reject) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const transaction = db!.transaction(key, "readonly"); + const store = transaction.objectStore(key); + const request = store.get(key); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }); +}; + +export const setSettingImpl: SandboxImpl["SET_SETTING"] = async ([ + key, + value, +]) => { + if (db === null) { + db = await openDB(); + } + + if (db === null) { + throw new Error("db is null"); + } + + return new Promise((resolve, reject) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const transaction = db!.transaction(key, "readwrite"); + const store = transaction.objectStore(key); + const request = store.put(value, key); + request.onsuccess = () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const readRequest = db! + .transaction(key, "readonly") + .objectStore(key) + .get(key); + readRequest.onsuccess = () => { + resolve(readRequest.result); + }; + readRequest.onerror = () => { + reject(readRequest.error); + }; + }; + request.onerror = () => { + reject(request.error); + }; + }); +}; From 2b4fcc1edcb4bdd3680a401420279a703660fba9 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 13 Jun 2023 20:22:03 +0900 Subject: [PATCH 11/75] =?UTF-8?q?Theme=E3=81=AFimpl=E3=81=AE=E6=96=B9?= =?UTF-8?q?=E3=81=A7settings=E3=81=8B=E3=82=89=E5=BC=95=E3=81=A3=E5=BC=B5?= =?UTF-8?q?=E3=81=A3=E3=81=A6=E5=8F=8D=E6=98=A0=E3=81=95=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/backgroundImpl.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index aaadafcb80..add233e96e 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -64,18 +64,26 @@ export const getUpdateInfosImpl: SandboxImpl["GET_UPDATE_INFOS"] = () => { return fetch(toStaticPath(UpdateInfosJsonFileName)).then((v) => v.json()); }; -// FIXME: 簡単のため、currentThemeを固定値にしている -// 呼び出し元で実際の値でoverrideする -export const themeImpl: SandboxImpl["THEME"] = () => { +export const themeImpl: SandboxImpl["THEME"] = async () => { return Promise.all( // FIXME: themeファイルのいい感じのパスの設定 ["/themes/default.json", "/themes/dark.json"].map((url) => fetch(url).then((res) => res.json()) ) - ).then((v) => ({ - currentTheme: "Default", - availableThemes: v, - })); + ) + .then((v) => ({ + currentTheme: "Default", + availableThemes: v, + })) + .then((v) => + getSettingImpl(["currentTheme"]).then( + (currentTheme) => + ({ + ...v, + currentTheme, + } as { currentTheme: string; availableThemes: any[] }) // FIXME: typing + ) + ); }; const dbName = "voicevox-web"; From 777e2f8e67897e831929db8e621f13c10cd0e538 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 19:25:32 +0900 Subject: [PATCH 12/75] =?UTF-8?q?IndexedDB=E3=81=A7=E3=81=AE=E5=80=A4?= =?UTF-8?q?=E3=81=AE=E6=8C=81=E3=81=A1=E6=96=B9=E9=96=93=E9=81=95=E3=81=88?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/backgroundImpl.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index add233e96e..d16044f53b 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -89,6 +89,8 @@ export const themeImpl: SandboxImpl["THEME"] = async () => { const dbName = "voicevox-web"; // FIXME: DBのバージョンを何かしらの形で行いたい const dbVersion = 1; +// NOTE: settingを複数持つことはないと仮定して、keyを固定してしまう +const entryKey = "value"; const openDB = () => new Promise((resolve, reject) => { @@ -106,7 +108,7 @@ const openDB = () => const db = request.result; const baseSchema = electronStoreSchema.parse({}); Object.entries(baseSchema).forEach(([key, value]) => { - db.createObjectStore(key, { autoIncrement: true }).add(value); + db.createObjectStore(key).add(value, entryKey); }); } // TODO: migrate @@ -128,7 +130,7 @@ export const getSettingImpl: SandboxImpl["GET_SETTING"] = async ([key]) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const transaction = db!.transaction(key, "readonly"); const store = transaction.objectStore(key); - const request = store.get(key); + const request = store.get(entryKey); request.onsuccess = () => { resolve(request.result); }; From 6b90d5f8da5a1fa74bc29ebc1b7b63e223d80b71 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 19:27:34 +0900 Subject: [PATCH 13/75] =?UTF-8?q?postMessage=E3=81=99=E3=82=8Bmessage?= =?UTF-8?q?=E3=81=AE=E5=9E=8B=E9=96=93=E9=81=95=E3=81=88=E3=81=A6=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 42 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index 4a9b10cf06..226a3526e9 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -18,52 +18,55 @@ import type { IpcIHData } from "@/type/ipc"; type MessageReturnTypes = { [K in keyof IpcIHData]: IpcIHData[K]["return"] }; const typedPostMessage = ( - message: MessageReturnTypes[K] + type: K, + message: MessageReturnTypes[K], + eventId: string ) => { - postMessage(message); + postMessage({ type, return: message, eventId }); }; onmessage = (e: MessageEvent) => { - switch (e.data.type) { + const type = e.data.type; + switch (type) { case "GET_APP_INFOS": return getAppInfosImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_TEMP_DIR": console.error("Not Implemented, it should not be called from VOICEVOX"); - postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); + postMessage({ type, return: [], eventId: e.data.eventId }); break; case "GET_HOW_TO_USE_TEXT": return getHowToUseTextImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_POLICY_TEXT": return getPolicyTextImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_OSS_LICENSES": return getOssLicensesImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_UPDATE_INFOS": return getUpdateInfosImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_OSS_COMMUNITY_INFOS": return getOssCommunityInfosImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_CONTACT_TEXT": return getContactTextImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_Q_AND_A_TEXT": return getQAndATextImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_PRIVACY_POLICY_TEXT": return getPrivacyPolicyTextImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "GET_ALT_PORT_INFOS": case "SHOW_AUDIO_SAVE_DIALOG": @@ -96,24 +99,23 @@ onmessage = (e: MessageEvent) => { case "GET_DEFAULT_HOTKEY_SETTINGS": case "GET_DEFAULT_TOOLBAR_SETTING": console.dir(e.data); - postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); + postMessage({ type: type, return: [], eventId: e.data.eventId }); break; case "THEME": - // TODO: 実際のcurrentThemeを設定して返す return themeImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "ON_VUEX_READY": console.dir(e.data); - postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); + postMessage({ type, return: [], eventId: e.data.eventId }); break; case "GET_SETTING": return getSettingImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "SET_SETTING": return setSettingImpl(e.data.args).then((v) => - typedPostMessage(v) + typedPostMessage(type, v, e.data.eventId) ); case "SET_ENGINE_SETTING": case "SET_NATIVE_THEME": @@ -125,7 +127,7 @@ onmessage = (e: MessageEvent) => { case "WRITE_FILE": case "READ_FILE": console.dir(e.data); - postMessage({ type: e.data.type, return: [], eventId: e.data.eventId }); + postMessage({ type, return: [], eventId: e.data.eventId }); break; } }; From a865dd2dfebcbb93296c11481aa0bbd10a11ceb3 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 20:06:53 +0900 Subject: [PATCH 14/75] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E9=96=A2=E6=95=B0=E3=82=82export=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=81=8A=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 7 ++++--- src/browser/backgroundImpl.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index 226a3526e9..3ab311a475 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -8,6 +8,7 @@ import { getPrivacyPolicyTextImpl, getQAndATextImpl, getSettingImpl, + getTempDirImpl, getUpdateInfosImpl, setSettingImpl, themeImpl, @@ -33,9 +34,9 @@ onmessage = (e: MessageEvent) => { typedPostMessage(type, v, e.data.eventId) ); case "GET_TEMP_DIR": - console.error("Not Implemented, it should not be called from VOICEVOX"); - postMessage({ type, return: [], eventId: e.data.eventId }); - break; + return getTempDirImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "GET_HOW_TO_USE_TEXT": return getHowToUseTextImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index d16044f53b..278b8e8c12 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -27,6 +27,14 @@ export const getAppInfosImpl: SandboxImpl["GET_APP_INFOS"] = () => { return Promise.resolve(appInfo); }; +/** + * @deprecated ブラウザ版では使用されていないはずです + */ +export const getTempDirImpl: SandboxImpl["GET_TEMP_DIR"] = () => { + console.error("Not Implemented, it should not be called from VOICEVOX"); + return Promise.resolve(""); +}; + // TODO: base pathを設定できるようにするか、ビルド時埋め込みにする const toStaticPath = (fileName: string) => `/${fileName}`; From b316bca2703c8d225449b8c2b6894c93b0cd0b08 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 20:10:02 +0900 Subject: [PATCH 15/75] impl getAltPortInfos --- src/browser/background.ts | 4 ++++ src/browser/backgroundImpl.ts | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/browser/background.ts b/src/browser/background.ts index 3ab311a475..679f783d74 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -1,4 +1,5 @@ import { + getAltPortInfosImpl, getAppInfosImpl, getContactTextImpl, getHowToUseTextImpl, @@ -70,6 +71,9 @@ onmessage = (e: MessageEvent) => { typedPostMessage(type, v, e.data.eventId) ); case "GET_ALT_PORT_INFOS": + return getAltPortInfosImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "SHOW_AUDIO_SAVE_DIALOG": case "SHOW_TEXT_SAVE_DIALOG": case "SHOW_VVPP_OPEN_DIALOG": diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 278b8e8c12..88087a61fa 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -72,6 +72,13 @@ export const getUpdateInfosImpl: SandboxImpl["GET_UPDATE_INFOS"] = () => { return fetch(toStaticPath(UpdateInfosJsonFileName)).then((v) => v.json()); }; +/** + * @deprecated ブラウザ版ではサポートされていません + */ +export const getAltPortInfosImpl: SandboxImpl["GET_ALT_PORT_INFOS"] = () => { + return Promise.resolve({}); +}; + export const themeImpl: SandboxImpl["THEME"] = async () => { return Promise.all( // FIXME: themeファイルのいい感じのパスの設定 From ba05df64634c0030183c83fb05c4863d8fff91db Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 20:19:27 +0900 Subject: [PATCH 16/75] =?UTF-8?q?IndexedDB=E7=9B=B4=E3=81=97=E3=81=9F2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/backgroundImpl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 88087a61fa..1e5c935c12 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -171,13 +171,13 @@ export const setSettingImpl: SandboxImpl["SET_SETTING"] = async ([ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const transaction = db!.transaction(key, "readwrite"); const store = transaction.objectStore(key); - const request = store.put(value, key); + const request = store.put(value, entryKey); request.onsuccess = () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const readRequest = db! .transaction(key, "readonly") .objectStore(key) - .get(key); + .get(entryKey); readRequest.onsuccess = () => { resolve(readRequest.result); }; From a6e3771c4172143c243c11bc7a90c2d481706ec2 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 20:34:15 +0900 Subject: [PATCH 17/75] =?UTF-8?q?openTextEditContextMenuImpl=E3=81=AF?= =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=9D=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 8 ++++++++ src/browser/backgroundImpl.ts | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/browser/background.ts b/src/browser/background.ts index 679f783d74..909ffb2848 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -11,6 +11,7 @@ import { getSettingImpl, getTempDirImpl, getUpdateInfosImpl, + openTextEditContextMenuImpl, setSettingImpl, themeImpl, } from "./backgroundImpl"; @@ -85,7 +86,14 @@ onmessage = (e: MessageEvent) => { case "SHOW_QUESTION_DIALOG": case "SHOW_WARNING_DIALOG": case "SHOW_ERROR_DIALOG": + // TODO: DIALOG周りの実装は要検討 + console.dir(e.data); + postMessage({ type, return: [], eventId: e.data.eventId }); + break; case "OPEN_TEXT_EDIT_CONTEXT_MENU": + return openTextEditContextMenuImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "IS_AVAILABLE_GPU_MODE": case "IS_MAXIMIZED_WINDOW": case "CLOSE_WINDOW": diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 1e5c935c12..f47b80f30d 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -79,6 +79,14 @@ export const getAltPortInfosImpl: SandboxImpl["GET_ALT_PORT_INFOS"] = () => { return Promise.resolve({}); }; +/** + * @deprecated ブラウザ版では不要です + */ +export const openTextEditContextMenuImpl: SandboxImpl["OPEN_TEXT_EDIT_CONTEXT_MENU"] = + () => { + return Promise.resolve(); + }; + export const themeImpl: SandboxImpl["THEME"] = async () => { return Promise.all( // FIXME: themeファイルのいい感じのパスの設定 From d2f96adc826d664ca651021a3fdc94da8c29abd8 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 20:40:11 +0900 Subject: [PATCH 18/75] =?UTF-8?q?Dummy=E3=81=A7EngineInfo=E3=82=92?= =?UTF-8?q?=E5=85=A5=E3=82=8C=E3=81=A6=E3=81=8A=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 7 +++++++ src/browser/backgroundImpl.ts | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index 909ffb2848..61fa0a3de7 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -1,4 +1,5 @@ import { + engineInfosImpl, getAltPortInfosImpl, getAppInfosImpl, getContactTextImpl, @@ -102,7 +103,13 @@ onmessage = (e: MessageEvent) => { case "LOG_ERROR": case "LOG_WARN": case "LOG_INFO": + console.dir(e.data); + postMessage({ type: type, return: [], eventId: e.data.eventId }); + break; case "ENGINE_INFOS": + return engineInfosImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "RESTART_ENGINE_ALL": case "RESTART_ENGINE": case "OPEN_ENGINE_DIRECTORY": diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index f47b80f30d..0987c1488b 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -1,5 +1,5 @@ import type { IpcIHData } from "@/type/ipc"; -import { electronStoreSchema } from "@/type/preload"; +import { EngineId, EngineInfo, electronStoreSchema } from "@/type/preload"; import { ContactTextFileName, HowToUseTextFileName, @@ -87,6 +87,21 @@ export const openTextEditContextMenuImpl: SandboxImpl["OPEN_TEXT_EDIT_CONTEXT_ME return Promise.resolve(); }; +const defaultEngine: EngineInfo = { + uuid: EngineId("074fc39e-678b-4c13-8916-ffca8d505d1d"), + host: "http://127.0.0.1:50021", + name: "VOICEVOX Engine", + path: undefined, + executionEnabled: false, + executionFilePath: "", + executionArgs: [], + type: "default", +}; + +export const engineInfosImpl: SandboxImpl["ENGINE_INFOS"] = async () => { + return [defaultEngine]; +}; + export const themeImpl: SandboxImpl["THEME"] = async () => { return Promise.all( // FIXME: themeファイルのいい感じのパスの設定 From 0c3d5c2af8fe5c0c8a3e9a2578d36cf9499b997b Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 20:43:38 +0900 Subject: [PATCH 19/75] =?UTF-8?q?engineSettings=E3=81=AEmigration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/backgroundImpl.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 0987c1488b..491335d5af 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -1,5 +1,10 @@ import type { IpcIHData } from "@/type/ipc"; -import { EngineId, EngineInfo, electronStoreSchema } from "@/type/preload"; +import { + EngineId, + EngineInfo, + electronStoreSchema, + engineSettingSchema, +} from "@/type/preload"; import { ContactTextFileName, HowToUseTextFileName, @@ -146,7 +151,19 @@ const openDB = () => const db = request.result; const baseSchema = electronStoreSchema.parse({}); Object.entries(baseSchema).forEach(([key, value]) => { - db.createObjectStore(key).add(value, entryKey); + const k = key as keyof typeof baseSchema; + if (k !== "engineSettings") { + db.createObjectStore(key).add(value, entryKey); + return; + } + // defaultのEngineSettingを追加 + const defaultVoicevoxEngineId = EngineId(defaultEngine.uuid); + db.createObjectStore(key).add( + { + [defaultVoicevoxEngineId]: engineSettingSchema.parse({}), + }, + entryKey + ); }); } // TODO: migrate From 3d8b048bb4a0e57a52908b68039c964396a4e5ca Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 23:21:07 +0900 Subject: [PATCH 20/75] Fix Theme impl --- src/browser/backgroundImpl.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 491335d5af..9df1fd3ec1 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -2,6 +2,7 @@ import type { IpcIHData } from "@/type/ipc"; import { EngineId, EngineInfo, + ThemeConf, electronStoreSchema, engineSettingSchema, } from "@/type/preload"; @@ -107,7 +108,11 @@ export const engineInfosImpl: SandboxImpl["ENGINE_INFOS"] = async () => { return [defaultEngine]; }; -export const themeImpl: SandboxImpl["THEME"] = async () => { +export const themeImpl: SandboxImpl["THEME"] = async ([{ newData }]) => { + if (newData !== undefined) { + await setSettingImpl(["currentTheme", newData]); + return; + } return Promise.all( // FIXME: themeファイルのいい感じのパスの設定 ["/themes/default.json", "/themes/dark.json"].map((url) => @@ -124,7 +129,7 @@ export const themeImpl: SandboxImpl["THEME"] = async () => { ({ ...v, currentTheme, - } as { currentTheme: string; availableThemes: any[] }) // FIXME: typing + } as { currentTheme: string; availableThemes: ThemeConf[] }) ) ); }; From d993cec89a98c46a4b6ae9b30ab2a15aa0fc79df Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 23:23:44 +0900 Subject: [PATCH 21/75] impl HotkeySettings --- src/browser/background.ts | 7 +++++++ src/browser/backgroundImpl.ts | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/browser/background.ts b/src/browser/background.ts index 61fa0a3de7..0e03a13990 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -12,6 +12,7 @@ import { getSettingImpl, getTempDirImpl, getUpdateInfosImpl, + hotkeySettingsImpl, openTextEditContextMenuImpl, setSettingImpl, themeImpl, @@ -115,7 +116,13 @@ onmessage = (e: MessageEvent) => { case "OPEN_ENGINE_DIRECTORY": case "CHECK_FILE_EXISTS": case "CHANGE_PIN_WINDOW": + console.dir(e.data); + postMessage({ type: type, return: [], eventId: e.data.eventId }); + break; case "HOTKEY_SETTINGS": + return hotkeySettingsImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "GET_DEFAULT_HOTKEY_SETTINGS": case "GET_DEFAULT_TOOLBAR_SETTING": console.dir(e.data); diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 9df1fd3ec1..b7871c0ed4 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -108,6 +108,27 @@ export const engineInfosImpl: SandboxImpl["ENGINE_INFOS"] = async () => { return [defaultEngine]; }; +export const hotkeySettingsImpl: SandboxImpl["HOTKEY_SETTINGS"] = async ([ + { newData }, +]) => { + type HotkeySettingType = ReturnType< + typeof electronStoreSchema["parse"] + >["hotkeySettings"]; + if (newData !== undefined) { + const hotkeySettings = (await getSettingImpl([ + "hotkeySettings", + ])) as HotkeySettingType; + const hotkeySetting = hotkeySettings.find( + (hotkey) => hotkey.action == newData.action + ); + if (hotkeySetting !== undefined) { + hotkeySetting.combination = newData.combination; + } + await setSettingImpl(["hotkeySettings", hotkeySettings]); + } + return getSettingImpl(["hotkeySettings"]) as Promise; +}; + export const themeImpl: SandboxImpl["THEME"] = async ([{ newData }]) => { if (newData !== undefined) { await setSettingImpl(["currentTheme", newData]); From ab044bad1a817fba765f38ec5b1830292a3642f1 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 14 Jun 2023 23:30:58 +0900 Subject: [PATCH 22/75] impl getDefault~ --- src/browser/background.ts | 11 ++++++++--- src/browser/backgroundImpl.ts | 12 ++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index 0e03a13990..8a46d48bc8 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -3,6 +3,8 @@ import { getAltPortInfosImpl, getAppInfosImpl, getContactTextImpl, + getDefaultHotkeySettingsImpl, + getDefaultToolbarSettingImpl, getHowToUseTextImpl, getOssCommunityInfosImpl, getOssLicensesImpl, @@ -124,10 +126,13 @@ onmessage = (e: MessageEvent) => { typedPostMessage(type, v, e.data.eventId) ); case "GET_DEFAULT_HOTKEY_SETTINGS": + return getDefaultHotkeySettingsImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "GET_DEFAULT_TOOLBAR_SETTING": - console.dir(e.data); - postMessage({ type: type, return: [], eventId: e.data.eventId }); - break; + return getDefaultToolbarSettingImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "THEME": return themeImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index b7871c0ed4..0881ef6246 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -3,6 +3,8 @@ import { EngineId, EngineInfo, ThemeConf, + defaultHotkeySettings, + defaultToolbarButtonSetting, electronStoreSchema, engineSettingSchema, } from "@/type/preload"; @@ -129,6 +131,16 @@ export const hotkeySettingsImpl: SandboxImpl["HOTKEY_SETTINGS"] = async ([ return getSettingImpl(["hotkeySettings"]) as Promise; }; +export const getDefaultHotkeySettingsImpl: SandboxImpl["GET_DEFAULT_HOTKEY_SETTINGS"] = + () => { + return Promise.resolve(defaultHotkeySettings); + }; + +export const getDefaultToolbarSettingImpl: SandboxImpl["GET_DEFAULT_TOOLBAR_SETTING"] = + () => { + return Promise.resolve(defaultToolbarButtonSetting); + }; + export const themeImpl: SandboxImpl["THEME"] = async ([{ newData }]) => { if (newData !== undefined) { await setSettingImpl(["currentTheme", newData]); From ffe6e2f2a63583dd75c978f42b6f630fce40b680 Mon Sep 17 00:00:00 2001 From: yamachu Date: Thu, 15 Jun 2023 21:30:24 +0900 Subject: [PATCH 23/75] impl log --- src/browser/background.ts | 17 ++++++++++++++--- src/browser/backgroundImpl.ts | 15 +++++++++++++++ src/browser/sandbox.ts | 3 --- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index 8a46d48bc8..c302c3137f 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -15,6 +15,9 @@ import { getTempDirImpl, getUpdateInfosImpl, hotkeySettingsImpl, + logErrorImpl, + logInfoImpl, + logWarnImpl, openTextEditContextMenuImpl, setSettingImpl, themeImpl, @@ -103,12 +106,20 @@ onmessage = (e: MessageEvent) => { case "CLOSE_WINDOW": case "MINIMIZE_WINDOW": case "MAXIMIZE_WINDOW": + // NOTE: Browser版ではサポートしない + return typedPostMessage(type, void 0, e.data.eventId); case "LOG_ERROR": + return logErrorImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "LOG_WARN": + return logWarnImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "LOG_INFO": - console.dir(e.data); - postMessage({ type: type, return: [], eventId: e.data.eventId }); - break; + return logInfoImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "ENGINE_INFOS": return engineInfosImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 0881ef6246..b68575714d 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -95,6 +95,21 @@ export const openTextEditContextMenuImpl: SandboxImpl["OPEN_TEXT_EDIT_CONTEXT_ME return Promise.resolve(); }; +export const logErrorImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { + console.error(...params); + return Promise.resolve(); +}; + +export const logWarnImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { + console.warn(...params); + return Promise.resolve(); +}; + +export const logInfoImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { + console.info(...params); + return Promise.resolve(); +}; + const defaultEngine: EngineInfo = { uuid: EngineId("074fc39e-678b-4c13-8916-ffca8d505d1d"), host: "http://127.0.0.1:50021", diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index a5d62c5cf5..ea63c281bd 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -148,15 +148,12 @@ export const api: typeof window[typeof SandboxKey] = { return invoker("MAXIMIZE_WINDOW", []); }, logError(...params: unknown[]) { - console.error(...params); return invoker("LOG_ERROR", [params]); }, logWarn(...params: unknown[]) { - console.warn(...params); return invoker("LOG_WARN", [params]); }, logInfo(...params: unknown[]) { - console.info(...params); return invoker("LOG_INFO", [params]); }, engineInfos() { From 3e47456c7c0197b5e7179fc8c059b266225996e8 Mon Sep 17 00:00:00 2001 From: yamachu Date: Thu, 15 Jun 2023 21:37:24 +0900 Subject: [PATCH 24/75] impl setEngineSetting --- src/browser/background.ts | 4 ++++ src/browser/backgroundImpl.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/browser/background.ts b/src/browser/background.ts index c302c3137f..e1071a7894 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -19,6 +19,7 @@ import { logInfoImpl, logWarnImpl, openTextEditContextMenuImpl, + setEngineSettingImpl, setSettingImpl, themeImpl, } from "./backgroundImpl"; @@ -161,6 +162,9 @@ onmessage = (e: MessageEvent) => { typedPostMessage(type, v, e.data.eventId) ); case "SET_ENGINE_SETTING": + return setEngineSettingImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "SET_NATIVE_THEME": case "INSTALL_VVPP_ENGINE": case "UNINSTALL_VVPP_ENGINE": diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index b68575714d..bec8ffaff0 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -2,6 +2,7 @@ import type { IpcIHData } from "@/type/ipc"; import { EngineId, EngineInfo, + EngineSettings, ThemeConf, defaultHotkeySettings, defaultToolbarButtonSetting, @@ -283,3 +284,15 @@ export const setSettingImpl: SandboxImpl["SET_SETTING"] = async ([ }; }); }; + +export const setEngineSettingImpl: SandboxImpl["SET_ENGINE_SETTING"] = async ([ + engineId, + newData, +]) => { + const engineSettings = (await getSettingImpl([ + "engineSettings", + ])) as EngineSettings; + engineSettings[engineId] = newData; + await setSettingImpl(["engineSettings", engineSettings]); + return; +}; From 152ac3026cfc9a0cf39a985fc6d3c1c86d865587 Mon Sep 17 00:00:00 2001 From: yamachu Date: Thu, 15 Jun 2023 22:10:57 +0900 Subject: [PATCH 25/75] =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=82=E3=81=AE=E3=81=AE=E3=83=9E=E3=83=BC=E3=82=AF?= =?UTF-8?q?=E3=81=A8=E3=83=86=E3=83=B3=E3=83=97=E3=83=AC=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 73 +++++++++++++++++++++++++++++++---- src/browser/backgroundImpl.ts | 51 ++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 8 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index e1071a7894..85ea52deb8 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -1,4 +1,5 @@ import { + checkFileExistsImpl, engineInfosImpl, getAltPortInfosImpl, getAppInfosImpl, @@ -15,13 +16,19 @@ import { getTempDirImpl, getUpdateInfosImpl, hotkeySettingsImpl, + isAvailableGpuModeImpl, + isMaximizedWindowImpl, + joinPathImpl, logErrorImpl, logInfoImpl, logWarnImpl, + onVuexReadyImpl, openTextEditContextMenuImpl, + readFileImpl, setEngineSettingImpl, setSettingImpl, themeImpl, + writeFileImpl, } from "./backgroundImpl"; import type { MainToWorkerMessage } from "./type"; import type { IpcIHData } from "@/type/ipc"; @@ -95,7 +102,9 @@ onmessage = (e: MessageEvent) => { case "SHOW_WARNING_DIALOG": case "SHOW_ERROR_DIALOG": // TODO: DIALOG周りの実装は要検討 + console.group(type); console.dir(e.data); + console.groupEnd(); postMessage({ type, return: [], eventId: e.data.eventId }); break; case "OPEN_TEXT_EDIT_CONTEXT_MENU": @@ -103,11 +112,20 @@ onmessage = (e: MessageEvent) => { typedPostMessage(type, v, e.data.eventId) ); case "IS_AVAILABLE_GPU_MODE": + return isAvailableGpuModeImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "IS_MAXIMIZED_WINDOW": + return isMaximizedWindowImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "CLOSE_WINDOW": case "MINIMIZE_WINDOW": case "MAXIMIZE_WINDOW": // NOTE: Browser版ではサポートしない + console.group(type); + console.dir(e.data); + console.groupEnd(); return typedPostMessage(type, void 0, e.data.eventId); case "LOG_ERROR": return logErrorImpl(e.data.args).then((v) => @@ -127,12 +145,27 @@ onmessage = (e: MessageEvent) => { ); case "RESTART_ENGINE_ALL": case "RESTART_ENGINE": + // NOTE: Browser版ではサポートしない + console.group(type); + console.dir(e.data); + console.groupEnd(); + return typedPostMessage(type, void 0, e.data.eventId); case "OPEN_ENGINE_DIRECTORY": + // NOTE: Browser版ではサポートしない + console.group(type); + console.dir(e.data); + console.groupEnd(); + return typedPostMessage(type, void 0, e.data.eventId); case "CHECK_FILE_EXISTS": + return checkFileExistsImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "CHANGE_PIN_WINDOW": + // NOTE: Browser版ではサポートしない + console.group(type); console.dir(e.data); - postMessage({ type: type, return: [], eventId: e.data.eventId }); - break; + console.groupEnd(); + return typedPostMessage(type, void 0, e.data.eventId); case "HOTKEY_SETTINGS": return hotkeySettingsImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) @@ -150,9 +183,9 @@ onmessage = (e: MessageEvent) => { typedPostMessage(type, v, e.data.eventId) ); case "ON_VUEX_READY": - console.dir(e.data); - postMessage({ type, return: [], eventId: e.data.eventId }); - break; + return onVuexReadyImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "GET_SETTING": return getSettingImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) @@ -166,15 +199,39 @@ onmessage = (e: MessageEvent) => { typedPostMessage(type, v, e.data.eventId) ); case "SET_NATIVE_THEME": + // NOTE: Browser版ではサポートしない + console.group(type); + console.dir(e.data); + console.groupEnd(); + postMessage({ type: type, return: [], eventId: e.data.eventId }); + break; case "INSTALL_VVPP_ENGINE": case "UNINSTALL_VVPP_ENGINE": case "VALIDATE_ENGINE_DIR": + // NOTE: Browser版ではサポートしない + console.group(type); + console.dir(e.data); + console.groupEnd(); + postMessage({ type: type, return: [], eventId: e.data.eventId }); + break; case "RESTART_APP": + // NOTE: Browser版ではサポートしない + console.group(type); + console.dir(e.data); + console.groupEnd(); + postMessage({ type: type, return: [], eventId: e.data.eventId }); + break; case "JOIN_PATH": + return joinPathImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "WRITE_FILE": + return writeFileImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); case "READ_FILE": - console.dir(e.data); - postMessage({ type, return: [], eventId: e.data.eventId }); - break; + return readFileImpl(e.data.args).then((v) => + typedPostMessage(type, v, e.data.eventId) + ); } }; diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index bec8ffaff0..084e4cd3da 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -96,6 +96,20 @@ export const openTextEditContextMenuImpl: SandboxImpl["OPEN_TEXT_EDIT_CONTEXT_ME return Promise.resolve(); }; +/** + * @fixme canvasでWebGLから調べたり、WebGPUがサポートされているかを調べたりで判断は出来そう + * @todo WebAssembly版をサポートする時に実装する + */ +export const isAvailableGpuModeImpl: SandboxImpl["IS_AVAILABLE_GPU_MODE"] = + () => { + return Promise.resolve(false); + }; + +// NOTE: UIの表示状態の制御のためだけなので固定値を返している +export const isMaximizedWindowImpl: SandboxImpl["IS_MAXIMIZED_WINDOW"] = () => { + return Promise.resolve(true); +}; + export const logErrorImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { console.error(...params); return Promise.resolve(); @@ -126,6 +140,14 @@ export const engineInfosImpl: SandboxImpl["ENGINE_INFOS"] = async () => { return [defaultEngine]; }; +export const checkFileExistsImpl: SandboxImpl["CHECK_FILE_EXISTS"] = async ([ + { file }, +]) => { + // TODO: Impl + console.error(`Not Implemented, check: ${file}`); + return Promise.resolve(false); +}; + export const hotkeySettingsImpl: SandboxImpl["HOTKEY_SETTINGS"] = async ([ { newData }, ]) => { @@ -183,6 +205,11 @@ export const themeImpl: SandboxImpl["THEME"] = async ([{ newData }]) => { ); }; +export const onVuexReadyImpl: SandboxImpl["ON_VUEX_READY"] = async () => { + // NOTE: 何もしなくて良さそう + return Promise.resolve(); +}; + const dbName = "voicevox-web"; // FIXME: DBのバージョンを何かしらの形で行いたい const dbVersion = 1; @@ -296,3 +323,27 @@ export const setEngineSettingImpl: SandboxImpl["SET_ENGINE_SETTING"] = async ([ await setSettingImpl(["engineSettings", engineSettings]); return; }; + +/** + * @deprecated ブラウザ版では使用されていないはずです + */ +export const joinPathImpl: SandboxImpl["JOIN_PATH"] = () => { + console.error("Not Implemented, it should not be called from VOICEVOX"); + return Promise.resolve(""); +}; + +export const writeFileImpl: SandboxImpl["WRITE_FILE"] = async ([ + { filePath, buffer }, +]) => { + // TODO: Impl + console.error(`Not Implemented, write: ${filePath}`, buffer); + return Promise.resolve(undefined); +}; + +export const readFileImpl: SandboxImpl["READ_FILE"] = async ([ + { filePath }, +]) => { + // TODO: Impl + console.error(`Not Implemented, read: ${filePath}`); + return Promise.resolve(new ArrayBuffer(0)); +}; From 7c9d4fe6cb295fd6cb8e4deb85320b0f54cf2876 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 00:27:23 +0900 Subject: [PATCH 26/75] =?UTF-8?q?schema=E4=B8=8D=E6=95=B4=E5=90=88?= =?UTF-8?q?=E3=81=8C=E8=B5=B7=E3=81=93=E3=82=8B=E5=85=A5=E5=8A=9B=E3=82=82?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E3=81=AA=E3=81=93=E3=81=A8=E3=81=AB=E6=B0=97?= =?UTF-8?q?=E3=81=A5=E3=81=84=E3=81=9F=E3=81=AE=E3=81=A7=E4=BB=8A=E5=BE=8C?= =?UTF-8?q?=E5=AF=BE=E5=87=A6=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/backgroundImpl.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 084e4cd3da..0678138526 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -288,6 +288,8 @@ export const setSettingImpl: SandboxImpl["SET_SETTING"] = async ([ throw new Error("db is null"); } + // TODO: Schemaに合っているか保存時にvalidationしたい + return new Promise((resolve, reject) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const transaction = db!.transaction(key, "readwrite"); From 3591ddb4cc66940ade468bc5cc89b508f03cec58 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 01:03:30 +0900 Subject: [PATCH 27/75] =?UTF-8?q?window.showSaveFilePicker=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E3=81=86=E3=81=AB=E3=81=AF=E5=9E=8B=E5=AE=9A=E7=BE=A9?= =?UTF-8?q?=E3=81=8C=E5=BF=85=E8=A6=81=E3=81=A0=E3=81=A3=E3=81=9F=E3=81=AE?= =?UTF-8?q?=E3=81=A7=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit see: https://stackoverflow.com/questions/71309058/property-showsavefilepicker-does-not-exist-on-type-window-typeof-globalthis --- package-lock.json | 13 +++++++++++++ package.json | 1 + 2 files changed, 14 insertions(+) diff --git a/package-lock.json b/package-lock.json index 69452bb0c4..18c2e5ac74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "@types/semver": "7.3.9", "@types/unzipper": "0.10.5", "@types/uuid": "8.3.4", + "@types/wicg-file-system-access": "2020.9.6", "@typescript-eslint/eslint-plugin": "5.38.1", "@typescript-eslint/parser": "5.38.1", "@vitejs/plugin-vue": "4.0.0", @@ -1956,6 +1957,12 @@ "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", "optional": true }, + "node_modules/@types/wicg-file-system-access": { + "version": "2020.9.6", + "resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.6.tgz", + "integrity": "sha512-6hogE75Hl2Ov/jgp8ZhDaGmIF/q3J07GtXf8nCJCwKTHq7971po5+DId7grft09zG7plBwpF6ZU0yx9Du4/e1A==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.22", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", @@ -18550,6 +18557,12 @@ "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", "optional": true }, + "@types/wicg-file-system-access": { + "version": "2020.9.6", + "resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.6.tgz", + "integrity": "sha512-6hogE75Hl2Ov/jgp8ZhDaGmIF/q3J07GtXf8nCJCwKTHq7971po5+DId7grft09zG7plBwpF6ZU0yx9Du4/e1A==", + "dev": true + }, "@types/yargs": { "version": "17.0.22", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", diff --git a/package.json b/package.json index 5dae34ad12..4764780d00 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "@types/semver": "7.3.9", "@types/unzipper": "0.10.5", "@types/uuid": "8.3.4", + "@types/wicg-file-system-access": "2020.9.6", "@typescript-eslint/eslint-plugin": "5.38.1", "@typescript-eslint/parser": "5.38.1", "@vitejs/plugin-vue": "4.0.0", From bc2a811d46f5b72d2c93349fd17f406279101ec9 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 01:03:48 +0900 Subject: [PATCH 28/75] =?UTF-8?q?tsconfig=E3=81=A7Dialog=E9=96=A2=E4=BF=82?= =?UTF-8?q?=E3=81=AE=E5=9E=8B=E3=82=92=E8=AA=AD=E3=81=BE=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 3fd320f9a5..39cf9c9d64 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "allowSyntheticDefaultImports": true, "sourceMap": true, "baseUrl": ".", - "types": ["vitest/globals"], + "types": ["vitest/globals", "@types/wicg-file-system-access"], "paths": { "@/*": ["src/*"] }, From c992283a79b7f93065ee9de0e838465261361edc Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 01:06:25 +0900 Subject: [PATCH 29/75] =?UTF-8?q?dialog=E3=82=84FileIO=E3=81=AB=E9=96=A2?= =?UTF-8?q?=E3=81=99=E3=82=8B=E5=87=A6=E7=90=86=E3=81=AFWebWorker=E3=81=A7?= =?UTF-8?q?=E8=A1=8C=E3=81=86=E3=81=AB=E3=81=AF=E3=81=8B=E3=81=AA=E3=82=8A?= =?UTF-8?q?=E9=9D=A2=E5=80=92=E3=81=9D=E3=81=86=E3=81=A0=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=81=AE=E3=81=A7=E3=80=81=E4=B8=80=E6=97=A6=E3=83=99=E3=82=BF?= =?UTF-8?q?=E3=81=ABsandbox=E3=81=AB=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 音声以外のDialogは優先度を低くして、まずは音声に関する処理を出来るようにした --- src/browser/sandbox.ts | 117 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 7 deletions(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index ea63c281bd..aabf003602 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -8,6 +8,7 @@ import { HotkeySetting, NativeThemeType, SandboxKey, + WriteFileErrorResult, } from "@/type/preload"; const worker = new Worker(new URL("./background.ts", import.meta.url), { @@ -31,6 +32,8 @@ const invoker = ( }); }; +let directoryHandler: FileSystemDirectoryHandle | undefined = undefined; + export const api: typeof window[typeof SandboxKey] = { getAppInfos() { return invoker("GET_APP_INFOS", []); @@ -74,8 +77,41 @@ export const api: typeof window[typeof SandboxKey] = { // DELETE_ME: もう使ってなさそう throw new Error(`not implemented: loadTempFile is already obsoleted`); }, - showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { - return invoker("SHOW_AUDIO_SAVE_DIALOG", [obj]); + async showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { + // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している + // FIXME: 途中でディレクトリを変えたいとかには対応できない… + if (directoryHandler === undefined) { + const _directoryHandler = await window + .showDirectoryPicker({ + mode: "readwrite", + }) + .catch(() => undefined); + if (_directoryHandler === undefined) { + return undefined; + } + + directoryHandler = _directoryHandler; + } + + const { defaultPath } = obj; + const fileHandle = await window + .showSaveFilePicker({ + types: [ + { + description: "Wave File", + accept: { + "audio/wav": [".wav"], + }, + }, + ], + excludeAcceptAllOption: true, + suggestedName: defaultPath, + }) + .catch(() => undefined); + if (fileHandle === undefined) { + return undefined; + } + return fileHandle.name; }, showTextSaveDialog(obj: { title: string; defaultPath?: string }) { return invoker("SHOW_TEXT_SAVE_DIALOG", [obj]); @@ -83,8 +119,18 @@ export const api: typeof window[typeof SandboxKey] = { showVvppOpenDialog(obj: { title: string; defaultPath?: string }) { return invoker("SHOW_VVPP_OPEN_DIALOG", [obj]); }, - showOpenDirectoryDialog(obj: { title: string }) { - return invoker("SHOW_OPEN_DIRECTORY_DIALOG", [obj]); + async showOpenDirectoryDialog(/** obj: { title: string } */) { + const _directoryHandler = await window + .showDirectoryPicker({ + mode: "readwrite", + }) + .catch(() => undefined); + if (_directoryHandler === undefined) { + return undefined; + } + + directoryHandler = _directoryHandler; + return _directoryHandler.name; }, showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { return invoker("SHOW_PROJECT_SAVE_DIALOG", [obj]); @@ -113,7 +159,40 @@ export const api: typeof window[typeof SandboxKey] = { return invoker("SHOW_IMPORT_FILE_DIALOG", [obj]); }, writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { - return invoker("WRITE_FILE", [obj]); + // FIXME: fixedDirectoryが設定されている場合は、絶対パスでの指定になるためディレクトリへの権限が得られていない状態になり失敗する + if (directoryHandler === undefined) { + return Promise.resolve({ + code: undefined, + message: "ディレクトリへのアクセス許可がありません", + }); + } + + /** FIXME: 以下のファイル名に関する処理は切り出して checkFile などでも再利用する */ + let path = obj.filePath; + + // FIXME: / や \ は OS 依存のため、どうにかする + if (path.includes("/") || path.includes("\\")) { + if (path.startsWith(directoryHandler.name)) { + path = path.slice(directoryHandler.name.length + 1 /* / or \ */); + } + } + + return directoryHandler + ?.getFileHandle(path, { create: true }) + .then((fileHandle) => { + return fileHandle.createWritable().then((writable) => { + return writable.write(obj.buffer).then(() => writable.close()); + }); + }) + .then(() => undefined) + .catch((e) => { + // FIXME + console.error(e); + return { + code: undefined, + message: e.message as string, + } as WriteFileErrorResult; + }); }, readFile(obj: { filePath: string }) { return invoker("READ_FILE", [obj]); @@ -168,8 +247,32 @@ export const api: typeof window[typeof SandboxKey] = { hotkeySettings(newData?: HotkeySetting) { return invoker("HOTKEY_SETTINGS", [{ newData }]); }, - checkFileExists(file: string) { - return invoker("CHECK_FILE_EXISTS", [{ file }]); + async checkFileExists(file: string) { + if (directoryHandler === undefined) { + // FIXME: trueだとloopするはず + return Promise.resolve(false); + } + // NOTE: fixedDirectoryが設定されている場合、filePathが名前だけでなくディレクトリも含んでいるため、失敗する + const fileEntries = []; + for await (const [ + fileOrDirectoryName, + entry, + ] of directoryHandler.entries()) { + if (entry.kind === "file") { + fileEntries.push(fileOrDirectoryName); + } + } + + let path = file; + + // FIXME: / や \ は OS 依存のため、どうにかする + if (path.includes("/") || path.includes("\\")) { + if (path.startsWith(directoryHandler.name)) { + path = path.slice(directoryHandler.name.length + 1 /* / or \ */); + } + } + + return Promise.resolve(fileEntries.includes(path)); }, changePinWindow() { return invoker("CHANGE_PIN_WINDOW", []); From dad2a65bac03f981f5062d2ac5c3468c594ff0f3 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 02:20:57 +0900 Subject: [PATCH 30/75] =?UTF-8?q?IndexedDB=E3=81=B8=E3=81=AE=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=82=92Worker=E4=BB=A5=E5=A4=96=E3=81=A7=E3=82=82?= =?UTF-8?q?=E8=A1=8C=E3=81=84=E3=81=9F=E3=81=84=E3=81=AE=E3=81=A7=E5=88=87?= =?UTF-8?q?=E3=82=8A=E5=87=BA=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/backgroundImpl.ts | 57 ++--------------------------------- src/browser/contract.ts | 12 ++++++++ src/browser/store.ts | 47 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 src/browser/contract.ts create mode 100644 src/browser/store.ts diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 0678138526..acfe013f9d 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -1,13 +1,12 @@ +import { defaultEngine } from "./contract"; +import { entryKey, openDB } from "./store"; import type { IpcIHData } from "@/type/ipc"; import { - EngineId, - EngineInfo, EngineSettings, ThemeConf, defaultHotkeySettings, defaultToolbarButtonSetting, electronStoreSchema, - engineSettingSchema, } from "@/type/preload"; import { ContactTextFileName, @@ -125,17 +124,6 @@ export const logInfoImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { return Promise.resolve(); }; -const defaultEngine: EngineInfo = { - uuid: EngineId("074fc39e-678b-4c13-8916-ffca8d505d1d"), - host: "http://127.0.0.1:50021", - name: "VOICEVOX Engine", - path: undefined, - executionEnabled: false, - executionFilePath: "", - executionArgs: [], - type: "default", -}; - export const engineInfosImpl: SandboxImpl["ENGINE_INFOS"] = async () => { return [defaultEngine]; }; @@ -210,47 +198,6 @@ export const onVuexReadyImpl: SandboxImpl["ON_VUEX_READY"] = async () => { return Promise.resolve(); }; -const dbName = "voicevox-web"; -// FIXME: DBのバージョンを何かしらの形で行いたい -const dbVersion = 1; -// NOTE: settingを複数持つことはないと仮定して、keyを固定してしまう -const entryKey = "value"; - -const openDB = () => - new Promise((resolve, reject) => { - const request = indexedDB.open(dbName, dbVersion); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - // TODO: handling - reject(request.error); - }; - request.onupgradeneeded = (ev) => { - if (ev.oldVersion === 0) { - // Initialize - const db = request.result; - const baseSchema = electronStoreSchema.parse({}); - Object.entries(baseSchema).forEach(([key, value]) => { - const k = key as keyof typeof baseSchema; - if (k !== "engineSettings") { - db.createObjectStore(key).add(value, entryKey); - return; - } - // defaultのEngineSettingを追加 - const defaultVoicevoxEngineId = EngineId(defaultEngine.uuid); - db.createObjectStore(key).add( - { - [defaultVoicevoxEngineId]: engineSettingSchema.parse({}), - }, - entryKey - ); - }); - } - // TODO: migrate - }; - }); - let db: IDBDatabase | null = null; export const getSettingImpl: SandboxImpl["GET_SETTING"] = async ([key]) => { diff --git a/src/browser/contract.ts b/src/browser/contract.ts new file mode 100644 index 0000000000..efad2f8ff1 --- /dev/null +++ b/src/browser/contract.ts @@ -0,0 +1,12 @@ +import { EngineInfo, EngineId } from "@/type/preload"; + +export const defaultEngine: EngineInfo = { + uuid: EngineId("074fc39e-678b-4c13-8916-ffca8d505d1d"), + host: "http://127.0.0.1:50021", + name: "VOICEVOX Engine", + path: undefined, + executionEnabled: false, + executionFilePath: "", + executionArgs: [], + type: "default", +}; diff --git a/src/browser/store.ts b/src/browser/store.ts new file mode 100644 index 0000000000..46b7d8ad46 --- /dev/null +++ b/src/browser/store.ts @@ -0,0 +1,47 @@ +import { defaultEngine } from "./contract"; +import { + EngineId, + electronStoreSchema, + engineSettingSchema, +} from "@/type/preload"; + +const dbName = "voicevox-web"; +// FIXME: DBのバージョンを何かしらの形で行いたい +const dbVersion = 1; +// NOTE: settingを複数持つことはないと仮定して、keyを固定してしまう +export const entryKey = "value"; + +export const openDB = () => + new Promise((resolve, reject) => { + const request = indexedDB.open(dbName, dbVersion); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + // TODO: handling + reject(request.error); + }; + request.onupgradeneeded = (ev) => { + if (ev.oldVersion === 0) { + // Initialize + const db = request.result; + const baseSchema = electronStoreSchema.parse({}); + Object.entries(baseSchema).forEach(([key, value]) => { + const k = key as keyof typeof baseSchema; + if (k !== "engineSettings") { + db.createObjectStore(key).add(value, entryKey); + return; + } + // defaultのEngineSettingを追加 + const defaultVoicevoxEngineId = EngineId(defaultEngine.uuid); + db.createObjectStore(key).add( + { + [defaultVoicevoxEngineId]: engineSettingSchema.parse({}), + }, + entryKey + ); + }); + } + // TODO: migrate + }; + }); From a2caf29fed73767e516452ee64468502bb0b6d26 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 02:25:52 +0900 Subject: [PATCH 31/75] =?UTF-8?q?Fixed=E3=81=AA=E3=82=93=E3=81=A1=E3=82=83?= =?UTF-8?q?=E3=82=89=E3=81=AB=E3=82=82=E5=AF=BE=E5=BF=9C=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=80=81directoryHandler?= =?UTF-8?q?=E3=82=92IndexedDB=E3=81=AB=E4=BF=9D=E5=AD=98=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=81=8A=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ディレクトリを削除した場合は再度同名のディレクトリを作成するか、設定し直す必要はある --- src/browser/contract.ts | 2 + src/browser/sandbox.ts | 204 +++++++++++++++++++++++++++++++++++----- src/browser/store.ts | 5 +- 3 files changed, 187 insertions(+), 24 deletions(-) diff --git a/src/browser/contract.ts b/src/browser/contract.ts index efad2f8ff1..64665359e8 100644 --- a/src/browser/contract.ts +++ b/src/browser/contract.ts @@ -10,3 +10,5 @@ export const defaultEngine: EngineInfo = { executionArgs: [], type: "default", }; + +export const directoryHandlerStoreKey = "directoryHandler"; diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index aabf003602..b95e001019 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -1,5 +1,8 @@ +import { sep } from "path"; import { v4 } from "uuid"; +import { directoryHandlerStoreKey } from "./contract"; import type { WorkerToMainMessage } from "./type"; +import { openDB } from "./store"; import { IpcIHData, IpcSOData } from "@/type/ipc"; import { ElectronStoreType, @@ -32,7 +35,13 @@ const invoker = ( }); }; -let directoryHandler: FileSystemDirectoryHandle | undefined = undefined; +const lastSelectedDirectoryHandlerSymbol = Symbol( + "lastSelectedDirectoryHandler" +); +const directoryHandlerMap: Map< + string | typeof lastSelectedDirectoryHandlerSymbol, + FileSystemDirectoryHandle +> = new Map(); export const api: typeof window[typeof SandboxKey] = { getAppInfos() { @@ -78,9 +87,11 @@ export const api: typeof window[typeof SandboxKey] = { throw new Error(`not implemented: loadTempFile is already obsoleted`); }, async showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { - // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している - // FIXME: 途中でディレクトリを変えたいとかには対応できない… - if (directoryHandler === undefined) { + if ( + directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined + ) { + // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している + // FIXME: 途中でディレクトリを変えたいとかには対応できない… const _directoryHandler = await window .showDirectoryPicker({ mode: "readwrite", @@ -90,7 +101,29 @@ export const api: typeof window[typeof SandboxKey] = { return undefined; } - directoryHandler = _directoryHandler; + const db = await openDB(); + await new Promise((resolve, reject) => { + const transaction = db.transaction( + directoryHandlerStoreKey, + "readwrite" + ); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.put(_directoryHandler, _directoryHandler.name); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + }); + + directoryHandlerMap.set( + lastSelectedDirectoryHandlerSymbol, + _directoryHandler + ); } const { defaultPath } = obj; @@ -111,6 +144,8 @@ export const api: typeof window[typeof SandboxKey] = { if (fileHandle === undefined) { return undefined; } + + // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない return fileHandle.name; }, showTextSaveDialog(obj: { title: string; defaultPath?: string }) { @@ -129,7 +164,24 @@ export const api: typeof window[typeof SandboxKey] = { return undefined; } - directoryHandler = _directoryHandler; + const db = await openDB(); + await new Promise((resolve, reject) => { + const transaction = db.transaction(directoryHandlerStoreKey, "readwrite"); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.put(_directoryHandler, _directoryHandler.name); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + }); + + // NOTE: 同一のディレクトリ名だった場合、後で選択されたディレクトリがそれ移行の処理で使用されるため、意図しない保存が発生するかもしれない + directoryHandlerMap.set(_directoryHandler.name, _directoryHandler); return _directoryHandler.name; }, showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { @@ -158,27 +210,83 @@ export const api: typeof window[typeof SandboxKey] = { showImportFileDialog(obj: { title: string }) { return invoker("SHOW_IMPORT_FILE_DIALOG", [obj]); }, - writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { - // FIXME: fixedDirectoryが設定されている場合は、絶対パスでの指定になるためディレクトリへの権限が得られていない状態になり失敗する - if (directoryHandler === undefined) { + async writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { + if ( + directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === + undefined && + obj.filePath.indexOf(sep) === -1 + ) { return Promise.resolve({ code: undefined, message: "ディレクトリへのアクセス許可がありません", }); } + let directoryHandler = directoryHandlerMap.get( + lastSelectedDirectoryHandlerSymbol + ); + /** FIXME: 以下のファイル名に関する処理は切り出して checkFile などでも再利用する */ let path = obj.filePath; - // FIXME: / や \ は OS 依存のため、どうにかする - if (path.includes("/") || path.includes("\\")) { - if (path.startsWith(directoryHandler.name)) { - path = path.slice(directoryHandler.name.length + 1 /* / or \ */); + if (path.includes(sep)) { + const maybeDirectoryHandlerName = path.split(sep)[0]; + if (directoryHandlerMap.has(maybeDirectoryHandlerName)) { + path = path.slice(maybeDirectoryHandlerName.length + sep.length); + directoryHandler = directoryHandlerMap.get(maybeDirectoryHandlerName); + } else { + const db = await openDB(); + const maybeFixedDirectory = await new Promise< + FileSystemDirectoryHandle | undefined + >((resolve, reject) => { + const transaction = db.transaction( + directoryHandlerStoreKey, + "readonly" + ); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.get(maybeDirectoryHandlerName); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + return undefined; + }); + + if (maybeFixedDirectory === undefined) { + return Promise.resolve({ + code: undefined, + message: "ディレクトリへのアクセス許可がありません", + }); + } + + if ( + !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) + ) { + return Promise.resolve({ + code: undefined, + message: "ディレクトリへのアクセス許可がありません", + }); + } + + directoryHandlerMap.set(maybeDirectoryHandlerName, maybeFixedDirectory); + directoryHandler = maybeFixedDirectory; } } + if (directoryHandler === undefined) { + return Promise.resolve({ + code: undefined, + message: "ディレクトリへのアクセス許可がありません", + }); + } + return directoryHandler - ?.getFileHandle(path, { create: true }) + .getFileHandle(path, { create: true }) .then((fileHandle) => { return fileHandle.createWritable().then((writable) => { return writable.write(obj.buffer).then(() => writable.close()); @@ -248,6 +356,65 @@ export const api: typeof window[typeof SandboxKey] = { return invoker("HOTKEY_SETTINGS", [{ newData }]); }, async checkFileExists(file: string) { + if ( + directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === + undefined && + file.indexOf(sep) === -1 + ) { + // FIXME: trueだとloopするはず + return Promise.resolve(false); + } + + let directoryHandler = directoryHandlerMap.get( + lastSelectedDirectoryHandlerSymbol + ); + + /** FIXME: 以下のファイル名に関する処理は切り出して checkFile などでも再利用する */ + let path = file; + + if (path.includes(sep)) { + const maybeDirectoryHandlerName = path.split(sep)[0]; + if (directoryHandlerMap.has(maybeDirectoryHandlerName)) { + path = path.slice(maybeDirectoryHandlerName.length + sep.length); + directoryHandler = directoryHandlerMap.get(maybeDirectoryHandlerName); + } else { + const db = await openDB(); + const maybeFixedDirectory = await new Promise< + FileSystemDirectoryHandle | undefined + >((resolve, reject) => { + const transaction = db.transaction( + directoryHandlerStoreKey, + "readonly" + ); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.get(maybeDirectoryHandlerName); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + return undefined; + }); + + if (maybeFixedDirectory === undefined) { + return Promise.resolve(false); + } + + if ( + !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) + ) { + return Promise.resolve(false); + } + + directoryHandlerMap.set(maybeDirectoryHandlerName, maybeFixedDirectory); + directoryHandler = maybeFixedDirectory; + } + } + if (directoryHandler === undefined) { // FIXME: trueだとloopするはず return Promise.resolve(false); @@ -263,15 +430,6 @@ export const api: typeof window[typeof SandboxKey] = { } } - let path = file; - - // FIXME: / や \ は OS 依存のため、どうにかする - if (path.includes("/") || path.includes("\\")) { - if (path.startsWith(directoryHandler.name)) { - path = path.slice(directoryHandler.name.length + 1 /* / or \ */); - } - } - return Promise.resolve(fileEntries.includes(path)); }, changePinWindow() { diff --git a/src/browser/store.ts b/src/browser/store.ts index 46b7d8ad46..0896ea70c9 100644 --- a/src/browser/store.ts +++ b/src/browser/store.ts @@ -1,4 +1,4 @@ -import { defaultEngine } from "./contract"; +import { defaultEngine, directoryHandlerStoreKey } from "./contract"; import { EngineId, electronStoreSchema, @@ -41,6 +41,9 @@ export const openDB = () => entryKey ); }); + + // DirectoryHandlerも格納する + db.createObjectStore(directoryHandlerStoreKey); } // TODO: migrate }; From ce9b6266daa07a41ee4ef56fd1fcd5cde57e61c4 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 02:27:54 +0900 Subject: [PATCH 32/75] =?UTF-8?q?db=E5=91=A8=E3=82=8A=E3=81=AE=E3=81=8A?= =?UTF-8?q?=E6=B0=97=E6=8C=81=E3=81=A1=E5=A4=89=E3=81=88=E3=81=A8=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/store.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/browser/store.ts b/src/browser/store.ts index 0896ea70c9..d0a34fb12b 100644 --- a/src/browser/store.ts +++ b/src/browser/store.ts @@ -6,7 +6,8 @@ import { } from "@/type/preload"; const dbName = "voicevox-web"; -// FIXME: DBのバージョンを何かしらの形で行いたい +// FIXME: DBのschemaを変更したら、dbVersionを上げる +// TODO: 気づけるようにしたい const dbVersion = 1; // NOTE: settingを複数持つことはないと仮定して、keyを固定してしまう export const entryKey = "value"; From 5d47d4fbe63810879a029d7d2f22aed22a5c2628 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 02:30:49 +0900 Subject: [PATCH 33/75] =?UTF-8?q?File=20I/O=E3=81=AB=E9=96=A2=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=82=E3=81=AE=E3=82=92=E4=B8=80=E6=97=A6Worker?= =?UTF-8?q?=E3=81=8B=E3=82=89=E6=8E=92=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 22 +++++----------------- src/browser/backgroundImpl.ts | 24 ------------------------ 2 files changed, 5 insertions(+), 41 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index 85ea52deb8..fb2841b524 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -1,5 +1,4 @@ import { - checkFileExistsImpl, engineInfosImpl, getAltPortInfosImpl, getAppInfosImpl, @@ -24,11 +23,9 @@ import { logWarnImpl, onVuexReadyImpl, openTextEditContextMenuImpl, - readFileImpl, setEngineSettingImpl, setSettingImpl, themeImpl, - writeFileImpl, } from "./backgroundImpl"; import type { MainToWorkerMessage } from "./type"; import type { IpcIHData } from "@/type/ipc"; @@ -101,11 +98,7 @@ onmessage = (e: MessageEvent) => { case "SHOW_QUESTION_DIALOG": case "SHOW_WARNING_DIALOG": case "SHOW_ERROR_DIALOG": - // TODO: DIALOG周りの実装は要検討 - console.group(type); - console.dir(e.data); - console.groupEnd(); - postMessage({ type, return: [], eventId: e.data.eventId }); + // NOTE: DialogはWorker側では処理しない break; case "OPEN_TEXT_EDIT_CONTEXT_MENU": return openTextEditContextMenuImpl(e.data.args).then((v) => @@ -157,9 +150,8 @@ onmessage = (e: MessageEvent) => { console.groupEnd(); return typedPostMessage(type, void 0, e.data.eventId); case "CHECK_FILE_EXISTS": - return checkFileExistsImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); + // NOTE: FileI/OはWorker側では処理しない + break; case "CHANGE_PIN_WINDOW": // NOTE: Browser版ではサポートしない console.group(type); @@ -226,12 +218,8 @@ onmessage = (e: MessageEvent) => { typedPostMessage(type, v, e.data.eventId) ); case "WRITE_FILE": - return writeFileImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); case "READ_FILE": - return readFileImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); + // NOTE: FileI/OはWorker側では処理しない + break; } }; diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index acfe013f9d..3a1529f6f7 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -128,14 +128,6 @@ export const engineInfosImpl: SandboxImpl["ENGINE_INFOS"] = async () => { return [defaultEngine]; }; -export const checkFileExistsImpl: SandboxImpl["CHECK_FILE_EXISTS"] = async ([ - { file }, -]) => { - // TODO: Impl - console.error(`Not Implemented, check: ${file}`); - return Promise.resolve(false); -}; - export const hotkeySettingsImpl: SandboxImpl["HOTKEY_SETTINGS"] = async ([ { newData }, ]) => { @@ -280,19 +272,3 @@ export const joinPathImpl: SandboxImpl["JOIN_PATH"] = () => { console.error("Not Implemented, it should not be called from VOICEVOX"); return Promise.resolve(""); }; - -export const writeFileImpl: SandboxImpl["WRITE_FILE"] = async ([ - { filePath, buffer }, -]) => { - // TODO: Impl - console.error(`Not Implemented, write: ${filePath}`, buffer); - return Promise.resolve(undefined); -}; - -export const readFileImpl: SandboxImpl["READ_FILE"] = async ([ - { filePath }, -]) => { - // TODO: Impl - console.error(`Not Implemented, read: ${filePath}`); - return Promise.resolve(new ArrayBuffer(0)); -}; From 48390391a074bf343f4c1466887b27e009c7dbc1 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 02:40:10 +0900 Subject: [PATCH 34/75] =?UTF-8?q?=E5=AE=9F=E3=81=AFdb=E3=81=AEopen?= =?UTF-8?q?=E3=81=AEcost=E3=81=9D=E3=81=93=E3=81=BE=E3=81=A7=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=93=E3=81=98=E3=82=83=E3=81=AA=E3=81=84=E3=81=8B?= =?UTF-8?q?=E8=AA=AD=E3=81=BF=E3=81=A7=E3=82=A4=E3=83=B3=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=B3=E3=82=B9=E4=BF=9D=E6=8C=81=E3=82=92=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=81=A7=E3=81=BF=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/backgroundImpl.ts | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 3a1529f6f7..2fe2807621 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -190,20 +190,11 @@ export const onVuexReadyImpl: SandboxImpl["ON_VUEX_READY"] = async () => { return Promise.resolve(); }; -let db: IDBDatabase | null = null; - export const getSettingImpl: SandboxImpl["GET_SETTING"] = async ([key]) => { - if (db === null) { - db = await openDB(); - } - - if (db === null) { - throw new Error("db is null"); - } + const db = await openDB(); return new Promise((resolve, reject) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const transaction = db!.transaction(key, "readonly"); + const transaction = db.transaction(key, "readonly"); const store = transaction.objectStore(key); const request = store.get(entryKey); request.onsuccess = () => { @@ -219,24 +210,15 @@ export const setSettingImpl: SandboxImpl["SET_SETTING"] = async ([ key, value, ]) => { - if (db === null) { - db = await openDB(); - } - - if (db === null) { - throw new Error("db is null"); - } + const db = await openDB(); // TODO: Schemaに合っているか保存時にvalidationしたい - return new Promise((resolve, reject) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const transaction = db!.transaction(key, "readwrite"); + const transaction = db.transaction(key, "readwrite"); const store = transaction.objectStore(key); const request = store.put(value, entryKey); request.onsuccess = () => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const readRequest = db! + const readRequest = db .transaction(key, "readonly") .objectStore(key) .get(entryKey); From d670c721f14ee341182299d6047db3fcaa85ce79 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 02:41:59 +0900 Subject: [PATCH 35/75] =?UTF-8?q?debug=E7=94=A8=E9=80=94=E3=81=A8=E3=81=97?= =?UTF-8?q?=E3=81=A6=E6=AE=8B=E3=81=97=E3=81=A6=E3=81=9F=E3=81=91=E3=81=A9?= =?UTF-8?q?=E3=80=81=E5=AE=9F=E8=A3=85=E3=81=AF=E5=BF=85=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9F=E3=82=BF=E3=82=A4=E3=83=9F=E3=83=B3?= =?UTF-8?q?=E3=82=B0=E3=81=A7=E8=A1=8C=E3=81=86=E3=81=AE=E3=81=A7=E4=B8=80?= =?UTF-8?q?=E6=97=A6log=E3=81=AE=E5=87=BA=E5=8A=9B=E3=82=92=E6=AD=A2?= =?UTF-8?q?=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index b95e001019..8d5437edb8 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -318,7 +318,6 @@ export const api: typeof window[typeof SandboxKey] = { channel: T, listener: (_: unknown, ...args: IpcSOData[T]["args"]) => void ) { - console.dir(`channel: ${channel}, listener: ${listener}`); window.addEventListener("message", (event) => { if (event.data.channel === channel) { listener(event.data.args); From d0670616726f2ef0aef0ff048ae880ac389518c9 Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 03:19:29 +0900 Subject: [PATCH 36/75] =?UTF-8?q?=E3=82=B3=E3=83=94=E3=83=9A=E3=81=A7?= =?UTF-8?q?=E3=81=A8=E3=82=8A=E3=81=82=E3=81=88=E3=81=9A=E5=BF=85=E8=A6=81?= =?UTF-8?q?=E3=81=AB=E3=81=AA=E3=82=8B=E3=82=82=E3=81=AE=E3=82=92=E7=94=9F?= =?UTF-8?q?=E3=82=84=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 263 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 253 insertions(+), 10 deletions(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 8d5437edb8..4105d05534 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -148,11 +148,74 @@ export const api: typeof window[typeof SandboxKey] = { // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない return fileHandle.name; }, - showTextSaveDialog(obj: { title: string; defaultPath?: string }) { - return invoker("SHOW_TEXT_SAVE_DIALOG", [obj]); + // FIXME: showSaveFilePickerのtypesが違うだけなので、いい感じにする + async showTextSaveDialog(obj: { title: string; defaultPath?: string }) { + if ( + directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined + ) { + // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している + // FIXME: 途中でディレクトリを変えたいとかには対応できない… + const _directoryHandler = await window + .showDirectoryPicker({ + mode: "readwrite", + }) + .catch(() => undefined); + if (_directoryHandler === undefined) { + return undefined; + } + + const db = await openDB(); + await new Promise((resolve, reject) => { + const transaction = db.transaction( + directoryHandlerStoreKey, + "readwrite" + ); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.put(_directoryHandler, _directoryHandler.name); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + }); + + directoryHandlerMap.set( + lastSelectedDirectoryHandlerSymbol, + _directoryHandler + ); + } + + const { defaultPath } = obj; + const fileHandle = await window + .showSaveFilePicker({ + types: [ + { + description: "Text File", + accept: { + "text/plain": [".txt"], + }, + }, + ], + excludeAcceptAllOption: true, + suggestedName: defaultPath, + }) + .catch(() => undefined); + if (fileHandle === undefined) { + return undefined; + } + + // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない + return fileHandle.name; }, showVvppOpenDialog(obj: { title: string; defaultPath?: string }) { - return invoker("SHOW_VVPP_OPEN_DIALOG", [obj]); + // NOTE: 今後接続先を変える手段としてVvppが使われるかもしれないので、そのタイミングで実装する + throw new Error( + `not implemented: showVvppOpenDialog, request: ${JSON.stringify(obj)}` + ); }, async showOpenDirectoryDialog(/** obj: { title: string } */) { const _directoryHandler = await window @@ -184,18 +247,139 @@ export const api: typeof window[typeof SandboxKey] = { directoryHandlerMap.set(_directoryHandler.name, _directoryHandler); return _directoryHandler.name; }, - showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { - return invoker("SHOW_PROJECT_SAVE_DIALOG", [obj]); + // FIXME: showSaveFilePickerのtypesが違うだけなので、いい感じにする + async showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { + if ( + directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined + ) { + // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している + // FIXME: 途中でディレクトリを変えたいとかには対応できない… + const _directoryHandler = await window + .showDirectoryPicker({ + mode: "readwrite", + }) + .catch(() => undefined); + if (_directoryHandler === undefined) { + return undefined; + } + + const db = await openDB(); + await new Promise((resolve, reject) => { + const transaction = db.transaction( + directoryHandlerStoreKey, + "readwrite" + ); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.put(_directoryHandler, _directoryHandler.name); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + }); + + directoryHandlerMap.set( + lastSelectedDirectoryHandlerSymbol, + _directoryHandler + ); + } + + const { defaultPath } = obj; + const fileHandle = await window + .showSaveFilePicker({ + types: [ + { + description: "VOICEVOX Project file", + accept: { + "application/json": [".vvproj"], + }, + }, + ], + excludeAcceptAllOption: true, + suggestedName: defaultPath, + }) + .catch(() => undefined); + if (fileHandle === undefined) { + return undefined; + } + + // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない + return fileHandle.name; }, - showProjectLoadDialog(obj: { title: string }) { - return invoker("SHOW_PROJECT_LOAD_DIALOG", [obj]); + // FIXME: showSaveFilePickerのtypesが違うだけなので、いい感じにする + async showProjectLoadDialog(/** obj: { title: string } */) { + if ( + directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined + ) { + // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している + // FIXME: 途中でディレクトリを変えたいとかには対応できない… + const _directoryHandler = await window + .showDirectoryPicker({ + mode: "readwrite", + }) + .catch(() => undefined); + if (_directoryHandler === undefined) { + return undefined; + } + + const db = await openDB(); + await new Promise((resolve, reject) => { + const transaction = db.transaction( + directoryHandlerStoreKey, + "readwrite" + ); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.put(_directoryHandler, _directoryHandler.name); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + }); + + directoryHandlerMap.set( + lastSelectedDirectoryHandlerSymbol, + _directoryHandler + ); + } + + const fileHandle = await window + .showOpenFilePicker({ + types: [ + { + description: "VOICEVOX Project file", + accept: { + "application/json": [".vvproj"], + }, + }, + ], + excludeAcceptAllOption: true, + multiple: false, + }) + .catch(() => undefined); + if (fileHandle === undefined) { + return undefined; + } + + return fileHandle.map((v) => v.name); }, showMessageDialog(obj: { type: "none" | "info" | "error" | "question" | "warning"; title: string; message: string; }) { - return invoker("SHOW_MESSAGE_DIALOG", [obj]); + window.alert(`${obj.title}\n${obj.message}`); + // NOTE: どの呼び出し元も、return valueを使用していないので雑に対応している + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return Promise.resolve({} as any); }, showQuestionDialog(obj: { type: "none" | "info" | "error" | "question" | "warning"; @@ -205,10 +389,69 @@ export const api: typeof window[typeof SandboxKey] = { cancelId?: number; defaultId?: number; }) { + // TODO: 例えば動的にdialog要素をDOMに生成して、それを表示させるみたいのはあるかもしれない return invoker("SHOW_QUESTION_DIALOG", [obj]); }, - showImportFileDialog(obj: { title: string }) { - return invoker("SHOW_IMPORT_FILE_DIALOG", [obj]); + // FilePath, textDialogと一緒でいいかも, description text + async showImportFileDialog(/** obj: { title: string } */) { + if ( + directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined + ) { + // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している + // FIXME: 途中でディレクトリを変えたいとかには対応できない… + const _directoryHandler = await window + .showDirectoryPicker({ + mode: "readwrite", + }) + .catch(() => undefined); + if (_directoryHandler === undefined) { + return undefined; + } + + const db = await openDB(); + await new Promise((resolve, reject) => { + const transaction = db.transaction( + directoryHandlerStoreKey, + "readwrite" + ); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.put(_directoryHandler, _directoryHandler.name); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + }); + + directoryHandlerMap.set( + lastSelectedDirectoryHandlerSymbol, + _directoryHandler + ); + } + + const fileHandle = await window + .showOpenFilePicker({ + types: [ + { + description: "Text", + accept: { + "text/plain": [".txt"], + }, + }, + ], + excludeAcceptAllOption: true, + multiple: false, + }) + .catch(() => undefined); + if (fileHandle === undefined) { + return undefined; + } + + return fileHandle[0].name; }, async writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { if ( From 07b54d79c417d56ae6edf74ea6266ad2306dfdfe Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 03:33:08 +0900 Subject: [PATCH 37/75] =?UTF-8?q?=E6=96=87=E5=AD=97=E5=88=97=E3=82=92?= =?UTF-8?q?=E5=86=8D=E5=BA=A6stringify=E3=81=97=E3=81=A6=E3=81=84=E3=81=9F?= =?UTF-8?q?=E3=81=AE=E3=81=A7=E3=80=81=E4=BA=8C=E9=87=8D=E3=81=AB=20"=20?= =?UTF-8?q?=E3=81=8C=E5=87=BA=E7=8F=BE=E3=81=97=E3=81=A6=E3=81=97=E3=81=BE?= =?UTF-8?q?=E3=81=A3=E3=81=A6=E3=81=84=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vite.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vite.config.ts b/vite.config.ts index 6f077edee7..556a538dc6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -99,8 +99,8 @@ export default defineConfig((options) => { ], define: { [`process.env`]: { - APP_NAME: JSON.stringify(process.env.npm_package_name), - APP_VERSION: JSON.stringify(process.env.npm_package_version), + APP_NAME: process.env.npm_package_name, + APP_VERSION: process.env.npm_package_version, }, }, }; From e577ad6ad823bfdc777343a8801a6c8dcdaa40da Mon Sep 17 00:00:00 2001 From: yamachu Date: Fri, 16 Jun 2023 03:33:28 +0900 Subject: [PATCH 38/75] =?UTF-8?q?=E3=81=BE=E3=81=9F=E3=82=B3=E3=83=94?= =?UTF-8?q?=E3=83=9A=E3=81=A7I/O=E5=91=A8=E3=82=8A=E3=81=8C=E7=94=9F?= =?UTF-8?q?=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 78 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 4105d05534..e3494df824 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -545,8 +545,82 @@ export const api: typeof window[typeof SandboxKey] = { } as WriteFileErrorResult; }); }, - readFile(obj: { filePath: string }) { - return invoker("READ_FILE", [obj]); + async readFile(obj: { filePath: string }) { + if ( + directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === + undefined && + obj.filePath.indexOf(sep) === -1 + ) { + return Promise.reject( + new Error("ディレクトリへのアクセス許可がありません") + ); + } + + let directoryHandler = directoryHandlerMap.get( + lastSelectedDirectoryHandlerSymbol + ); + + /** FIXME: 以下のファイル名に関する処理は切り出して checkFile などでも再利用する */ + let path = obj.filePath; + + if (path.includes(sep)) { + const maybeDirectoryHandlerName = path.split(sep)[0]; + if (directoryHandlerMap.has(maybeDirectoryHandlerName)) { + path = path.slice(maybeDirectoryHandlerName.length + sep.length); + directoryHandler = directoryHandlerMap.get(maybeDirectoryHandlerName); + } else { + const db = await openDB(); + const maybeFixedDirectory = await new Promise< + FileSystemDirectoryHandle | undefined + >((resolve, reject) => { + const transaction = db.transaction( + directoryHandlerStoreKey, + "readonly" + ); + const store = transaction.objectStore(directoryHandlerStoreKey); + const request = store.get(maybeDirectoryHandlerName); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }).catch((e) => { + console.error(e); + // 握り潰してる + return undefined; + }); + + if (maybeFixedDirectory === undefined) { + return Promise.reject( + new Error("ディレクトリへのアクセス許可がありません") + ); + } + + if ( + !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) + ) { + return Promise.reject( + new Error("ディレクトリへのアクセス許可がありません") + ); + } + + directoryHandlerMap.set(maybeDirectoryHandlerName, maybeFixedDirectory); + directoryHandler = maybeFixedDirectory; + } + } + + if (directoryHandler === undefined) { + return Promise.reject( + new Error("ディレクトリへのアクセス許可がありません") + ); + } + + return directoryHandler.getFileHandle(path).then((fileHandle) => { + return fileHandle.getFile().then((file) => { + return file.arrayBuffer(); + }); + }); }, openTextEditContextMenu() { return invoker("OPEN_TEXT_EDIT_CONTEXT_MENU", []); From a686ea23ddf97d007f396ead0254b5df34a59eb0 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 20 Jun 2023 00:48:50 +0900 Subject: [PATCH 39/75] handler -> handle --- src/browser/contract.ts | 2 +- src/browser/store.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/browser/contract.ts b/src/browser/contract.ts index 64665359e8..486ed0ef65 100644 --- a/src/browser/contract.ts +++ b/src/browser/contract.ts @@ -11,4 +11,4 @@ export const defaultEngine: EngineInfo = { type: "default", }; -export const directoryHandlerStoreKey = "directoryHandler"; +export const directoryHandleStoreKey = "directoryHandle"; diff --git a/src/browser/store.ts b/src/browser/store.ts index d0a34fb12b..abeef9244d 100644 --- a/src/browser/store.ts +++ b/src/browser/store.ts @@ -1,4 +1,4 @@ -import { defaultEngine, directoryHandlerStoreKey } from "./contract"; +import { defaultEngine, directoryHandleStoreKey } from "./contract"; import { EngineId, electronStoreSchema, @@ -43,8 +43,8 @@ export const openDB = () => ); }); - // DirectoryHandlerも格納する - db.createObjectStore(directoryHandlerStoreKey); + // DirectoryHandleも格納する + db.createObjectStore(directoryHandleStoreKey); } // TODO: migrate }; From 76b6c0f199bdb502a001f068c52603dd5c550ba7 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 20 Jun 2023 01:23:06 +0900 Subject: [PATCH 40/75] =?UTF-8?q?FileIO=E3=81=AB=E9=96=A2=E3=81=99?= =?UTF-8?q?=E3=82=8B=E5=87=A6=E7=90=86=E3=82=92=E5=88=A5=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=AB=E5=88=87=E3=82=8A=E5=87=BA=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 428 +++++++++++++++++++++++++++ src/browser/sandbox.ts | 628 +++------------------------------------- 2 files changed, 462 insertions(+), 594 deletions(-) create mode 100644 src/browser/fileImpl.ts diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts new file mode 100644 index 0000000000..9da184e2f2 --- /dev/null +++ b/src/browser/fileImpl.ts @@ -0,0 +1,428 @@ +import { sep } from "path"; +import { directoryHandleStoreKey } from "./contract"; +import { openDB } from "./store"; +import { SandboxKey, WriteFileErrorResult } from "@/type/preload"; + +const showWritableDirectoryPicker = async (): Promise< + FileSystemDirectoryHandle | undefined +> => + window + .showDirectoryPicker({ + mode: "readwrite", + }) + .catch(() => undefined); + +const storeDirectoryHandle = async ( + directoryHandle: FileSystemDirectoryHandle +): Promise => { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction(directoryHandleStoreKey, "readwrite"); + const store = transaction.objectStore(directoryHandleStoreKey); + const request = store.put(directoryHandle, directoryHandle.name); + request.onsuccess = () => { + resolve(); + }; + request.onerror = () => { + reject(request.error); + }; + }); +}; + +const lastSelectedDirectoryHandleSymbol = Symbol("lastSelectedDirectoryHandle"); +const directoryHandleMap: Map< + string | typeof lastSelectedDirectoryHandleSymbol, + FileSystemDirectoryHandle +> = new Map(); + +const updateLatestSelectedDirectoryHandle = ( + handle: FileSystemDirectoryHandle +) => { + directoryHandleMap.set(lastSelectedDirectoryHandleSymbol, handle); +}; + +const getLatestSelectedDirectoryHandle = () => + directoryHandleMap.get(lastSelectedDirectoryHandleSymbol); + +type AcceptFileType = { + description: string; + accept: Record; +}; + +const showWritableFilePicker = async ({ + suggestedName, + fileType, +}: { + suggestedName?: string; + fileType: AcceptFileType; +}) => + window + .showSaveFilePicker({ + types: [fileType], + excludeAcceptAllOption: true, + suggestedName, + }) + .catch(() => undefined); + +const showLoadableFilePicker = async ({ + fileType, +}: { + fileType: AcceptFileType; +}) => + window + .showOpenFilePicker({ + types: [fileType], + excludeAcceptAllOption: true, + multiple: false, + }) + .catch(() => undefined); + +const requestSaveFileNameWithDirectoryPermission = async ({ + suggestedName, + fileType, +}: { + suggestedName?: string; + fileType: AcceptFileType; +}) => { + if (directoryHandleMap.get(lastSelectedDirectoryHandleSymbol) === undefined) { + // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している + // FIXME: 途中でディレクトリを変えたいとかには対応できない… + const _directoryHandler = await showWritableDirectoryPicker(); + if (_directoryHandler === undefined) { + return undefined; + } + + await storeDirectoryHandle(_directoryHandler).catch((e) => + console.error(e) + ); + + updateLatestSelectedDirectoryHandle(_directoryHandler); + } + + const fileHandle = await showWritableFilePicker({ + suggestedName, + fileType, + }); + if (fileHandle === undefined) { + return undefined; + } + + // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない + return fileHandle.name; +}; + +const requestLoadFileNameWithDirectoryPermission = async ({ + fileType, +}: { + fileType: AcceptFileType; +}) => { + if (directoryHandleMap.get(lastSelectedDirectoryHandleSymbol) === undefined) { + // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している + // FIXME: 途中でディレクトリを変えたいとかには対応できない… + const _directoryHandler = await showWritableDirectoryPicker(); + if (_directoryHandler === undefined) { + return undefined; + } + + await storeDirectoryHandle(_directoryHandler).catch((e) => + console.error(e) + ); + + updateLatestSelectedDirectoryHandle(_directoryHandler); + } + + const fileHandle = await showLoadableFilePicker({ + fileType, + }); + if (fileHandle === undefined) { + return undefined; + } + + // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない + return fileHandle.map((v) => v.name); +}; + +export const showAudioSaveDialogImpl: typeof window[typeof SandboxKey]["showAudioSaveDialog"] = + async (obj: { title: string; defaultPath?: string }) => { + return requestSaveFileNameWithDirectoryPermission({ + suggestedName: obj.defaultPath, + fileType: { description: "Wave File", accept: { "audio/wav": [".wav"] } }, + }); + }; + +export const showTextSaveDialogImpl: typeof window[typeof SandboxKey]["showTextSaveDialog"] = + async (obj: { title: string; defaultPath?: string }) => { + return requestSaveFileNameWithDirectoryPermission({ + suggestedName: obj.defaultPath, + fileType: { + description: "Text File", + accept: { + "text/plain": [".txt"], + }, + }, + }); + }; + +export const showOpenDirectoryDialogImpl: typeof window[typeof SandboxKey]["showOpenDirectoryDialog"] = + async () => { + const _directoryHandler = await showWritableDirectoryPicker(); + if (_directoryHandler === undefined) { + return undefined; + } + + await storeDirectoryHandle(_directoryHandler).catch((e) => + console.error(e) + ); + + // NOTE: 同一のディレクトリ名だった場合、後で選択されたディレクトリがそれ移行の処理で使用されるため、意図しない保存が発生するかもしれない + directoryHandleMap.set(_directoryHandler.name, _directoryHandler); + return _directoryHandler.name; + }; + +export const showProjectSaveDialogImpl: typeof window[typeof SandboxKey]["showProjectSaveDialog"] = + async (obj: { title: string; defaultPath?: string }) => { + return requestSaveFileNameWithDirectoryPermission({ + suggestedName: obj.defaultPath, + fileType: { + description: "VOICEVOX Project file", + accept: { + "application/json": [".vvproj"], + }, + }, + }); + }; + +export const showProjectLoadDialogImpl: typeof window[typeof SandboxKey]["showProjectLoadDialog"] = + async () => { + return requestLoadFileNameWithDirectoryPermission({ + fileType: { + description: "VOICEVOX Project file", + accept: { + "application/json": [".vvproj"], + }, + }, + }); + }; + +export const showImportFileDialogImpl: typeof window[typeof SandboxKey]["showImportFileDialog"] = + async () => { + return requestLoadFileNameWithDirectoryPermission({ + fileType: { + description: "Text", + accept: { + "text/plain": [".txt"], + }, + }, + }).then((v) => v?.[0]); + }; + +const isRootPathButDirectoryNotSelected = (path: string) => + directoryHandleMap.get(lastSelectedDirectoryHandleSymbol) === undefined && + path.indexOf(sep) === -1; + +const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { + const db = await openDB(); + return new Promise( + (resolve, reject) => { + const transaction = db.transaction(directoryHandleStoreKey, "readonly"); + const store = transaction.objectStore(directoryHandleStoreKey); + const request = store.get(maybeDirectoryHandleName); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + } + ).catch((e) => { + console.error(e); + // 握り潰してる + return undefined; + }); +}; + +export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = + async (obj: { filePath: string; buffer: ArrayBuffer }) => { + if (isRootPathButDirectoryNotSelected(obj.filePath)) { + return Promise.resolve({ + code: undefined, + message: "ディレクトリへのアクセス許可がありません", + }); + } + + let directoryHandle = getLatestSelectedDirectoryHandle(); + let path = obj.filePath; + + if (path.includes(sep)) { + const maybeDirectoryHandleName = path.split(sep)[0]; + if (directoryHandleMap.has(maybeDirectoryHandleName)) { + path = path.slice(maybeDirectoryHandleName.length + sep.length); + directoryHandle = directoryHandleMap.get(maybeDirectoryHandleName); + } else { + // NOTE: fixedDirectoryの場合こっちに落ちる場合がある + const maybeFixedDirectory = await fetchStoredDirectoryHandle( + maybeDirectoryHandleName + ); + + if (maybeFixedDirectory === undefined) { + return Promise.resolve({ + code: undefined, + message: `ディレクトリへのアクセス許可がありません。アクセスしようとしたディレクトリ名: ${maybeDirectoryHandleName}`, + }); + } + + if ( + !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) + ) { + return Promise.resolve({ + code: undefined, + message: + "ディレクトリへのアクセス許可がありません。ファイルの書き込みのために書き込み許可が必要です。", + }); + } + + directoryHandleMap.set(maybeDirectoryHandleName, maybeFixedDirectory); + + path = path.slice(maybeDirectoryHandleName.length + sep.length); + directoryHandle = maybeFixedDirectory; + } + } + + if (directoryHandle === undefined) { + return Promise.resolve({ + code: undefined, + message: "ディレクトリへのアクセス許可がありません", + }); + } + + return directoryHandle + .getFileHandle(path, { create: true }) + .then(async (fileHandle) => { + const writable = await fileHandle.createWritable(); + await writable.write(obj.buffer); + return writable.close(); + }) + .then(() => undefined) + .catch((e) => { + // FIXME + console.error(e); + return { + code: undefined, + message: e.message as string, + } as WriteFileErrorResult; + }); + }; + +export const readFileImpl: typeof window[typeof SandboxKey]["readFile"] = + async (obj: { filePath: string }) => { + if (isRootPathButDirectoryNotSelected(obj.filePath)) { + return Promise.reject( + new Error("ディレクトリへのアクセス許可がありません") + ); + } + + let directoryHandle = getLatestSelectedDirectoryHandle(); + let path = obj.filePath; + + if (path.includes(sep)) { + const maybeDirectoryHandleName = path.split(sep)[0]; + if (directoryHandleMap.has(maybeDirectoryHandleName)) { + path = path.slice(maybeDirectoryHandleName.length + sep.length); + directoryHandle = directoryHandleMap.get(maybeDirectoryHandleName); + } else { + const maybeFixedDirectory = await fetchStoredDirectoryHandle( + maybeDirectoryHandleName + ); + + if (maybeFixedDirectory === undefined) { + return Promise.reject( + new Error( + `ディレクトリへのアクセス許可がありません。アクセスしようとしたディレクトリ名: ${maybeDirectoryHandleName}` + ) + ); + } + + if ( + !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) + ) { + return Promise.reject( + new Error( + "ディレクトリへのアクセス許可がありません。ファイルの読み書きのために許可が必要です。" + ) + ); + } + + directoryHandleMap.set(maybeDirectoryHandleName, maybeFixedDirectory); + + path = path.slice(maybeDirectoryHandleName.length + sep.length); + directoryHandle = maybeFixedDirectory; + } + } + + if (directoryHandle === undefined) { + return Promise.reject( + new Error("ディレクトリへのアクセス許可がありません") + ); + } + + return directoryHandle.getFileHandle(path).then(async (fileHandle) => { + const file = await fileHandle.getFile(); + return file.arrayBuffer(); + }); + }; + +export const checkFileExistsImpl: typeof window[typeof SandboxKey]["checkFileExists"] = + async (file) => { + if (isRootPathButDirectoryNotSelected(file)) { + // FIXME: trueだとloopするはず + return Promise.resolve(false); + } + + let directoryHandle = getLatestSelectedDirectoryHandle(); + let path = file; + + if (path.includes(sep)) { + const maybeDirectoryHandleName = path.split(sep)[0]; + if (directoryHandleMap.has(maybeDirectoryHandleName)) { + path = path.slice(maybeDirectoryHandleName.length + sep.length); + directoryHandle = directoryHandleMap.get(maybeDirectoryHandleName); + } else { + // NOTE: fixedDirectoryの場合こっちに落ちる場合がある + const maybeFixedDirectory = await fetchStoredDirectoryHandle( + maybeDirectoryHandleName + ); + + if (maybeFixedDirectory === undefined) { + return Promise.resolve(false); + } + + if ( + !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) + ) { + return Promise.resolve(false); + } + + directoryHandleMap.set(maybeDirectoryHandleName, maybeFixedDirectory); + + path = path.slice(maybeDirectoryHandleName.length + sep.length); + directoryHandle = maybeFixedDirectory; + } + } + + if (directoryHandle === undefined) { + // FIXME: trueだとloopするはず + return Promise.resolve(false); + } + + const fileEntries = []; + for await (const [ + fileOrDirectoryName, + entry, + ] of directoryHandle.entries()) { + if (entry.kind === "file") { + fileEntries.push(fileOrDirectoryName); + } + } + + return Promise.resolve(fileEntries.includes(path)); + }; diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index e3494df824..4f52890267 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -1,8 +1,16 @@ -import { sep } from "path"; import { v4 } from "uuid"; -import { directoryHandlerStoreKey } from "./contract"; +import { + checkFileExistsImpl, + readFileImpl, + showAudioSaveDialogImpl, + showImportFileDialogImpl, + showOpenDirectoryDialogImpl, + showProjectLoadDialogImpl, + showProjectSaveDialogImpl, + showTextSaveDialogImpl, + writeFileImpl, +} from "./fileImpl"; import type { WorkerToMainMessage } from "./type"; -import { openDB } from "./store"; import { IpcIHData, IpcSOData } from "@/type/ipc"; import { ElectronStoreType, @@ -11,7 +19,6 @@ import { HotkeySetting, NativeThemeType, SandboxKey, - WriteFileErrorResult, } from "@/type/preload"; const worker = new Worker(new URL("./background.ts", import.meta.url), { @@ -35,14 +42,6 @@ const invoker = ( }); }; -const lastSelectedDirectoryHandlerSymbol = Symbol( - "lastSelectedDirectoryHandler" -); -const directoryHandlerMap: Map< - string | typeof lastSelectedDirectoryHandlerSymbol, - FileSystemDirectoryHandle -> = new Map(); - export const api: typeof window[typeof SandboxKey] = { getAppInfos() { return invoker("GET_APP_INFOS", []); @@ -86,130 +85,11 @@ export const api: typeof window[typeof SandboxKey] = { // DELETE_ME: もう使ってなさそう throw new Error(`not implemented: loadTempFile is already obsoleted`); }, - async showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { - if ( - directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined - ) { - // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している - // FIXME: 途中でディレクトリを変えたいとかには対応できない… - const _directoryHandler = await window - .showDirectoryPicker({ - mode: "readwrite", - }) - .catch(() => undefined); - if (_directoryHandler === undefined) { - return undefined; - } - - const db = await openDB(); - await new Promise((resolve, reject) => { - const transaction = db.transaction( - directoryHandlerStoreKey, - "readwrite" - ); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.put(_directoryHandler, _directoryHandler.name); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - }); - - directoryHandlerMap.set( - lastSelectedDirectoryHandlerSymbol, - _directoryHandler - ); - } - - const { defaultPath } = obj; - const fileHandle = await window - .showSaveFilePicker({ - types: [ - { - description: "Wave File", - accept: { - "audio/wav": [".wav"], - }, - }, - ], - excludeAcceptAllOption: true, - suggestedName: defaultPath, - }) - .catch(() => undefined); - if (fileHandle === undefined) { - return undefined; - } - - // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない - return fileHandle.name; - }, - // FIXME: showSaveFilePickerのtypesが違うだけなので、いい感じにする - async showTextSaveDialog(obj: { title: string; defaultPath?: string }) { - if ( - directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined - ) { - // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している - // FIXME: 途中でディレクトリを変えたいとかには対応できない… - const _directoryHandler = await window - .showDirectoryPicker({ - mode: "readwrite", - }) - .catch(() => undefined); - if (_directoryHandler === undefined) { - return undefined; - } - - const db = await openDB(); - await new Promise((resolve, reject) => { - const transaction = db.transaction( - directoryHandlerStoreKey, - "readwrite" - ); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.put(_directoryHandler, _directoryHandler.name); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - }); - - directoryHandlerMap.set( - lastSelectedDirectoryHandlerSymbol, - _directoryHandler - ); - } - - const { defaultPath } = obj; - const fileHandle = await window - .showSaveFilePicker({ - types: [ - { - description: "Text File", - accept: { - "text/plain": [".txt"], - }, - }, - ], - excludeAcceptAllOption: true, - suggestedName: defaultPath, - }) - .catch(() => undefined); - if (fileHandle === undefined) { - return undefined; - } - - // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない - return fileHandle.name; + showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { + return showAudioSaveDialogImpl(obj); + }, + showTextSaveDialog(obj: { title: string; defaultPath?: string }) { + return showTextSaveDialogImpl(obj); }, showVvppOpenDialog(obj: { title: string; defaultPath?: string }) { // NOTE: 今後接続先を変える手段としてVvppが使われるかもしれないので、そのタイミングで実装する @@ -217,159 +97,14 @@ export const api: typeof window[typeof SandboxKey] = { `not implemented: showVvppOpenDialog, request: ${JSON.stringify(obj)}` ); }, - async showOpenDirectoryDialog(/** obj: { title: string } */) { - const _directoryHandler = await window - .showDirectoryPicker({ - mode: "readwrite", - }) - .catch(() => undefined); - if (_directoryHandler === undefined) { - return undefined; - } - - const db = await openDB(); - await new Promise((resolve, reject) => { - const transaction = db.transaction(directoryHandlerStoreKey, "readwrite"); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.put(_directoryHandler, _directoryHandler.name); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - }); - - // NOTE: 同一のディレクトリ名だった場合、後で選択されたディレクトリがそれ移行の処理で使用されるため、意図しない保存が発生するかもしれない - directoryHandlerMap.set(_directoryHandler.name, _directoryHandler); - return _directoryHandler.name; - }, - // FIXME: showSaveFilePickerのtypesが違うだけなので、いい感じにする - async showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { - if ( - directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined - ) { - // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している - // FIXME: 途中でディレクトリを変えたいとかには対応できない… - const _directoryHandler = await window - .showDirectoryPicker({ - mode: "readwrite", - }) - .catch(() => undefined); - if (_directoryHandler === undefined) { - return undefined; - } - - const db = await openDB(); - await new Promise((resolve, reject) => { - const transaction = db.transaction( - directoryHandlerStoreKey, - "readwrite" - ); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.put(_directoryHandler, _directoryHandler.name); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - }); - - directoryHandlerMap.set( - lastSelectedDirectoryHandlerSymbol, - _directoryHandler - ); - } - - const { defaultPath } = obj; - const fileHandle = await window - .showSaveFilePicker({ - types: [ - { - description: "VOICEVOX Project file", - accept: { - "application/json": [".vvproj"], - }, - }, - ], - excludeAcceptAllOption: true, - suggestedName: defaultPath, - }) - .catch(() => undefined); - if (fileHandle === undefined) { - return undefined; - } - - // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない - return fileHandle.name; - }, - // FIXME: showSaveFilePickerのtypesが違うだけなので、いい感じにする - async showProjectLoadDialog(/** obj: { title: string } */) { - if ( - directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined - ) { - // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している - // FIXME: 途中でディレクトリを変えたいとかには対応できない… - const _directoryHandler = await window - .showDirectoryPicker({ - mode: "readwrite", - }) - .catch(() => undefined); - if (_directoryHandler === undefined) { - return undefined; - } - - const db = await openDB(); - await new Promise((resolve, reject) => { - const transaction = db.transaction( - directoryHandlerStoreKey, - "readwrite" - ); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.put(_directoryHandler, _directoryHandler.name); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - }); - - directoryHandlerMap.set( - lastSelectedDirectoryHandlerSymbol, - _directoryHandler - ); - } - - const fileHandle = await window - .showOpenFilePicker({ - types: [ - { - description: "VOICEVOX Project file", - accept: { - "application/json": [".vvproj"], - }, - }, - ], - excludeAcceptAllOption: true, - multiple: false, - }) - .catch(() => undefined); - if (fileHandle === undefined) { - return undefined; - } - - return fileHandle.map((v) => v.name); + showOpenDirectoryDialog(obj: { title: string }) { + return showOpenDirectoryDialogImpl(obj); + }, + showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { + return showProjectSaveDialogImpl(obj); + }, + showProjectLoadDialog(obj: { title: string }) { + return showProjectLoadDialogImpl(obj); }, showMessageDialog(obj: { type: "none" | "info" | "error" | "question" | "warning"; @@ -392,235 +127,14 @@ export const api: typeof window[typeof SandboxKey] = { // TODO: 例えば動的にdialog要素をDOMに生成して、それを表示させるみたいのはあるかもしれない return invoker("SHOW_QUESTION_DIALOG", [obj]); }, - // FilePath, textDialogと一緒でいいかも, description text - async showImportFileDialog(/** obj: { title: string } */) { - if ( - directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === undefined - ) { - // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している - // FIXME: 途中でディレクトリを変えたいとかには対応できない… - const _directoryHandler = await window - .showDirectoryPicker({ - mode: "readwrite", - }) - .catch(() => undefined); - if (_directoryHandler === undefined) { - return undefined; - } - - const db = await openDB(); - await new Promise((resolve, reject) => { - const transaction = db.transaction( - directoryHandlerStoreKey, - "readwrite" - ); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.put(_directoryHandler, _directoryHandler.name); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - }); - - directoryHandlerMap.set( - lastSelectedDirectoryHandlerSymbol, - _directoryHandler - ); - } - - const fileHandle = await window - .showOpenFilePicker({ - types: [ - { - description: "Text", - accept: { - "text/plain": [".txt"], - }, - }, - ], - excludeAcceptAllOption: true, - multiple: false, - }) - .catch(() => undefined); - if (fileHandle === undefined) { - return undefined; - } - - return fileHandle[0].name; - }, - async writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { - if ( - directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === - undefined && - obj.filePath.indexOf(sep) === -1 - ) { - return Promise.resolve({ - code: undefined, - message: "ディレクトリへのアクセス許可がありません", - }); - } - - let directoryHandler = directoryHandlerMap.get( - lastSelectedDirectoryHandlerSymbol - ); - - /** FIXME: 以下のファイル名に関する処理は切り出して checkFile などでも再利用する */ - let path = obj.filePath; - - if (path.includes(sep)) { - const maybeDirectoryHandlerName = path.split(sep)[0]; - if (directoryHandlerMap.has(maybeDirectoryHandlerName)) { - path = path.slice(maybeDirectoryHandlerName.length + sep.length); - directoryHandler = directoryHandlerMap.get(maybeDirectoryHandlerName); - } else { - const db = await openDB(); - const maybeFixedDirectory = await new Promise< - FileSystemDirectoryHandle | undefined - >((resolve, reject) => { - const transaction = db.transaction( - directoryHandlerStoreKey, - "readonly" - ); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.get(maybeDirectoryHandlerName); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - return undefined; - }); - - if (maybeFixedDirectory === undefined) { - return Promise.resolve({ - code: undefined, - message: "ディレクトリへのアクセス許可がありません", - }); - } - - if ( - !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) - ) { - return Promise.resolve({ - code: undefined, - message: "ディレクトリへのアクセス許可がありません", - }); - } - - directoryHandlerMap.set(maybeDirectoryHandlerName, maybeFixedDirectory); - directoryHandler = maybeFixedDirectory; - } - } - - if (directoryHandler === undefined) { - return Promise.resolve({ - code: undefined, - message: "ディレクトリへのアクセス許可がありません", - }); - } - - return directoryHandler - .getFileHandle(path, { create: true }) - .then((fileHandle) => { - return fileHandle.createWritable().then((writable) => { - return writable.write(obj.buffer).then(() => writable.close()); - }); - }) - .then(() => undefined) - .catch((e) => { - // FIXME - console.error(e); - return { - code: undefined, - message: e.message as string, - } as WriteFileErrorResult; - }); - }, - async readFile(obj: { filePath: string }) { - if ( - directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === - undefined && - obj.filePath.indexOf(sep) === -1 - ) { - return Promise.reject( - new Error("ディレクトリへのアクセス許可がありません") - ); - } - - let directoryHandler = directoryHandlerMap.get( - lastSelectedDirectoryHandlerSymbol - ); - - /** FIXME: 以下のファイル名に関する処理は切り出して checkFile などでも再利用する */ - let path = obj.filePath; - - if (path.includes(sep)) { - const maybeDirectoryHandlerName = path.split(sep)[0]; - if (directoryHandlerMap.has(maybeDirectoryHandlerName)) { - path = path.slice(maybeDirectoryHandlerName.length + sep.length); - directoryHandler = directoryHandlerMap.get(maybeDirectoryHandlerName); - } else { - const db = await openDB(); - const maybeFixedDirectory = await new Promise< - FileSystemDirectoryHandle | undefined - >((resolve, reject) => { - const transaction = db.transaction( - directoryHandlerStoreKey, - "readonly" - ); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.get(maybeDirectoryHandlerName); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - return undefined; - }); - - if (maybeFixedDirectory === undefined) { - return Promise.reject( - new Error("ディレクトリへのアクセス許可がありません") - ); - } - - if ( - !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) - ) { - return Promise.reject( - new Error("ディレクトリへのアクセス許可がありません") - ); - } - - directoryHandlerMap.set(maybeDirectoryHandlerName, maybeFixedDirectory); - directoryHandler = maybeFixedDirectory; - } - } - - if (directoryHandler === undefined) { - return Promise.reject( - new Error("ディレクトリへのアクセス許可がありません") - ); - } - - return directoryHandler.getFileHandle(path).then((fileHandle) => { - return fileHandle.getFile().then((file) => { - return file.arrayBuffer(); - }); - }); + showImportFileDialog(obj: { title: string }) { + return showImportFileDialogImpl(obj); + }, + writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { + return writeFileImpl(obj); + }, + readFile(obj: { filePath: string }) { + return readFileImpl(obj); }, openTextEditContextMenu() { return invoker("OPEN_TEXT_EDIT_CONTEXT_MENU", []); @@ -671,82 +185,8 @@ export const api: typeof window[typeof SandboxKey] = { hotkeySettings(newData?: HotkeySetting) { return invoker("HOTKEY_SETTINGS", [{ newData }]); }, - async checkFileExists(file: string) { - if ( - directoryHandlerMap.get(lastSelectedDirectoryHandlerSymbol) === - undefined && - file.indexOf(sep) === -1 - ) { - // FIXME: trueだとloopするはず - return Promise.resolve(false); - } - - let directoryHandler = directoryHandlerMap.get( - lastSelectedDirectoryHandlerSymbol - ); - - /** FIXME: 以下のファイル名に関する処理は切り出して checkFile などでも再利用する */ - let path = file; - - if (path.includes(sep)) { - const maybeDirectoryHandlerName = path.split(sep)[0]; - if (directoryHandlerMap.has(maybeDirectoryHandlerName)) { - path = path.slice(maybeDirectoryHandlerName.length + sep.length); - directoryHandler = directoryHandlerMap.get(maybeDirectoryHandlerName); - } else { - const db = await openDB(); - const maybeFixedDirectory = await new Promise< - FileSystemDirectoryHandle | undefined - >((resolve, reject) => { - const transaction = db.transaction( - directoryHandlerStoreKey, - "readonly" - ); - const store = transaction.objectStore(directoryHandlerStoreKey); - const request = store.get(maybeDirectoryHandlerName); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }).catch((e) => { - console.error(e); - // 握り潰してる - return undefined; - }); - - if (maybeFixedDirectory === undefined) { - return Promise.resolve(false); - } - - if ( - !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) - ) { - return Promise.resolve(false); - } - - directoryHandlerMap.set(maybeDirectoryHandlerName, maybeFixedDirectory); - directoryHandler = maybeFixedDirectory; - } - } - - if (directoryHandler === undefined) { - // FIXME: trueだとloopするはず - return Promise.resolve(false); - } - // NOTE: fixedDirectoryが設定されている場合、filePathが名前だけでなくディレクトリも含んでいるため、失敗する - const fileEntries = []; - for await (const [ - fileOrDirectoryName, - entry, - ] of directoryHandler.entries()) { - if (entry.kind === "file") { - fileEntries.push(fileOrDirectoryName); - } - } - - return Promise.resolve(fileEntries.includes(path)); + checkFileExists(file: string) { + return checkFileExistsImpl(file); }, changePinWindow() { return invoker("CHANGE_PIN_WINDOW", []); From 3479b34610f596c7c325e9e9ff9da45db67bd9e4 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 20 Jun 2023 01:23:13 +0900 Subject: [PATCH 41/75] =?UTF-8?q?=E3=81=84=E3=81=A4=E3=81=8B=E3=82=84?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 4f52890267..2fb5e713da 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -124,6 +124,7 @@ export const api: typeof window[typeof SandboxKey] = { cancelId?: number; defaultId?: number; }) { + // FIXME // TODO: 例えば動的にdialog要素をDOMに生成して、それを表示させるみたいのはあるかもしれない return invoker("SHOW_QUESTION_DIALOG", [obj]); }, From 46158a904444c12e88d5890ac6193caf2e028241 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 20 Jun 2023 01:36:19 +0900 Subject: [PATCH 42/75] =?UTF-8?q?docs=E3=81=AB=E3=81=9D=E3=81=A3=E3=81=A6?= =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E5=90=91=E3=81=91=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 9da184e2f2..ff8e303f38 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -246,7 +246,7 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = if (isRootPathButDirectoryNotSelected(obj.filePath)) { return Promise.resolve({ code: undefined, - message: "ディレクトリへのアクセス許可がありません", + message: "フォルダへのアクセス許可がありません", }); } @@ -267,7 +267,7 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = if (maybeFixedDirectory === undefined) { return Promise.resolve({ code: undefined, - message: `ディレクトリへのアクセス許可がありません。アクセスしようとしたディレクトリ名: ${maybeDirectoryHandleName}`, + message: `フォルダへのアクセス許可がありません。アクセスしようとしたフォルダ名: ${maybeDirectoryHandleName}`, }); } @@ -277,7 +277,7 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = return Promise.resolve({ code: undefined, message: - "ディレクトリへのアクセス許可がありません。ファイルの書き込みのために書き込み許可が必要です。", + "フォルダへのアクセス許可がありません。ファイルの書き込みのために書き込み許可が必要です。", }); } @@ -291,7 +291,7 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = if (directoryHandle === undefined) { return Promise.resolve({ code: undefined, - message: "ディレクトリへのアクセス許可がありません", + message: "フォルダへのアクセス許可がありません", }); } @@ -316,9 +316,7 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = export const readFileImpl: typeof window[typeof SandboxKey]["readFile"] = async (obj: { filePath: string }) => { if (isRootPathButDirectoryNotSelected(obj.filePath)) { - return Promise.reject( - new Error("ディレクトリへのアクセス許可がありません") - ); + return Promise.reject(new Error("フォルダへのアクセス許可がありません")); } let directoryHandle = getLatestSelectedDirectoryHandle(); @@ -337,7 +335,7 @@ export const readFileImpl: typeof window[typeof SandboxKey]["readFile"] = if (maybeFixedDirectory === undefined) { return Promise.reject( new Error( - `ディレクトリへのアクセス許可がありません。アクセスしようとしたディレクトリ名: ${maybeDirectoryHandleName}` + `フォルダへのアクセス許可がありません。アクセスしようとしたフォルダ名: ${maybeDirectoryHandleName}` ) ); } @@ -347,7 +345,7 @@ export const readFileImpl: typeof window[typeof SandboxKey]["readFile"] = ) { return Promise.reject( new Error( - "ディレクトリへのアクセス許可がありません。ファイルの読み書きのために許可が必要です。" + "フォルダへのアクセス許可がありません。ファイルの読み書きのために許可が必要です。" ) ); } @@ -360,9 +358,7 @@ export const readFileImpl: typeof window[typeof SandboxKey]["readFile"] = } if (directoryHandle === undefined) { - return Promise.reject( - new Error("ディレクトリへのアクセス許可がありません") - ); + return Promise.reject(new Error("フォルダへのアクセス許可がありません")); } return directoryHandle.getFileHandle(path).then(async (fileHandle) => { From f0f3185bd9b73714827c19921a53594219c967eb Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 20 Jun 2023 01:38:41 +0900 Subject: [PATCH 43/75] WIP: docs --- ...203\211\343\201\256\346\255\251\343\201\215\346\226\271.md" | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git "a/docs/\343\202\263\343\203\274\343\203\211\343\201\256\346\255\251\343\201\215\346\226\271.md" "b/docs/\343\202\263\343\203\274\343\203\211\343\201\256\346\255\251\343\201\215\346\226\271.md" index 2061fdd230..0d4ec03739 100644 --- "a/docs/\343\202\263\343\203\274\343\203\211\343\201\256\346\255\251\343\201\215\346\226\271.md" +++ "b/docs/\343\202\263\343\203\274\343\203\211\343\201\256\346\255\251\343\201\215\346\226\271.md" @@ -75,13 +75,14 @@ TODO ## ソースコードのディレクトリ構成 - src - - background.ts ・・・ 最初に実行されるコード。ウィンドウを表示したり、エンジンを起動したりする。Electron のメインプロセス。 + - background.ts ・・・ Electron 版で最初に実行されるコード。ウィンドウを表示したり、エンジンを起動したりする。Electron のメインプロセス。 - main.ts ・・・ ウィンドウを表示するために最初に実行されるコード。ここで Vue や Vuex を組み込む。 - App.vue ・・・ Vue のルートになるコンポーネント。他の全てのコンポーネントの親。 - views ディレクトリ ・・・ 画面全体を覆うような Vue コンポーネントのディレクトリ。 - components ディレクトリ ・・・ UI のパーツになる Vue コンポーネントディレクトリ。 - store ディレクトリ ・・・ Vuex のストアのディレクトリ。アプリのロジックの大半はここに書かれる。 - electron ディレクトリ ・・・ Electron の ipc 通信などのコードが置かれるディレクトリ。 + - browser ディレクトリ ・・・ WIP - type ディレクトリ ・・・ TypeScript 用の型定義などが入るディレクトリ。 - styles ディレクトリ ・・・ CSS や SCSS などのディレクトリ。 - infrastructures ディレクトリ ・・・ UI 用のコードと UI 以外のコードを跨ぐときに一枚かませたいときのためのコードのディレクトリ。 From 2cc3b3675a24b1d56b8841bb28c54ae48844349f Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 20 Jun 2023 19:21:55 +0900 Subject: [PATCH 44/75] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=81=AA=E3=81=84SandboxAPI=E3=81=AE=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 10 ---------- src/browser/backgroundImpl.ts | 16 ---------------- src/browser/sandbox.ts | 12 ------------ 3 files changed, 38 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index fb2841b524..d707df425f 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -12,12 +12,10 @@ import { getPrivacyPolicyTextImpl, getQAndATextImpl, getSettingImpl, - getTempDirImpl, getUpdateInfosImpl, hotkeySettingsImpl, isAvailableGpuModeImpl, isMaximizedWindowImpl, - joinPathImpl, logErrorImpl, logInfoImpl, logWarnImpl, @@ -47,10 +45,6 @@ onmessage = (e: MessageEvent) => { return getAppInfosImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) ); - case "GET_TEMP_DIR": - return getTempDirImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); case "GET_HOW_TO_USE_TEXT": return getHowToUseTextImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) @@ -213,10 +207,6 @@ onmessage = (e: MessageEvent) => { console.groupEnd(); postMessage({ type: type, return: [], eventId: e.data.eventId }); break; - case "JOIN_PATH": - return joinPathImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); case "WRITE_FILE": case "READ_FILE": // NOTE: FileI/OはWorker側では処理しない diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index 2fe2807621..a4e49f70f2 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -35,14 +35,6 @@ export const getAppInfosImpl: SandboxImpl["GET_APP_INFOS"] = () => { return Promise.resolve(appInfo); }; -/** - * @deprecated ブラウザ版では使用されていないはずです - */ -export const getTempDirImpl: SandboxImpl["GET_TEMP_DIR"] = () => { - console.error("Not Implemented, it should not be called from VOICEVOX"); - return Promise.resolve(""); -}; - // TODO: base pathを設定できるようにするか、ビルド時埋め込みにする const toStaticPath = (fileName: string) => `/${fileName}`; @@ -246,11 +238,3 @@ export const setEngineSettingImpl: SandboxImpl["SET_ENGINE_SETTING"] = async ([ await setSettingImpl(["engineSettings", engineSettings]); return; }; - -/** - * @deprecated ブラウザ版では使用されていないはずです - */ -export const joinPathImpl: SandboxImpl["JOIN_PATH"] = () => { - console.error("Not Implemented, it should not be called from VOICEVOX"); - return Promise.resolve(""); -}; diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 2fb5e713da..f2c5961dff 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -73,18 +73,6 @@ export const api: typeof window[typeof SandboxKey] = { getAltPortInfos() { return invoker("GET_ALT_PORT_INFOS", []); }, - async saveTempAudioFile(obj: { relativePath: string; buffer: ArrayBuffer }) { - // DELETE_ME: もう使ってなさそう - throw new Error( - `not implemented: saveTempAudioFile is already obsoleted: ${JSON.stringify( - obj - )}` - ); - }, - async loadTempFile() { - // DELETE_ME: もう使ってなさそう - throw new Error(`not implemented: loadTempFile is already obsoleted`); - }, showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { return showAudioSaveDialogImpl(obj); }, From 2e403e62ba7e82e6365cb61d6a82ee787c33e450 Mon Sep 17 00:00:00 2001 From: yamachu Date: Thu, 22 Jun 2023 01:52:39 +0900 Subject: [PATCH 45/75] bye debug console.~ --- src/browser/background.ts | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index d707df425f..5ea559308b 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -110,9 +110,6 @@ onmessage = (e: MessageEvent) => { case "MINIMIZE_WINDOW": case "MAXIMIZE_WINDOW": // NOTE: Browser版ではサポートしない - console.group(type); - console.dir(e.data); - console.groupEnd(); return typedPostMessage(type, void 0, e.data.eventId); case "LOG_ERROR": return logErrorImpl(e.data.args).then((v) => @@ -133,24 +130,15 @@ onmessage = (e: MessageEvent) => { case "RESTART_ENGINE_ALL": case "RESTART_ENGINE": // NOTE: Browser版ではサポートしない - console.group(type); - console.dir(e.data); - console.groupEnd(); return typedPostMessage(type, void 0, e.data.eventId); case "OPEN_ENGINE_DIRECTORY": // NOTE: Browser版ではサポートしない - console.group(type); - console.dir(e.data); - console.groupEnd(); return typedPostMessage(type, void 0, e.data.eventId); case "CHECK_FILE_EXISTS": // NOTE: FileI/OはWorker側では処理しない break; case "CHANGE_PIN_WINDOW": // NOTE: Browser版ではサポートしない - console.group(type); - console.dir(e.data); - console.groupEnd(); return typedPostMessage(type, void 0, e.data.eventId); case "HOTKEY_SETTINGS": return hotkeySettingsImpl(e.data.args).then((v) => @@ -186,25 +174,16 @@ onmessage = (e: MessageEvent) => { ); case "SET_NATIVE_THEME": // NOTE: Browser版ではサポートしない - console.group(type); - console.dir(e.data); - console.groupEnd(); postMessage({ type: type, return: [], eventId: e.data.eventId }); break; case "INSTALL_VVPP_ENGINE": case "UNINSTALL_VVPP_ENGINE": case "VALIDATE_ENGINE_DIR": // NOTE: Browser版ではサポートしない - console.group(type); - console.dir(e.data); - console.groupEnd(); postMessage({ type: type, return: [], eventId: e.data.eventId }); break; case "RESTART_APP": // NOTE: Browser版ではサポートしない - console.group(type); - console.dir(e.data); - console.groupEnd(); postMessage({ type: type, return: [], eventId: e.data.eventId }); break; case "WRITE_FILE": From eecb9fc1a8d2bff9c7853016ea8d2f9fa04eb05c Mon Sep 17 00:00:00 2001 From: yamachu Date: Thu, 22 Jun 2023 01:54:59 +0900 Subject: [PATCH 46/75] =?UTF-8?q?log=E3=81=AEImpl=E3=81=AE=E3=81=BFconsole?= =?UTF-8?q?=E3=81=AF=E8=A8=B1=E5=AE=B9=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/backgroundImpl.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts index a4e49f70f2..c4bf51b2d0 100644 --- a/src/browser/backgroundImpl.ts +++ b/src/browser/backgroundImpl.ts @@ -101,6 +101,7 @@ export const isMaximizedWindowImpl: SandboxImpl["IS_MAXIMIZED_WINDOW"] = () => { return Promise.resolve(true); }; +/* eslint-disable no-console */ // ログの吐き出し先は console ぐらいしかないので、ここでは特例で許可している export const logErrorImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { console.error(...params); return Promise.resolve(); @@ -115,6 +116,7 @@ export const logInfoImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { console.info(...params); return Promise.resolve(); }; +/* eslint-enable no-console */ export const engineInfosImpl: SandboxImpl["ENGINE_INFOS"] = async () => { return [defaultEngine]; From ae2322004df8348484dded121d48414e1b38071a Mon Sep 17 00:00:00 2001 From: yamachu Date: Thu, 22 Jun 2023 02:08:41 +0900 Subject: [PATCH 47/75] =?UTF-8?q?=E5=85=A8=E3=81=A6=E6=8F=A1=E3=82=8A?= =?UTF-8?q?=E6=BD=B0=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=81=AE=E3=81=A8?= =?UTF-8?q?=E4=B8=80=E7=B7=92=E3=81=AA=E3=81=AE=E3=81=A7=E3=80=81=E3=81=9D?= =?UTF-8?q?=E3=81=AE=E3=81=BE=E3=81=BEError=E3=81=AB=E8=90=BD=E3=81=A8?= =?UTF-8?q?=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index ff8e303f38..90c42d0d7a 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -92,9 +92,7 @@ const requestSaveFileNameWithDirectoryPermission = async ({ return undefined; } - await storeDirectoryHandle(_directoryHandler).catch((e) => - console.error(e) - ); + await storeDirectoryHandle(_directoryHandler); updateLatestSelectedDirectoryHandle(_directoryHandler); } @@ -124,9 +122,7 @@ const requestLoadFileNameWithDirectoryPermission = async ({ return undefined; } - await storeDirectoryHandle(_directoryHandler).catch((e) => - console.error(e) - ); + await storeDirectoryHandle(_directoryHandler); updateLatestSelectedDirectoryHandle(_directoryHandler); } @@ -170,9 +166,7 @@ export const showOpenDirectoryDialogImpl: typeof window[typeof SandboxKey]["show return undefined; } - await storeDirectoryHandle(_directoryHandler).catch((e) => - console.error(e) - ); + await storeDirectoryHandle(_directoryHandler); // NOTE: 同一のディレクトリ名だった場合、後で選択されたディレクトリがそれ移行の処理で使用されるため、意図しない保存が発生するかもしれない directoryHandleMap.set(_directoryHandler.name, _directoryHandler); @@ -234,9 +228,8 @@ const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { reject(request.error); }; } - ).catch((e) => { - console.error(e); - // 握り潰してる + ).catch(() => { + // FIXME: 握り潰してる return undefined; }); }; @@ -304,8 +297,6 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = }) .then(() => undefined) .catch((e) => { - // FIXME - console.error(e); return { code: undefined, message: e.message as string, From 7b377bf2f092a9e97a49435e03c3bd6a3c547601 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 24 Jun 2023 02:20:24 +0900 Subject: [PATCH 48/75] =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=82=E3=81=AE=E3=81=AFcase=E3=81=8B=E3=82=89?= =?UTF-8?q?=E6=8E=92=E9=99=A4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/background.ts | 51 ++------------------------------------- 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/src/browser/background.ts b/src/browser/background.ts index 5ea559308b..009fd93c45 100644 --- a/src/browser/background.ts +++ b/src/browser/background.ts @@ -81,19 +81,6 @@ onmessage = (e: MessageEvent) => { return getAltPortInfosImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) ); - case "SHOW_AUDIO_SAVE_DIALOG": - case "SHOW_TEXT_SAVE_DIALOG": - case "SHOW_VVPP_OPEN_DIALOG": - case "SHOW_OPEN_DIRECTORY_DIALOG": - case "SHOW_IMPORT_FILE_DIALOG": - case "SHOW_PROJECT_SAVE_DIALOG": - case "SHOW_PROJECT_LOAD_DIALOG": - case "SHOW_MESSAGE_DIALOG": - case "SHOW_QUESTION_DIALOG": - case "SHOW_WARNING_DIALOG": - case "SHOW_ERROR_DIALOG": - // NOTE: DialogはWorker側では処理しない - break; case "OPEN_TEXT_EDIT_CONTEXT_MENU": return openTextEditContextMenuImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) @@ -106,11 +93,6 @@ onmessage = (e: MessageEvent) => { return isMaximizedWindowImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) ); - case "CLOSE_WINDOW": - case "MINIMIZE_WINDOW": - case "MAXIMIZE_WINDOW": - // NOTE: Browser版ではサポートしない - return typedPostMessage(type, void 0, e.data.eventId); case "LOG_ERROR": return logErrorImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) @@ -127,19 +109,6 @@ onmessage = (e: MessageEvent) => { return engineInfosImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) ); - case "RESTART_ENGINE_ALL": - case "RESTART_ENGINE": - // NOTE: Browser版ではサポートしない - return typedPostMessage(type, void 0, e.data.eventId); - case "OPEN_ENGINE_DIRECTORY": - // NOTE: Browser版ではサポートしない - return typedPostMessage(type, void 0, e.data.eventId); - case "CHECK_FILE_EXISTS": - // NOTE: FileI/OはWorker側では処理しない - break; - case "CHANGE_PIN_WINDOW": - // NOTE: Browser版ではサポートしない - return typedPostMessage(type, void 0, e.data.eventId); case "HOTKEY_SETTINGS": return hotkeySettingsImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) @@ -172,23 +141,7 @@ onmessage = (e: MessageEvent) => { return setEngineSettingImpl(e.data.args).then((v) => typedPostMessage(type, v, e.data.eventId) ); - case "SET_NATIVE_THEME": - // NOTE: Browser版ではサポートしない - postMessage({ type: type, return: [], eventId: e.data.eventId }); - break; - case "INSTALL_VVPP_ENGINE": - case "UNINSTALL_VVPP_ENGINE": - case "VALIDATE_ENGINE_DIR": - // NOTE: Browser版ではサポートしない - postMessage({ type: type, return: [], eventId: e.data.eventId }); - break; - case "RESTART_APP": - // NOTE: Browser版ではサポートしない - postMessage({ type: type, return: [], eventId: e.data.eventId }); - break; - case "WRITE_FILE": - case "READ_FILE": - // NOTE: FileI/OはWorker側では処理しない - break; + default: + throw new Error(`Not implemented: ${type}`); } }; From 9f01ec8c8de572e14069d50da380a04d7218f304 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 24 Jun 2023 02:22:26 +0900 Subject: [PATCH 49/75] =?UTF-8?q?NativeTheme=E3=81=AF=E5=AF=BE=E5=BF=9C?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E3=81=8C=E3=81=82=E3=82=8A=E3=81=9D=E3=81=86?= =?UTF-8?q?=E3=81=A0=E3=81=A3=E3=81=9F=E3=82=89=E4=BB=8A=E5=BE=8C=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index f2c5961dff..5b070678fb 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -17,7 +17,6 @@ import { EngineId, EngineSetting, HotkeySetting, - NativeThemeType, SandboxKey, } from "@/type/preload"; @@ -186,8 +185,9 @@ export const api: typeof window[typeof SandboxKey] = { getDefaultToolbarSetting() { return invoker("GET_DEFAULT_TOOLBAR_SETTING", []); }, - setNativeTheme(source: NativeThemeType) { - return invoker("SET_NATIVE_THEME", [source]); + setNativeTheme(/* source: NativeThemeType */) { + // TODO: Impl + return; }, theme(newData?: string) { return invoker("THEME", [{ newData }]); From f30d86cf762e8a4e4dbb4c6bf7a859812cd3f3fe Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 24 Jun 2023 03:04:21 +0900 Subject: [PATCH 50/75] bye worker --- src/browser/background.ts | 147 ------------------------------------- src/browser/sandbox.ts | 150 +++++++++++++++++++------------------- src/browser/type.ts | 17 ----- 3 files changed, 74 insertions(+), 240 deletions(-) delete mode 100644 src/browser/background.ts delete mode 100644 src/browser/type.ts diff --git a/src/browser/background.ts b/src/browser/background.ts deleted file mode 100644 index 009fd93c45..0000000000 --- a/src/browser/background.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { - engineInfosImpl, - getAltPortInfosImpl, - getAppInfosImpl, - getContactTextImpl, - getDefaultHotkeySettingsImpl, - getDefaultToolbarSettingImpl, - getHowToUseTextImpl, - getOssCommunityInfosImpl, - getOssLicensesImpl, - getPolicyTextImpl, - getPrivacyPolicyTextImpl, - getQAndATextImpl, - getSettingImpl, - getUpdateInfosImpl, - hotkeySettingsImpl, - isAvailableGpuModeImpl, - isMaximizedWindowImpl, - logErrorImpl, - logInfoImpl, - logWarnImpl, - onVuexReadyImpl, - openTextEditContextMenuImpl, - setEngineSettingImpl, - setSettingImpl, - themeImpl, -} from "./backgroundImpl"; -import type { MainToWorkerMessage } from "./type"; -import type { IpcIHData } from "@/type/ipc"; - -type MessageReturnTypes = { [K in keyof IpcIHData]: IpcIHData[K]["return"] }; - -const typedPostMessage = ( - type: K, - message: MessageReturnTypes[K], - eventId: string -) => { - postMessage({ type, return: message, eventId }); -}; - -onmessage = (e: MessageEvent) => { - const type = e.data.type; - switch (type) { - case "GET_APP_INFOS": - return getAppInfosImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_HOW_TO_USE_TEXT": - return getHowToUseTextImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_POLICY_TEXT": - return getPolicyTextImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_OSS_LICENSES": - return getOssLicensesImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_UPDATE_INFOS": - return getUpdateInfosImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_OSS_COMMUNITY_INFOS": - return getOssCommunityInfosImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_CONTACT_TEXT": - return getContactTextImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_Q_AND_A_TEXT": - return getQAndATextImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_PRIVACY_POLICY_TEXT": - return getPrivacyPolicyTextImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_ALT_PORT_INFOS": - return getAltPortInfosImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "OPEN_TEXT_EDIT_CONTEXT_MENU": - return openTextEditContextMenuImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "IS_AVAILABLE_GPU_MODE": - return isAvailableGpuModeImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "IS_MAXIMIZED_WINDOW": - return isMaximizedWindowImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "LOG_ERROR": - return logErrorImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "LOG_WARN": - return logWarnImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "LOG_INFO": - return logInfoImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "ENGINE_INFOS": - return engineInfosImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "HOTKEY_SETTINGS": - return hotkeySettingsImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_DEFAULT_HOTKEY_SETTINGS": - return getDefaultHotkeySettingsImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_DEFAULT_TOOLBAR_SETTING": - return getDefaultToolbarSettingImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "THEME": - return themeImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "ON_VUEX_READY": - return onVuexReadyImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "GET_SETTING": - return getSettingImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "SET_SETTING": - return setSettingImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - case "SET_ENGINE_SETTING": - return setEngineSettingImpl(e.data.args).then((v) => - typedPostMessage(type, v, e.data.eventId) - ); - default: - throw new Error(`Not implemented: ${type}`); - } -}; diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 5b070678fb..d0825bab56 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -1,4 +1,29 @@ -import { v4 } from "uuid"; +import { + engineInfosImpl, + getAltPortInfosImpl, + getAppInfosImpl, + getContactTextImpl, + getDefaultHotkeySettingsImpl, + getDefaultToolbarSettingImpl, + getHowToUseTextImpl, + getOssCommunityInfosImpl, + getOssLicensesImpl, + getPolicyTextImpl, + getPrivacyPolicyTextImpl, + getQAndATextImpl, + getSettingImpl, + getUpdateInfosImpl, + hotkeySettingsImpl, + isAvailableGpuModeImpl, + isMaximizedWindowImpl, + logErrorImpl, + logInfoImpl, + logWarnImpl, + onVuexReadyImpl, + openTextEditContextMenuImpl, + setSettingImpl, + themeImpl, +} from "./backgroundImpl"; import { checkFileExistsImpl, readFileImpl, @@ -10,67 +35,40 @@ import { showTextSaveDialogImpl, writeFileImpl, } from "./fileImpl"; -import type { WorkerToMainMessage } from "./type"; -import { IpcIHData, IpcSOData } from "@/type/ipc"; -import { - ElectronStoreType, - EngineId, - EngineSetting, - HotkeySetting, - SandboxKey, -} from "@/type/preload"; -const worker = new Worker(new URL("./background.ts", import.meta.url), { - type: "module", -}); - -const invoker = ( - type: K, - args: IpcIHData[K]["args"] -): Promise => { - return new Promise((resolve) => { - const eventId = v4(); - const cb = (ev: MessageEvent) => { - if (ev.data.type === type && ev.data.eventId === eventId) { - worker.removeEventListener("message", cb); - resolve(ev.data.return); - } - }; - worker.addEventListener("message", cb); // 他のeventが届いた時にresolveしない様に、onceは使用していない - worker.postMessage({ type, args, eventId } /** MainToWorkerMessage */); - }); -}; +import { IpcSOData } from "@/type/ipc"; +import { ElectronStoreType, HotkeySetting, SandboxKey } from "@/type/preload"; export const api: typeof window[typeof SandboxKey] = { getAppInfos() { - return invoker("GET_APP_INFOS", []); + return getAppInfosImpl([]); }, getHowToUseText() { - return invoker("GET_HOW_TO_USE_TEXT", []); + return getHowToUseTextImpl([]); }, getPolicyText() { - return invoker("GET_POLICY_TEXT", []); + return getPolicyTextImpl([]); }, getOssLicenses() { - return invoker("GET_OSS_LICENSES", []); + return getOssLicensesImpl([]); }, getUpdateInfos() { - return invoker("GET_UPDATE_INFOS", []); + return getUpdateInfosImpl([]); }, getOssCommunityInfos() { - return invoker("GET_OSS_COMMUNITY_INFOS", []); + return getOssCommunityInfosImpl([]); }, getQAndAText() { - return invoker("GET_Q_AND_A_TEXT", []); + return getQAndATextImpl([]); }, getContactText() { - return invoker("GET_CONTACT_TEXT", []); + return getContactTextImpl([]); }, getPrivacyPolicyText() { - return invoker("GET_PRIVACY_POLICY_TEXT", []); + return getPrivacyPolicyTextImpl([]); }, - getAltPortInfos() { - return invoker("GET_ALT_PORT_INFOS", []); + async getAltPortInfos() { + return getAltPortInfosImpl([]); }, showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { return showAudioSaveDialogImpl(obj); @@ -113,7 +111,9 @@ export const api: typeof window[typeof SandboxKey] = { }) { // FIXME // TODO: 例えば動的にdialog要素をDOMに生成して、それを表示させるみたいのはあるかもしれない - return invoker("SHOW_QUESTION_DIALOG", [obj]); + throw new Error( + `Not implemented: showQuestionDialog, request: ${JSON.stringify(obj)}` + ); }, showImportFileDialog(obj: { title: string }) { return showImportFileDialogImpl(obj); @@ -125,13 +125,13 @@ export const api: typeof window[typeof SandboxKey] = { return readFileImpl(obj); }, openTextEditContextMenu() { - return invoker("OPEN_TEXT_EDIT_CONTEXT_MENU", []); + return openTextEditContextMenuImpl([]); }, isAvailableGPUMode() { - return invoker("IS_AVAILABLE_GPU_MODE", []); + return isAvailableGpuModeImpl([]); }, isMaximizedWindow() { - return invoker("IS_MAXIMIZED_WINDOW", []); + return isMaximizedWindowImpl([]); }, onReceivedIPCMsg( channel: T, @@ -144,81 +144,79 @@ export const api: typeof window[typeof SandboxKey] = { }); }, closeWindow() { - return invoker("CLOSE_WINDOW", []); + throw new Error(`Not supported on Browser version: closeWindow`); }, minimizeWindow() { - return invoker("MINIMIZE_WINDOW", []); + throw new Error(`Not supported on Browser version: minimizeWindow`); }, maximizeWindow() { - return invoker("MAXIMIZE_WINDOW", []); + throw new Error(`Not supported on Browser version: maximizeWindow`); }, logError(...params: unknown[]) { - return invoker("LOG_ERROR", [params]); + return logErrorImpl(params); }, logWarn(...params: unknown[]) { - return invoker("LOG_WARN", [params]); + return logWarnImpl(params); }, logInfo(...params: unknown[]) { - return invoker("LOG_INFO", [params]); + return logInfoImpl(params); }, engineInfos() { - return invoker("ENGINE_INFOS", []); + return engineInfosImpl([]); }, - restartEngine(engineId: EngineId) { - return invoker("RESTART_ENGINE", [{ engineId }]); + restartEngine(/* engineId: EngineId */) { + throw new Error(`Not supported on Browser version: restartEngine`); }, - openEngineDirectory(engineId: EngineId) { - return invoker("OPEN_ENGINE_DIRECTORY", [{ engineId }]); + openEngineDirectory(/* engineId: EngineId */) { + throw new Error(`Not supported on Browser version: openEngineDirectory`); }, hotkeySettings(newData?: HotkeySetting) { - return invoker("HOTKEY_SETTINGS", [{ newData }]); + return hotkeySettingsImpl([{ newData }]); }, checkFileExists(file: string) { return checkFileExistsImpl(file); }, changePinWindow() { - return invoker("CHANGE_PIN_WINDOW", []); + throw new Error(`Not supported on Browser version: changePinWindow`); }, getDefaultHotkeySettings() { - return invoker("GET_DEFAULT_HOTKEY_SETTINGS", []); + return getDefaultHotkeySettingsImpl([]); }, getDefaultToolbarSetting() { - return invoker("GET_DEFAULT_TOOLBAR_SETTING", []); + return getDefaultToolbarSettingImpl([]); }, setNativeTheme(/* source: NativeThemeType */) { // TODO: Impl return; }, theme(newData?: string) { - return invoker("THEME", [{ newData }]); + return themeImpl([{ newData }]); }, vuexReady() { - return invoker("ON_VUEX_READY", []); + return onVuexReadyImpl([]); }, getSetting(key: Key) { - return invoker("GET_SETTING", [key]) as Promise< - ElectronStoreType[typeof key] - >; + return getSettingImpl([key]) as Promise; }, setSetting( key: Key, newValue: ElectronStoreType[Key] ) { - return invoker("SET_SETTING", [key, newValue]) as Promise; + return setSettingImpl([key, newValue]) as Promise; }, - setEngineSetting(engineId: EngineId, engineSetting: EngineSetting) { - return invoker("SET_ENGINE_SETTING", [engineId, engineSetting]); + setEngineSetting(/* engineId: EngineId, engineSetting: EngineSetting */) { + throw new Error(`Not supported on Browser version: setEngineSetting`); }, - async installVvppEngine(path: string) { - return invoker("INSTALL_VVPP_ENGINE", [path]); + installVvppEngine(/* path: string */) { + throw new Error(`Not supported on Browser version: installVvppEngine`); }, - async uninstallVvppEngine(engineId: EngineId) { - return invoker("UNINSTALL_VVPP_ENGINE", [engineId]); + uninstallVvppEngine(/* engineId: EngineId */) { + throw new Error(`Not supported on Browser version: uninstallVvppEngine`); }, - validateEngineDir(engineDir: string) { - return invoker("VALIDATE_ENGINE_DIR", [{ engineDir }]); + validateEngineDir(/* engineDir: string */) { + throw new Error(`Not supported on Browser version: validateEngineDir`); }, - restartApp(obj: { isMultiEngineOffMode: boolean }) { - return invoker("RESTART_APP", [obj]); + restartApp(/* obj: { isMultiEngineOffMode: boolean } */) { + throw new Error(`Not supported on Browser version: restartApp`); }, }; diff --git a/src/browser/type.ts b/src/browser/type.ts deleted file mode 100644 index 59313624d5..0000000000 --- a/src/browser/type.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IpcIHData } from "@/type/ipc"; - -export type WorkerToMainMessage = { - [K in keyof IpcIHData]: { - type: K; - return: IpcIHData[K]["return"]; - eventId: string; - }; -}[keyof IpcIHData]; - -export type MainToWorkerMessage = { - [K in keyof IpcIHData]: { - type: K; - args: IpcIHData[K]["args"]; - eventId: string; - }; -}[keyof IpcIHData]; From 0df39599c8d9f925cd306462d6eb507956f006df Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 24 Jun 2023 03:19:23 +0900 Subject: [PATCH 51/75] bye backgroundImpl --- src/browser/backgroundImpl.ts | 242 ---------------------------------- src/browser/sandbox.ts | 209 +++++++++++++++++++++-------- 2 files changed, 151 insertions(+), 300 deletions(-) delete mode 100644 src/browser/backgroundImpl.ts diff --git a/src/browser/backgroundImpl.ts b/src/browser/backgroundImpl.ts deleted file mode 100644 index c4bf51b2d0..0000000000 --- a/src/browser/backgroundImpl.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { defaultEngine } from "./contract"; -import { entryKey, openDB } from "./store"; -import type { IpcIHData } from "@/type/ipc"; -import { - EngineSettings, - ThemeConf, - defaultHotkeySettings, - defaultToolbarButtonSetting, - electronStoreSchema, -} from "@/type/preload"; -import { - ContactTextFileName, - HowToUseTextFileName, - OssCommunityInfosFileName, - OssLicensesJsonFileName, - PolicyTextFileName, - PrivacyPolicyTextFileName, - QAndATextFileName, - UpdateInfosJsonFileName, -} from "@/type/staticResources"; - -type SandboxImpl = { - [K in keyof IpcIHData]: ( - args: IpcIHData[K]["args"] - ) => Promise; -}; - -export const getAppInfosImpl: SandboxImpl["GET_APP_INFOS"] = () => { - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - const appInfo = { - name: process.env.APP_NAME!, - version: process.env.APP_VERSION!, - }; - /* eslint-enable @typescript-eslint/no-non-null-assertion */ - return Promise.resolve(appInfo); -}; - -// TODO: base pathを設定できるようにするか、ビルド時埋め込みにする -const toStaticPath = (fileName: string) => `/${fileName}`; - -export const getHowToUseTextImpl: SandboxImpl["GET_HOW_TO_USE_TEXT"] = () => { - return fetch(toStaticPath(HowToUseTextFileName)).then((v) => v.text()); -}; - -export const getPolicyTextImpl: SandboxImpl["GET_POLICY_TEXT"] = () => { - return fetch(toStaticPath(PolicyTextFileName)).then((v) => v.text()); -}; - -export const getOssCommunityInfosImpl: SandboxImpl["GET_OSS_COMMUNITY_INFOS"] = - () => { - return fetch(toStaticPath(OssCommunityInfosFileName)).then((v) => v.text()); - }; - -export const getContactTextImpl: SandboxImpl["GET_CONTACT_TEXT"] = () => { - return fetch(toStaticPath(ContactTextFileName)).then((v) => v.text()); -}; - -export const getQAndATextImpl: SandboxImpl["GET_Q_AND_A_TEXT"] = () => { - return fetch(toStaticPath(QAndATextFileName)).then((v) => v.text()); -}; - -export const getPrivacyPolicyTextImpl: SandboxImpl["GET_PRIVACY_POLICY_TEXT"] = - () => { - return fetch(toStaticPath(PrivacyPolicyTextFileName)).then((v) => v.text()); - }; - -export const getOssLicensesImpl: SandboxImpl["GET_OSS_LICENSES"] = () => { - return fetch(toStaticPath(OssLicensesJsonFileName)).then((v) => v.json()); -}; - -export const getUpdateInfosImpl: SandboxImpl["GET_UPDATE_INFOS"] = () => { - return fetch(toStaticPath(UpdateInfosJsonFileName)).then((v) => v.json()); -}; - -/** - * @deprecated ブラウザ版ではサポートされていません - */ -export const getAltPortInfosImpl: SandboxImpl["GET_ALT_PORT_INFOS"] = () => { - return Promise.resolve({}); -}; - -/** - * @deprecated ブラウザ版では不要です - */ -export const openTextEditContextMenuImpl: SandboxImpl["OPEN_TEXT_EDIT_CONTEXT_MENU"] = - () => { - return Promise.resolve(); - }; - -/** - * @fixme canvasでWebGLから調べたり、WebGPUがサポートされているかを調べたりで判断は出来そう - * @todo WebAssembly版をサポートする時に実装する - */ -export const isAvailableGpuModeImpl: SandboxImpl["IS_AVAILABLE_GPU_MODE"] = - () => { - return Promise.resolve(false); - }; - -// NOTE: UIの表示状態の制御のためだけなので固定値を返している -export const isMaximizedWindowImpl: SandboxImpl["IS_MAXIMIZED_WINDOW"] = () => { - return Promise.resolve(true); -}; - -/* eslint-disable no-console */ // ログの吐き出し先は console ぐらいしかないので、ここでは特例で許可している -export const logErrorImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { - console.error(...params); - return Promise.resolve(); -}; - -export const logWarnImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { - console.warn(...params); - return Promise.resolve(); -}; - -export const logInfoImpl: SandboxImpl["LOG_ERROR"] = ([...params]) => { - console.info(...params); - return Promise.resolve(); -}; -/* eslint-enable no-console */ - -export const engineInfosImpl: SandboxImpl["ENGINE_INFOS"] = async () => { - return [defaultEngine]; -}; - -export const hotkeySettingsImpl: SandboxImpl["HOTKEY_SETTINGS"] = async ([ - { newData }, -]) => { - type HotkeySettingType = ReturnType< - typeof electronStoreSchema["parse"] - >["hotkeySettings"]; - if (newData !== undefined) { - const hotkeySettings = (await getSettingImpl([ - "hotkeySettings", - ])) as HotkeySettingType; - const hotkeySetting = hotkeySettings.find( - (hotkey) => hotkey.action == newData.action - ); - if (hotkeySetting !== undefined) { - hotkeySetting.combination = newData.combination; - } - await setSettingImpl(["hotkeySettings", hotkeySettings]); - } - return getSettingImpl(["hotkeySettings"]) as Promise; -}; - -export const getDefaultHotkeySettingsImpl: SandboxImpl["GET_DEFAULT_HOTKEY_SETTINGS"] = - () => { - return Promise.resolve(defaultHotkeySettings); - }; - -export const getDefaultToolbarSettingImpl: SandboxImpl["GET_DEFAULT_TOOLBAR_SETTING"] = - () => { - return Promise.resolve(defaultToolbarButtonSetting); - }; - -export const themeImpl: SandboxImpl["THEME"] = async ([{ newData }]) => { - if (newData !== undefined) { - await setSettingImpl(["currentTheme", newData]); - return; - } - return Promise.all( - // FIXME: themeファイルのいい感じのパスの設定 - ["/themes/default.json", "/themes/dark.json"].map((url) => - fetch(url).then((res) => res.json()) - ) - ) - .then((v) => ({ - currentTheme: "Default", - availableThemes: v, - })) - .then((v) => - getSettingImpl(["currentTheme"]).then( - (currentTheme) => - ({ - ...v, - currentTheme, - } as { currentTheme: string; availableThemes: ThemeConf[] }) - ) - ); -}; - -export const onVuexReadyImpl: SandboxImpl["ON_VUEX_READY"] = async () => { - // NOTE: 何もしなくて良さそう - return Promise.resolve(); -}; - -export const getSettingImpl: SandboxImpl["GET_SETTING"] = async ([key]) => { - const db = await openDB(); - - return new Promise((resolve, reject) => { - const transaction = db.transaction(key, "readonly"); - const store = transaction.objectStore(key); - const request = store.get(entryKey); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }); -}; - -export const setSettingImpl: SandboxImpl["SET_SETTING"] = async ([ - key, - value, -]) => { - const db = await openDB(); - - // TODO: Schemaに合っているか保存時にvalidationしたい - return new Promise((resolve, reject) => { - const transaction = db.transaction(key, "readwrite"); - const store = transaction.objectStore(key); - const request = store.put(value, entryKey); - request.onsuccess = () => { - const readRequest = db - .transaction(key, "readonly") - .objectStore(key) - .get(entryKey); - readRequest.onsuccess = () => { - resolve(readRequest.result); - }; - readRequest.onerror = () => { - reject(readRequest.error); - }; - }; - request.onerror = () => { - reject(request.error); - }; - }); -}; - -export const setEngineSettingImpl: SandboxImpl["SET_ENGINE_SETTING"] = async ([ - engineId, - newData, -]) => { - const engineSettings = (await getSettingImpl([ - "engineSettings", - ])) as EngineSettings; - engineSettings[engineId] = newData; - await setSettingImpl(["engineSettings", engineSettings]); - return; -}; diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index d0825bab56..b573917202 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -1,29 +1,4 @@ -import { - engineInfosImpl, - getAltPortInfosImpl, - getAppInfosImpl, - getContactTextImpl, - getDefaultHotkeySettingsImpl, - getDefaultToolbarSettingImpl, - getHowToUseTextImpl, - getOssCommunityInfosImpl, - getOssLicensesImpl, - getPolicyTextImpl, - getPrivacyPolicyTextImpl, - getQAndATextImpl, - getSettingImpl, - getUpdateInfosImpl, - hotkeySettingsImpl, - isAvailableGpuModeImpl, - isMaximizedWindowImpl, - logErrorImpl, - logInfoImpl, - logWarnImpl, - onVuexReadyImpl, - openTextEditContextMenuImpl, - setSettingImpl, - themeImpl, -} from "./backgroundImpl"; +import { defaultEngine } from "./contract"; import { checkFileExistsImpl, readFileImpl, @@ -35,40 +10,72 @@ import { showTextSaveDialogImpl, writeFileImpl, } from "./fileImpl"; +import { entryKey, openDB } from "./store"; import { IpcSOData } from "@/type/ipc"; -import { ElectronStoreType, HotkeySetting, SandboxKey } from "@/type/preload"; +import { + defaultHotkeySettings, + defaultToolbarButtonSetting, + electronStoreSchema, + ElectronStoreType, + EngineId, + EngineSetting, + EngineSettings, + HotkeySetting, + SandboxKey, + ThemeConf, +} from "@/type/preload"; +import { + ContactTextFileName, + HowToUseTextFileName, + OssCommunityInfosFileName, + OssLicensesJsonFileName, + PolicyTextFileName, + PrivacyPolicyTextFileName, + QAndATextFileName, + UpdateInfosJsonFileName, +} from "@/type/staticResources"; + +// TODO: base pathを設定できるようにするか、ビルド時埋め込みにする +const toStaticPath = (fileName: string) => `/${fileName}`; export const api: typeof window[typeof SandboxKey] = { getAppInfos() { - return getAppInfosImpl([]); + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + const appInfo = { + name: process.env.APP_NAME!, + version: process.env.APP_VERSION!, + }; + /* eslint-enable @typescript-eslint/no-non-null-assertion */ + return Promise.resolve(appInfo); }, getHowToUseText() { - return getHowToUseTextImpl([]); + return fetch(toStaticPath(HowToUseTextFileName)).then((v) => v.text()); }, getPolicyText() { - return getPolicyTextImpl([]); + return fetch(toStaticPath(PolicyTextFileName)).then((v) => v.text()); }, getOssLicenses() { - return getOssLicensesImpl([]); + return fetch(toStaticPath(OssLicensesJsonFileName)).then((v) => v.json()); }, getUpdateInfos() { - return getUpdateInfosImpl([]); + return fetch(toStaticPath(UpdateInfosJsonFileName)).then((v) => v.json()); }, getOssCommunityInfos() { - return getOssCommunityInfosImpl([]); + return fetch(toStaticPath(OssCommunityInfosFileName)).then((v) => v.text()); }, getQAndAText() { - return getQAndATextImpl([]); + return fetch(toStaticPath(QAndATextFileName)).then((v) => v.text()); }, getContactText() { - return getContactTextImpl([]); + return fetch(toStaticPath(ContactTextFileName)).then((v) => v.text()); }, getPrivacyPolicyText() { - return getPrivacyPolicyTextImpl([]); + return fetch(toStaticPath(PrivacyPolicyTextFileName)).then((v) => v.text()); }, - async getAltPortInfos() { - return getAltPortInfosImpl([]); + getAltPortInfos() { + // NOTE: ブラウザ版ではサポートされていません + return Promise.resolve({}); }, showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { return showAudioSaveDialogImpl(obj); @@ -125,13 +132,17 @@ export const api: typeof window[typeof SandboxKey] = { return readFileImpl(obj); }, openTextEditContextMenu() { - return openTextEditContextMenuImpl([]); + // NOTE: ブラウザ版では不要 + return Promise.resolve(); }, isAvailableGPUMode() { - return isAvailableGpuModeImpl([]); + // TODO: WebAssembly版をサポートする時に実装する + // FIXME: canvasでWebGLから調べたり、WebGPUがサポートされているかを調べたりで判断は出来そう + return Promise.resolve(false); }, isMaximizedWindow() { - return isMaximizedWindowImpl([]); + // NOTE: UIの表示状態の制御のためだけなので固定値を返している + return Promise.resolve(true); }, onReceivedIPCMsg( channel: T, @@ -152,17 +163,22 @@ export const api: typeof window[typeof SandboxKey] = { maximizeWindow() { throw new Error(`Not supported on Browser version: maximizeWindow`); }, + /* eslint-disable no-console */ // ログの吐き出し先は console ぐらいしかないので、ここでは特例で許可している logError(...params: unknown[]) { - return logErrorImpl(params); + console.error(...params); + return; }, logWarn(...params: unknown[]) { - return logWarnImpl(params); + console.warn(...params); + return; }, logInfo(...params: unknown[]) { - return logInfoImpl(params); + console.info(...params); + return; }, + /* eslint-enable no-console */ engineInfos() { - return engineInfosImpl([]); + return Promise.resolve([defaultEngine]); }, restartEngine(/* engineId: EngineId */) { throw new Error(`Not supported on Browser version: restartEngine`); @@ -170,8 +186,23 @@ export const api: typeof window[typeof SandboxKey] = { openEngineDirectory(/* engineId: EngineId */) { throw new Error(`Not supported on Browser version: openEngineDirectory`); }, - hotkeySettings(newData?: HotkeySetting) { - return hotkeySettingsImpl([{ newData }]); + async hotkeySettings(newData?: HotkeySetting) { + type HotkeySettingType = ReturnType< + typeof electronStoreSchema["parse"] + >["hotkeySettings"]; + if (newData !== undefined) { + const hotkeySettings = (await this.getSetting( + "hotkeySettings" + )) as HotkeySettingType; + const hotkeySetting = hotkeySettings.find( + (hotkey) => hotkey.action == newData.action + ); + if (hotkeySetting !== undefined) { + hotkeySetting.combination = newData.combination; + } + await this.setSetting("hotkeySettings", hotkeySettings); + } + return this.getSetting("hotkeySettings") as Promise; }, checkFileExists(file: string) { return checkFileExistsImpl(file); @@ -180,32 +211,94 @@ export const api: typeof window[typeof SandboxKey] = { throw new Error(`Not supported on Browser version: changePinWindow`); }, getDefaultHotkeySettings() { - return getDefaultHotkeySettingsImpl([]); + return Promise.resolve(defaultHotkeySettings); }, getDefaultToolbarSetting() { - return getDefaultToolbarSettingImpl([]); + return Promise.resolve(defaultToolbarButtonSetting); }, setNativeTheme(/* source: NativeThemeType */) { // TODO: Impl return; }, - theme(newData?: string) { - return themeImpl([{ newData }]); + async theme(newData?: string) { + if (newData !== undefined) { + await this.setSetting("currentTheme", newData); + return; + } + return Promise.all( + // FIXME: themeファイルのいい感じのパスの設定 + ["/themes/default.json", "/themes/dark.json"].map((url) => + fetch(url).then((res) => res.json()) + ) + ) + .then((v) => ({ + currentTheme: "Default", + availableThemes: v, + })) + .then((v) => + this.getSetting("currentTheme").then( + (currentTheme) => + ({ + ...v, + currentTheme, + } as { currentTheme: string; availableThemes: ThemeConf[] }) + ) + ); }, vuexReady() { - return onVuexReadyImpl([]); + // NOTE: 何もしなくて良さそう + return Promise.resolve(); }, - getSetting(key: Key) { - return getSettingImpl([key]) as Promise; + async getSetting(key: Key) { + const db = await openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction(key, "readonly"); + const store = transaction.objectStore(key); + const request = store.get(entryKey); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }); }, - setSetting( + async setSetting( key: Key, newValue: ElectronStoreType[Key] ) { - return setSettingImpl([key, newValue]) as Promise; + const db = await openDB(); + + // TODO: Schemaに合っているか保存時にvalidationしたい + return new Promise((resolve, reject) => { + const transaction = db.transaction(key, "readwrite"); + const store = transaction.objectStore(key); + const request = store.put(newValue, entryKey); + request.onsuccess = () => { + const readRequest = db + .transaction(key, "readonly") + .objectStore(key) + .get(entryKey); + readRequest.onsuccess = () => { + resolve(readRequest.result); + }; + readRequest.onerror = () => { + reject(readRequest.error); + }; + }; + request.onerror = () => { + reject(request.error); + }; + }); }, - setEngineSetting(/* engineId: EngineId, engineSetting: EngineSetting */) { - throw new Error(`Not supported on Browser version: setEngineSetting`); + async setEngineSetting(engineId: EngineId, engineSetting: EngineSetting) { + const engineSettings = (await this.getSetting( + "engineSettings" + )) as EngineSettings; + engineSettings[engineId] = engineSetting; + await this.setSetting("engineSettings", engineSettings); + return; }, installVvppEngine(/* path: string */) { throw new Error(`Not supported on Browser version: installVvppEngine`); From 2d40feff649ebfa4d6a35aaf25a120816939765c Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 24 Jun 2023 03:20:23 +0900 Subject: [PATCH 52/75] store -> storeImpl --- src/browser/fileImpl.ts | 2 +- src/browser/sandbox.ts | 2 +- src/browser/{store.ts => storeImpl.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/browser/{store.ts => storeImpl.ts} (100%) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 90c42d0d7a..845af4b093 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -1,6 +1,6 @@ import { sep } from "path"; import { directoryHandleStoreKey } from "./contract"; -import { openDB } from "./store"; +import { openDB } from "./storeImpl"; import { SandboxKey, WriteFileErrorResult } from "@/type/preload"; const showWritableDirectoryPicker = async (): Promise< diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index b573917202..6034ba2264 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -10,7 +10,7 @@ import { showTextSaveDialogImpl, writeFileImpl, } from "./fileImpl"; -import { entryKey, openDB } from "./store"; +import { entryKey, openDB } from "./storeImpl"; import { IpcSOData } from "@/type/ipc"; import { diff --git a/src/browser/store.ts b/src/browser/storeImpl.ts similarity index 100% rename from src/browser/store.ts rename to src/browser/storeImpl.ts From fc4561df0d2b69a2a42dfb86d9350345421bd523 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 24 Jun 2023 03:31:46 +0900 Subject: [PATCH 53/75] =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E5=91=A8=E3=82=8A?= =?UTF-8?q?=E3=81=AE=E5=87=A6=E7=90=86=E3=81=AFstoreImpl=E3=81=AB=E5=88=87?= =?UTF-8?q?=E3=82=8A=E5=87=BA=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 48 +++++----------------------------------- src/browser/storeImpl.ts | 42 ++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 6034ba2264..5b63a545ff 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -10,14 +10,13 @@ import { showTextSaveDialogImpl, writeFileImpl, } from "./fileImpl"; -import { entryKey, openDB } from "./storeImpl"; +import { getSettingEntry, setSettingEntry } from "./storeImpl"; import { IpcSOData } from "@/type/ipc"; import { defaultHotkeySettings, defaultToolbarButtonSetting, electronStoreSchema, - ElectronStoreType, EngineId, EngineSetting, EngineSettings, @@ -249,48 +248,11 @@ export const api: typeof window[typeof SandboxKey] = { // NOTE: 何もしなくて良さそう return Promise.resolve(); }, - async getSetting(key: Key) { - const db = await openDB(); - - return new Promise((resolve, reject) => { - const transaction = db.transaction(key, "readonly"); - const store = transaction.objectStore(key); - const request = store.get(entryKey); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - }); + getSetting(key) { + return getSettingEntry(key); }, - async setSetting( - key: Key, - newValue: ElectronStoreType[Key] - ) { - const db = await openDB(); - - // TODO: Schemaに合っているか保存時にvalidationしたい - return new Promise((resolve, reject) => { - const transaction = db.transaction(key, "readwrite"); - const store = transaction.objectStore(key); - const request = store.put(newValue, entryKey); - request.onsuccess = () => { - const readRequest = db - .transaction(key, "readonly") - .objectStore(key) - .get(entryKey); - readRequest.onsuccess = () => { - resolve(readRequest.result); - }; - readRequest.onerror = () => { - reject(readRequest.error); - }; - }; - request.onerror = () => { - reject(request.error); - }; - }); + setSetting(key, newValue) { + return setSettingEntry(key, newValue).then(() => getSettingEntry(key)); }, async setEngineSetting(engineId: EngineId, engineSetting: EngineSetting) { const engineSettings = (await this.getSetting( diff --git a/src/browser/storeImpl.ts b/src/browser/storeImpl.ts index abeef9244d..a9d9ede1e5 100644 --- a/src/browser/storeImpl.ts +++ b/src/browser/storeImpl.ts @@ -1,7 +1,9 @@ import { defaultEngine, directoryHandleStoreKey } from "./contract"; + import { - EngineId, electronStoreSchema, + ElectronStoreType, + EngineId, engineSettingSchema, } from "@/type/preload"; @@ -49,3 +51,41 @@ export const openDB = () => // TODO: migrate }; }); + +export const setSettingEntry = async ( + key: Key, + newValue: ElectronStoreType[Key] +) => { + const db = await openDB(); + + // TODO: Schemaに合っているか保存時にvalidationしたい + return new Promise((resolve, reject) => { + const transaction = db.transaction(key, "readwrite"); + const store = transaction.objectStore(key); + const request = store.put(newValue, entryKey); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }); +}; + +export const getSettingEntry = async ( + key: Key +): Promise => { + const db = await openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction(key, "readonly"); + const store = transaction.objectStore(key); + const request = store.get(entryKey); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }); +}; From efaac6d63547413328925df87f7bfb6ad09d467e Mon Sep 17 00:00:00 2001 From: yamachu Date: Thu, 29 Jun 2023 00:14:30 +0900 Subject: [PATCH 54/75] =?UTF-8?q?serve=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E7=B5=B1=E4=B8=80=E6=84=9F=E3=82=92=E6=8C=81?= =?UTF-8?q?=E3=81=9F=E3=81=9B=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AB=E3=80=81?= =?UTF-8?q?browser=E3=82=92prefix=E3=81=AB=E3=81=A4=E3=81=91=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: sevenc-nanashi --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4764780d00..136c3105b5 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ }, "scripts": { "serve": "cross-env VITE_IS_ELECTRON=false vite", - "serve:browser": "cross-env VITE_IS_ELECTRON=false VITE_IS_BROWSER=true vite", "build": "cross-env VITE_IS_ELECTRON=false vite build", "test:unit": "vitest --run", "test-watch:unit": "vitest --watch", @@ -30,6 +29,7 @@ "electron:build_pnever": "cross-env VITE_IS_ELECTRON=true vite build && electron-builder --config electron-builder.config.js --publish never", "electron:build_pnever_prepackaged": "cross-var electron-builder --config electron-builder.config.js --publish never --prepackaged $PREPACKAGED", "electron:serve": "cross-env VITE_IS_ELECTRON=true vite", + "browser:serve": "cross-env VITE_IS_ELECTRON=false VITE_IS_BROWSER=true vite", "postinstall": "electron-builder install-app-deps && node build/download7z.js", "postuninstall": "electron-builder install-app-deps", "license:generate": "node build/generateLicenses.js", From f133472ab751d1338e0f8abacb0ee8299598a8d7 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 1 Jul 2023 16:20:05 +0900 Subject: [PATCH 55/75] =?UTF-8?q?Sandbox=20interface=E3=81=8B=E3=82=89?= =?UTF-8?q?=E5=8F=82=E7=85=A7=E3=82=92=E6=8E=A2=E3=81=97=E3=82=84=E3=81=99?= =?UTF-8?q?=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=80=81=E5=9E=8B=E3=82=92?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E5=8F=82=E7=85=A7=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 5b63a545ff..181b9b8e51 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -21,7 +21,7 @@ import { EngineSetting, EngineSettings, HotkeySetting, - SandboxKey, + Sandbox, ThemeConf, } from "@/type/preload"; import { @@ -38,7 +38,7 @@ import { // TODO: base pathを設定できるようにするか、ビルド時埋め込みにする const toStaticPath = (fileName: string) => `/${fileName}`; -export const api: typeof window[typeof SandboxKey] = { +export const api: Sandbox = { getAppInfos() { /* eslint-disable @typescript-eslint/no-non-null-assertion */ const appInfo = { From a7832053dcbb256705085134cdbf48287ad64b02 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 1 Jul 2023 16:22:20 +0900 Subject: [PATCH 56/75] =?UTF-8?q?window.SandboxKey=20=E3=81=AB=E4=BB=A3?= =?UTF-8?q?=E5=85=A5=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E5=80=A4=E3=81=8C?= =?UTF-8?q?Sandbox=E3=81=AE=E5=AE=9F=E8=A3=85=E3=81=A7=E3=81=82=E3=82=8B?= =?UTF-8?q?=E3=81=93=E3=81=A8=E3=82=92=E6=98=8E=E7=A4=BA=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=81=9F=E3=82=81=E3=81=AB=E3=80=81=E9=83=A8=E5=88=86=E5=9E=8B?= =?UTF-8?q?=E3=81=A7=E3=81=82=E3=82=8B=E3=81=93=E3=81=A8=E7=A2=BA=E8=AA=8D?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ts-ignore で readonly に代入しているのを握り潰しているので、型が違っていてもエラーに出来ないため、定数への代入のタイミングで型の確認をしている --- src/browser/preload.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/browser/preload.ts b/src/browser/preload.ts index f0743f43e0..8c614eadf4 100644 --- a/src/browser/preload.ts +++ b/src/browser/preload.ts @@ -1,5 +1,6 @@ -import { SandboxKey } from "../type/preload"; +import { SandboxKey, Sandbox } from "../type/preload"; import { api } from "./sandbox"; +const sandbox: Sandbox = api; // @ts-expect-error readonlyになっているが、初期化処理はここで行うので問題ない -window[SandboxKey] = api; +window[SandboxKey] = sandbox; From 124cd26cc5a2ac1979031df8b495d720db742968 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 1 Jul 2023 16:22:40 +0900 Subject: [PATCH 57/75] =?UTF-8?q?=E3=82=8F=E3=81=8B=E3=82=89=E3=81=AA?= =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=9F=E3=82=89=E3=81=A8=E3=82=8A=E3=81=82?= =?UTF-8?q?=E3=81=88=E3=81=9Athrow=E3=81=97=E3=81=A6=E3=81=8F=E3=82=8C?= =?UTF-8?q?=E3=82=92=E6=9B=B8=E3=81=84=E3=81=A6=E3=81=8A=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/sandbox.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 181b9b8e51..1d2fc76b46 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -38,6 +38,11 @@ import { // TODO: base pathを設定できるようにするか、ビルド時埋め込みにする const toStaticPath = (fileName: string) => `/${fileName}`; +/** + * Browser版のSandBox実装 + * src/type/preload.tsのSandboxを変更した場合は、interfaceに追従した変更が必要 + * まだ開発中のため、Browser版の実装も同時に行えない場合は、メソッドを追加して throw new Error() する + */ export const api: Sandbox = { getAppInfos() { /* eslint-disable @typescript-eslint/no-non-null-assertion */ From 57203a33b63e8e6eaa56b6c4a4476dce6f4ccf07 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sun, 2 Jul 2023 20:32:41 +0900 Subject: [PATCH 58/75] =?UTF-8?q?@types/wicg-file-system-access=20?= =?UTF-8?q?=E3=82=92tsconfig=E3=81=A7=E8=AA=AD=E3=81=BE=E3=81=9B=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=81=A7=E3=80=81=E3=82=A2=E3=83=97=E3=83=AA=E3=82=B1?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AEglobal=E5=9E=8B?= =?UTF-8?q?=E5=AE=9A=E7=BE=A9=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=A7?= =?UTF-8?q?=E8=AA=AD=E3=81=BE=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit コメントが残しやすいのと、アプリケーション依存なのでこっちの方が適当っぽそう --- src/type/globals.d.ts | 2 ++ tsconfig.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/type/globals.d.ts b/src/type/globals.d.ts index b165e46bf3..74fa2173cd 100644 --- a/src/type/globals.d.ts +++ b/src/type/globals.d.ts @@ -1,5 +1,7 @@ // Include global variables to build immer source code export * from "immer/src/types/globals"; +// showDirectoryPicker などのAPIをブラウザで使用するためにimportしている +import "@types/wicg-file-system-access"; import { SandboxKey } from "./preload"; declare global { diff --git a/tsconfig.json b/tsconfig.json index 39cf9c9d64..3fd320f9a5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "allowSyntheticDefaultImports": true, "sourceMap": true, "baseUrl": ".", - "types": ["vitest/globals", "@types/wicg-file-system-access"], + "types": ["vitest/globals"], "paths": { "@/*": ["src/*"] }, From a3c3fe6d4240b959e9a3ff9d4f0d89fc851da435 Mon Sep 17 00:00:00 2001 From: yamachu Date: Sun, 2 Jul 2023 20:42:24 +0900 Subject: [PATCH 59/75] =?UTF-8?q?=E3=83=9E=E3=82=A4=E3=82=B0=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E5=BF=98=E3=82=8C=E3=81=AB?= =?UTF-8?q?=E6=B0=97=E3=81=A5=E3=81=91=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?console.error=E3=82=92=E5=90=90=E3=81=84=E3=81=A6=E3=81=8A?= =?UTF-8?q?=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Hiroshiba --- src/browser/storeImpl.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/browser/storeImpl.ts b/src/browser/storeImpl.ts index a9d9ede1e5..38f6a58c0b 100644 --- a/src/browser/storeImpl.ts +++ b/src/browser/storeImpl.ts @@ -47,8 +47,14 @@ export const openDB = () => // DirectoryHandleも格納する db.createObjectStore(directoryHandleStoreKey); + } else if (ev.newVersion !== null && ev.newVersion > ev.oldVersion) { + // TODO: migrate + /* eslint-disable no-console */ // logger みたいなパッケージに切り出して、それに依存する形でもいいかも + console.error( + `マイグレーション処理が必要です。${ev.oldVersion} => ${ev.newVersion}` + ); + /* eslint-enable no-console */ } - // TODO: migrate }; }); From 20d1c1ae75c96cd2a2d73b4f45300e59f1ad43ef Mon Sep 17 00:00:00 2001 From: yamachu Date: Sun, 2 Jul 2023 21:03:43 +0900 Subject: [PATCH 60/75] =?UTF-8?q?migration=E3=81=A8=E3=81=8B=E3=82=92?= =?UTF-8?q?=E8=A1=8C=E3=81=86=E9=9A=9B=E3=81=AB=E3=83=88=E3=83=A9=E3=83=B3?= =?UTF-8?q?=E3=82=B6=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E7=9A=84=E3=81=AB?= =?UTF-8?q?=E3=82=82=E5=8D=98=E4=B8=80=E3=81=AEjsonObject=E3=81=A8?= =?UTF-8?q?=E3=81=97=E3=81=A6=E6=89=B1=E3=81=88=E3=81=B0=E3=82=84=E3=82=8A?= =?UTF-8?q?=E3=82=84=E3=81=99=E3=81=9D=E3=81=86=E3=81=8B=E3=81=AA=E3=81=A8?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9F=E3=81=AE=E3=81=A7=E3=80=81=E4=B8=80?= =?UTF-8?q?=E3=81=A4=E3=81=AEstore=E3=81=AB=E9=9B=86=E7=B4=84=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/storeImpl.ts | 51 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/browser/storeImpl.ts b/src/browser/storeImpl.ts index 38f6a58c0b..8c0e78e0b9 100644 --- a/src/browser/storeImpl.ts +++ b/src/browser/storeImpl.ts @@ -8,6 +8,7 @@ import { } from "@/type/preload"; const dbName = "voicevox-web"; +const electronStoreKey = "electronStore"; // FIXME: DBのschemaを変更したら、dbVersionを上げる // TODO: 気づけるようにしたい const dbVersion = 1; @@ -29,21 +30,12 @@ export const openDB = () => // Initialize const db = request.result; const baseSchema = electronStoreSchema.parse({}); - Object.entries(baseSchema).forEach(([key, value]) => { - const k = key as keyof typeof baseSchema; - if (k !== "engineSettings") { - db.createObjectStore(key).add(value, entryKey); - return; - } - // defaultのEngineSettingを追加 - const defaultVoicevoxEngineId = EngineId(defaultEngine.uuid); - db.createObjectStore(key).add( - { - [defaultVoicevoxEngineId]: engineSettingSchema.parse({}), - }, - entryKey - ); - }); + + const defaultVoicevoxEngineId = EngineId(defaultEngine.uuid); + baseSchema.engineSettings = { + [defaultVoicevoxEngineId]: engineSettingSchema.parse({}), + }; + db.createObjectStore(electronStoreKey).add(baseSchema, entryKey); // DirectoryHandleも格納する db.createObjectStore(directoryHandleStoreKey); @@ -66,14 +58,23 @@ export const setSettingEntry = async ( // TODO: Schemaに合っているか保存時にvalidationしたい return new Promise((resolve, reject) => { - const transaction = db.transaction(key, "readwrite"); - const store = transaction.objectStore(key); - const request = store.put(newValue, entryKey); - request.onsuccess = () => { - resolve(request.result); + const transaction = db.transaction(electronStoreKey, "readwrite"); + const store = transaction.objectStore(electronStoreKey); + const getRequest = store.get(entryKey); + getRequest.onsuccess = () => { + const baseSchema = electronStoreSchema.parse(getRequest.result); + baseSchema[key] = newValue; + const validatedSchema = electronStoreSchema.parse(baseSchema); + const putRequest = store.put(validatedSchema, entryKey); + putRequest.onsuccess = () => { + resolve(putRequest.result); + }; + putRequest.onerror = () => { + reject(putRequest.error); + }; }; - request.onerror = () => { - reject(request.error); + getRequest.onerror = () => { + reject(getRequest.error); }; }); }; @@ -84,11 +85,11 @@ export const getSettingEntry = async ( const db = await openDB(); return new Promise((resolve, reject) => { - const transaction = db.transaction(key, "readonly"); - const store = transaction.objectStore(key); + const transaction = db.transaction(electronStoreKey, "readonly"); + const store = transaction.objectStore(electronStoreKey); const request = store.get(entryKey); request.onsuccess = () => { - resolve(request.result); + resolve(request.result[key]); }; request.onerror = () => { reject(request.error); From cfe3e3fbeac6de8eadfc9267cd26755fa0d81eab Mon Sep 17 00:00:00 2001 From: yamachu Date: Sun, 2 Jul 2023 21:06:42 +0900 Subject: [PATCH 61/75] =?UTF-8?q?directoryHandle=E3=82=92=E4=BF=9D?= =?UTF-8?q?=E6=8C=81=E3=81=99=E3=82=8B=E8=83=8C=E6=99=AF=E3=82=92=E6=9B=B8?= =?UTF-8?q?=E3=81=84=E3=81=A6=E3=81=8A=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/storeImpl.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/browser/storeImpl.ts b/src/browser/storeImpl.ts index 8c0e78e0b9..91574feb42 100644 --- a/src/browser/storeImpl.ts +++ b/src/browser/storeImpl.ts @@ -37,7 +37,10 @@ export const openDB = () => }; db.createObjectStore(electronStoreKey).add(baseSchema, entryKey); - // DirectoryHandleも格納する + // NOTE: fixedExportDirectoryを使用してファイルの書き出しをする際、 + // audio.tsの現在の実装では、ディレクトリを選択するモーダルを表示しないようになっている + // ディレクトリへの書き出し権限の要求は、モーダルの表示かディレクトリを指定したファイルの書き出しの時のみで、 + // directoryHandleがないと権限の要求が出来ないため、directoryHandleを永続化しておく db.createObjectStore(directoryHandleStoreKey); } else if (ev.newVersion !== null && ev.newVersion > ev.oldVersion) { // TODO: migrate From f038630e20b040ae21415fbbfc830c24191923cf Mon Sep 17 00:00:00 2001 From: yamachu Date: Sun, 2 Jul 2023 21:20:14 +0900 Subject: [PATCH 62/75] =?UTF-8?q?write=E5=91=A8=E3=82=8A=E3=81=AE=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=81=AFaudio.ts=E3=81=AEaction=E3=81=8B=E3=82=89?= =?UTF-8?q?=E6=B8=A1=E3=81=95=E3=82=8C=E3=82=8BfilePath=E3=81=AB=E4=BE=9D?= =?UTF-8?q?=E5=AD=98=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=81=AE=E3=81=A7?= =?UTF-8?q?=E3=80=81=E3=81=9D=E3=81=AE=E6=97=A8=E3=82=92=E3=82=B3=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AB=E6=AE=8B=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 845af4b093..4e8aad1a94 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -246,6 +246,10 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = let directoryHandle = getLatestSelectedDirectoryHandle(); let path = obj.filePath; + // NOTE: fixedExportEnabled が有効になっている GENERATE_AND_SAVE_AUDIO action では、ファイル名に加えディレクトリ名も指定された状態でfilePathが渡ってくる + // また GENERATE_AND_SAVE_ALL_AUDIO action では fixedExportEnabled の有効の有無に関わらず、ディレクトリ名も指定された状態でfilePathが渡ってくる + // FileSystemDirectoryHandle.getFileHandle では / のような separator が含まれるとエラーになるため、以下の if 文で separator を除去している + // また separator 以前の文字列はディレクトリ名として扱われ、それを key として directoryHandleMap からハンドラを取得したり、set している if (path.includes(sep)) { const maybeDirectoryHandleName = path.split(sep)[0]; if (directoryHandleMap.has(maybeDirectoryHandleName)) { From d40d430faaae52c466a95e0433024e72f20a4dc9 Mon Sep 17 00:00:00 2001 From: Yusuke Yamada Date: Sun, 2 Jul 2023 23:57:41 +0900 Subject: [PATCH 63/75] Update src/browser/fileImpl.ts Co-authored-by: Hiroshiba --- src/browser/fileImpl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 4e8aad1a94..66fe1489aa 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -10,7 +10,8 @@ const showWritableDirectoryPicker = async (): Promise< .showDirectoryPicker({ mode: "readwrite", }) - .catch(() => undefined); + // キャンセルするとエラーが投げられる + .catch(() => undefined); // FIXME: このままだとダイアログ表示エラーと見分けがつかない const storeDirectoryHandle = async ( directoryHandle: FileSystemDirectoryHandle From 8f96446a25645aefbfda9219f39726a4646b6931 Mon Sep 17 00:00:00 2001 From: yamachu Date: Mon, 3 Jul 2023 21:17:59 +0900 Subject: [PATCH 64/75] =?UTF-8?q?=E3=83=87=E3=82=A3=E3=83=AC=E3=82=AF?= =?UTF-8?q?=E3=83=88=E3=83=AA=E3=82=92=E7=89=B9=E5=AE=9A=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=8D=98=E4=BD=93=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=81=AB=E5=AF=BE=E3=81=99=E3=82=8B=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=81=AF=E4=B8=80=E6=97=A6=E3=82=B5=E3=83=9D=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 97 ++++++++++++----------------------------- 1 file changed, 27 insertions(+), 70 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 66fe1489aa..c06bad5741 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -30,20 +30,7 @@ const storeDirectoryHandle = async ( }); }; -const lastSelectedDirectoryHandleSymbol = Symbol("lastSelectedDirectoryHandle"); -const directoryHandleMap: Map< - string | typeof lastSelectedDirectoryHandleSymbol, - FileSystemDirectoryHandle -> = new Map(); - -const updateLatestSelectedDirectoryHandle = ( - handle: FileSystemDirectoryHandle -) => { - directoryHandleMap.set(lastSelectedDirectoryHandleSymbol, handle); -}; - -const getLatestSelectedDirectoryHandle = () => - directoryHandleMap.get(lastSelectedDirectoryHandleSymbol); +const directoryHandleMap: Map = new Map(); type AcceptFileType = { description: string; @@ -85,19 +72,6 @@ const requestSaveFileNameWithDirectoryPermission = async ({ suggestedName?: string; fileType: AcceptFileType; }) => { - if (directoryHandleMap.get(lastSelectedDirectoryHandleSymbol) === undefined) { - // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している - // FIXME: 途中でディレクトリを変えたいとかには対応できない… - const _directoryHandler = await showWritableDirectoryPicker(); - if (_directoryHandler === undefined) { - return undefined; - } - - await storeDirectoryHandle(_directoryHandler); - - updateLatestSelectedDirectoryHandle(_directoryHandler); - } - const fileHandle = await showWritableFilePicker({ suggestedName, fileType, @@ -115,19 +89,6 @@ const requestLoadFileNameWithDirectoryPermission = async ({ }: { fileType: AcceptFileType; }) => { - if (directoryHandleMap.get(lastSelectedDirectoryHandleSymbol) === undefined) { - // Wave File以外のものを同一ディレクトリに保存したり、名前を変えて保存するためにDirectoryのPickerを使用している - // FIXME: 途中でディレクトリを変えたいとかには対応できない… - const _directoryHandler = await showWritableDirectoryPicker(); - if (_directoryHandler === undefined) { - return undefined; - } - - await storeDirectoryHandle(_directoryHandler); - - updateLatestSelectedDirectoryHandle(_directoryHandler); - } - const fileHandle = await showLoadableFilePicker({ fileType, }); @@ -211,10 +172,6 @@ export const showImportFileDialogImpl: typeof window[typeof SandboxKey]["showImp }).then((v) => v?.[0]); }; -const isRootPathButDirectoryNotSelected = (path: string) => - directoryHandleMap.get(lastSelectedDirectoryHandleSymbol) === undefined && - path.indexOf(sep) === -1; - const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { const db = await openDB(); return new Promise( @@ -237,21 +194,20 @@ const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = async (obj: { filePath: string; buffer: ArrayBuffer }) => { - if (isRootPathButDirectoryNotSelected(obj.filePath)) { + let directoryHandle: FileSystemDirectoryHandle | undefined = undefined; + let path = obj.filePath; + + if (path.indexOf(sep) === -1) { return Promise.resolve({ code: undefined, - message: "フォルダへのアクセス許可がありません", + message: + "フォルダへのアクセス許可がありません、ブラウザ版ではファイル単体の書き出しをサポートしていません", }); - } - - let directoryHandle = getLatestSelectedDirectoryHandle(); - let path = obj.filePath; - - // NOTE: fixedExportEnabled が有効になっている GENERATE_AND_SAVE_AUDIO action では、ファイル名に加えディレクトリ名も指定された状態でfilePathが渡ってくる - // また GENERATE_AND_SAVE_ALL_AUDIO action では fixedExportEnabled の有効の有無に関わらず、ディレクトリ名も指定された状態でfilePathが渡ってくる - // FileSystemDirectoryHandle.getFileHandle では / のような separator が含まれるとエラーになるため、以下の if 文で separator を除去している - // また separator 以前の文字列はディレクトリ名として扱われ、それを key として directoryHandleMap からハンドラを取得したり、set している - if (path.includes(sep)) { + } else { + // NOTE: fixedExportEnabled が有効になっている GENERATE_AND_SAVE_AUDIO action では、ファイル名に加えディレクトリ名も指定された状態でfilePathが渡ってくる + // また GENERATE_AND_SAVE_ALL_AUDIO action では fixedExportEnabled の有効の有無に関わらず、ディレクトリ名も指定された状態でfilePathが渡ってくる + // FileSystemDirectoryHandle.getFileHandle では / のような separator が含まれるとエラーになるため、以下の if 文で separator を除去している + // また separator 以前の文字列はディレクトリ名として扱われ、それを key として directoryHandleMap からハンドラを取得したり、set している const maybeDirectoryHandleName = path.split(sep)[0]; if (directoryHandleMap.has(maybeDirectoryHandleName)) { path = path.slice(maybeDirectoryHandleName.length + sep.length); @@ -289,7 +245,8 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = if (directoryHandle === undefined) { return Promise.resolve({ code: undefined, - message: "フォルダへのアクセス許可がありません", + message: + "フォルダへのアクセス許可がありません、ブラウザ版ではファイル単体の書き出しをサポートしていません", }); } @@ -311,14 +268,16 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = export const readFileImpl: typeof window[typeof SandboxKey]["readFile"] = async (obj: { filePath: string }) => { - if (isRootPathButDirectoryNotSelected(obj.filePath)) { - return Promise.reject(new Error("フォルダへのアクセス許可がありません")); - } - - let directoryHandle = getLatestSelectedDirectoryHandle(); + let directoryHandle: FileSystemDirectoryHandle | undefined = undefined; let path = obj.filePath; - if (path.includes(sep)) { + if (path.indexOf(sep) === -1) { + return Promise.reject( + new Error( + "フォルダへのアクセス許可がありません、ブラウザ版ではファイル単体の読み込みをサポートしていません" + ) + ); + } else { const maybeDirectoryHandleName = path.split(sep)[0]; if (directoryHandleMap.has(maybeDirectoryHandleName)) { path = path.slice(maybeDirectoryHandleName.length + sep.length); @@ -365,15 +324,13 @@ export const readFileImpl: typeof window[typeof SandboxKey]["readFile"] = export const checkFileExistsImpl: typeof window[typeof SandboxKey]["checkFileExists"] = async (file) => { - if (isRootPathButDirectoryNotSelected(file)) { - // FIXME: trueだとloopするはず - return Promise.resolve(false); - } - - let directoryHandle = getLatestSelectedDirectoryHandle(); + let directoryHandle: FileSystemDirectoryHandle | undefined = undefined; let path = file; - if (path.includes(sep)) { + if (path.indexOf(sep) === -1) { + // FIXME: trueだとloopするはず + return Promise.resolve(false); + } else { const maybeDirectoryHandleName = path.split(sep)[0]; if (directoryHandleMap.has(maybeDirectoryHandleName)) { path = path.slice(maybeDirectoryHandleName.length + sep.length); From e2adadf88d0b9535cba921dece53d26542570c9d Mon Sep 17 00:00:00 2001 From: yamachu Date: Mon, 3 Jul 2023 21:20:06 +0900 Subject: [PATCH 65/75] =?UTF-8?q?readFile=E3=81=AF=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=E3=81=AB=E3=83=87=E3=82=A3=E3=83=AC=E3=82=AF=E3=83=88?= =?UTF-8?q?=E3=83=AA=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=97=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=81=AE=E3=81=A7=E5=AE=9F=E8=A3=85=E3=81=94=E3=81=A8=E4=B8=80?= =?UTF-8?q?=E6=97=A6=E6=B6=88=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 56 ----------------------------------------- src/browser/sandbox.ts | 7 +++--- 2 files changed, 4 insertions(+), 59 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index c06bad5741..87818d8ad6 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -266,62 +266,6 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = }); }; -export const readFileImpl: typeof window[typeof SandboxKey]["readFile"] = - async (obj: { filePath: string }) => { - let directoryHandle: FileSystemDirectoryHandle | undefined = undefined; - let path = obj.filePath; - - if (path.indexOf(sep) === -1) { - return Promise.reject( - new Error( - "フォルダへのアクセス許可がありません、ブラウザ版ではファイル単体の読み込みをサポートしていません" - ) - ); - } else { - const maybeDirectoryHandleName = path.split(sep)[0]; - if (directoryHandleMap.has(maybeDirectoryHandleName)) { - path = path.slice(maybeDirectoryHandleName.length + sep.length); - directoryHandle = directoryHandleMap.get(maybeDirectoryHandleName); - } else { - const maybeFixedDirectory = await fetchStoredDirectoryHandle( - maybeDirectoryHandleName - ); - - if (maybeFixedDirectory === undefined) { - return Promise.reject( - new Error( - `フォルダへのアクセス許可がありません。アクセスしようとしたフォルダ名: ${maybeDirectoryHandleName}` - ) - ); - } - - if ( - !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) - ) { - return Promise.reject( - new Error( - "フォルダへのアクセス許可がありません。ファイルの読み書きのために許可が必要です。" - ) - ); - } - - directoryHandleMap.set(maybeDirectoryHandleName, maybeFixedDirectory); - - path = path.slice(maybeDirectoryHandleName.length + sep.length); - directoryHandle = maybeFixedDirectory; - } - } - - if (directoryHandle === undefined) { - return Promise.reject(new Error("フォルダへのアクセス許可がありません")); - } - - return directoryHandle.getFileHandle(path).then(async (fileHandle) => { - const file = await fileHandle.getFile(); - return file.arrayBuffer(); - }); - }; - export const checkFileExistsImpl: typeof window[typeof SandboxKey]["checkFileExists"] = async (file) => { let directoryHandle: FileSystemDirectoryHandle | undefined = undefined; diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 1d2fc76b46..817072319c 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -1,7 +1,6 @@ import { defaultEngine } from "./contract"; import { checkFileExistsImpl, - readFileImpl, showAudioSaveDialogImpl, showImportFileDialogImpl, showOpenDirectoryDialogImpl, @@ -132,8 +131,10 @@ export const api: Sandbox = { writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { return writeFileImpl(obj); }, - readFile(obj: { filePath: string }) { - return readFileImpl(obj); + readFile(/* obj: { filePath: string } */) { + throw new Error( + "ブラウザ版では現在ファイルの読み込みをサポートしていません" + ); }, openTextEditContextMenu() { // NOTE: ブラウザ版では不要 From 03cb8d2ac1df8470fc9efbea616ffc75a9b3b111 Mon Sep 17 00:00:00 2001 From: yamachu Date: Mon, 3 Jul 2023 21:34:27 +0900 Subject: [PATCH 66/75] =?UTF-8?q?=E6=84=8F=E5=91=B3=E5=8D=98=E4=BD=8D?= =?UTF-8?q?=E3=81=A7=E9=96=A2=E6=95=B0=E3=81=AE=E5=A0=B4=E6=89=80=E3=81=BE?= =?UTF-8?q?=E3=81=A8=E3=82=81=E3=82=88=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 60 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 87818d8ad6..82612f9b0e 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -3,16 +3,6 @@ import { directoryHandleStoreKey } from "./contract"; import { openDB } from "./storeImpl"; import { SandboxKey, WriteFileErrorResult } from "@/type/preload"; -const showWritableDirectoryPicker = async (): Promise< - FileSystemDirectoryHandle | undefined -> => - window - .showDirectoryPicker({ - mode: "readwrite", - }) - // キャンセルするとエラーが投げられる - .catch(() => undefined); // FIXME: このままだとダイアログ表示エラーと見分けがつかない - const storeDirectoryHandle = async ( directoryHandle: FileSystemDirectoryHandle ): Promise => { @@ -30,6 +20,26 @@ const storeDirectoryHandle = async ( }); }; +const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { + const db = await openDB(); + return new Promise( + (resolve, reject) => { + const transaction = db.transaction(directoryHandleStoreKey, "readonly"); + const store = transaction.objectStore(directoryHandleStoreKey); + const request = store.get(maybeDirectoryHandleName); + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + } + ).catch(() => { + // FIXME: 握り潰してる + return undefined; + }); +}; + const directoryHandleMap: Map = new Map(); type AcceptFileType = { @@ -37,6 +47,16 @@ type AcceptFileType = { accept: Record; }; +const showWritableDirectoryPicker = async (): Promise< + FileSystemDirectoryHandle | undefined +> => + window + .showDirectoryPicker({ + mode: "readwrite", + }) + // キャンセルするとエラーが投げられる + .catch(() => undefined); // FIXME: このままだとダイアログ表示エラーと見分けがつかない + const showWritableFilePicker = async ({ suggestedName, fileType, @@ -172,26 +192,6 @@ export const showImportFileDialogImpl: typeof window[typeof SandboxKey]["showImp }).then((v) => v?.[0]); }; -const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { - const db = await openDB(); - return new Promise( - (resolve, reject) => { - const transaction = db.transaction(directoryHandleStoreKey, "readonly"); - const store = transaction.objectStore(directoryHandleStoreKey); - const request = store.get(maybeDirectoryHandleName); - request.onsuccess = () => { - resolve(request.result); - }; - request.onerror = () => { - reject(request.error); - }; - } - ).catch(() => { - // FIXME: 握り潰してる - return undefined; - }); -}; - export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = async (obj: { filePath: string; buffer: ArrayBuffer }) => { let directoryHandle: FileSystemDirectoryHandle | undefined = undefined; From 30aaa8fc5ca4277fc44e3f0235cb8c54937e271e Mon Sep 17 00:00:00 2001 From: yamachu Date: Mon, 3 Jul 2023 21:37:11 +0900 Subject: [PATCH 67/75] =?UTF-8?q?=E6=8F=A1=E3=82=8A=E3=81=A4=E3=81=B6?= =?UTF-8?q?=E3=81=99=E6=84=8F=E5=91=B3=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 82612f9b0e..7cce014463 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -34,10 +34,7 @@ const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { reject(request.error); }; } - ).catch(() => { - // FIXME: 握り潰してる - return undefined; - }); + ); }; const directoryHandleMap: Map = new Map(); From d12315512886ff9ba10f09790b50f1f6ac4d1bff Mon Sep 17 00:00:00 2001 From: yamachu Date: Mon, 3 Jul 2023 22:28:43 +0900 Subject: [PATCH 68/75] =?UTF-8?q?=E3=82=B3=E3=83=94=E3=83=9A=E3=81=A7?= =?UTF-8?q?=E5=A2=97=E3=82=84=E3=81=97=E3=81=A6=E3=81=84=E3=81=9FDirectory?= =?UTF-8?q?Handle=E5=91=A8=E3=82=8A=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?=E3=81=BE=E3=81=A8=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 147 ++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 82 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 7cce014463..4d8f57b815 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -189,10 +189,51 @@ export const showImportFileDialogImpl: typeof window[typeof SandboxKey]["showImp }).then((v) => v?.[0]); }; +// separator 以前の文字列はディレクトリ名として扱う +const resolveDirectoryName = (path: string) => path.split(sep)[0]; + +// FileSystemDirectoryHandle.getFileHandle では / のような separator が含まれるとエラーになるため、ファイル名のみを抽出している +const resolveFileName = (path: string) => { + const maybeDirectoryHandleName = resolveDirectoryName(path); + return path.slice(maybeDirectoryHandleName.length + sep.length); +}; + +// FileSystemDirectoryHandle.getFileHandle では / のような separator が含まれるとエラーになるため、以下の if 文で separator を除去している +// また separator 以前の文字列はディレクトリ名として扱われ、それを key として directoryHandleMap からハンドラを取得したり、set している +const getDirectoryHandleFromDirectoryPath = async ( + maybeDirectoryPathKey: string +): Promise => { + const maybeHandle = directoryHandleMap.get(maybeDirectoryPathKey); + + if (maybeHandle !== undefined) { + return maybeHandle; + } else { + // NOTE: fixedDirectoryの場合こっちに落ちる場合がある + const maybeFixedDirectory = await fetchStoredDirectoryHandle( + maybeDirectoryPathKey + ); + + if (maybeFixedDirectory === undefined) { + throw new Error( + `フォルダへのアクセス許可がありません。アクセスしようとしたフォルダ名: ${maybeDirectoryPathKey}` + ); + } + + if (!(await maybeFixedDirectory.requestPermission({ mode: "readwrite" }))) { + throw new Error( + "フォルダへのアクセス許可がありません。ファイルの読み書きのためにアクセス許可が必要です。" + ); + } + + return maybeFixedDirectory; + } +}; + +// NOTE: fixedExportEnabled が有効になっている GENERATE_AND_SAVE_AUDIO action では、ファイル名に加えディレクトリ名も指定された状態でfilePathが渡ってくる +// また GENERATE_AND_SAVE_ALL_AUDIO action では fixedExportEnabled の有効の有無に関わらず、ディレクトリ名も指定された状態でfilePathが渡ってくる export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = async (obj: { filePath: string; buffer: ArrayBuffer }) => { - let directoryHandle: FileSystemDirectoryHandle | undefined = undefined; - let path = obj.filePath; + const path = obj.filePath; if (path.indexOf(sep) === -1) { return Promise.resolve({ @@ -200,55 +241,19 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = message: "フォルダへのアクセス許可がありません、ブラウザ版ではファイル単体の書き出しをサポートしていません", }); - } else { - // NOTE: fixedExportEnabled が有効になっている GENERATE_AND_SAVE_AUDIO action では、ファイル名に加えディレクトリ名も指定された状態でfilePathが渡ってくる - // また GENERATE_AND_SAVE_ALL_AUDIO action では fixedExportEnabled の有効の有無に関わらず、ディレクトリ名も指定された状態でfilePathが渡ってくる - // FileSystemDirectoryHandle.getFileHandle では / のような separator が含まれるとエラーになるため、以下の if 文で separator を除去している - // また separator 以前の文字列はディレクトリ名として扱われ、それを key として directoryHandleMap からハンドラを取得したり、set している - const maybeDirectoryHandleName = path.split(sep)[0]; - if (directoryHandleMap.has(maybeDirectoryHandleName)) { - path = path.slice(maybeDirectoryHandleName.length + sep.length); - directoryHandle = directoryHandleMap.get(maybeDirectoryHandleName); - } else { - // NOTE: fixedDirectoryの場合こっちに落ちる場合がある - const maybeFixedDirectory = await fetchStoredDirectoryHandle( - maybeDirectoryHandleName - ); - - if (maybeFixedDirectory === undefined) { - return Promise.resolve({ - code: undefined, - message: `フォルダへのアクセス許可がありません。アクセスしようとしたフォルダ名: ${maybeDirectoryHandleName}`, - }); - } - - if ( - !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) - ) { - return Promise.resolve({ - code: undefined, - message: - "フォルダへのアクセス許可がありません。ファイルの書き込みのために書き込み許可が必要です。", - }); - } - - directoryHandleMap.set(maybeDirectoryHandleName, maybeFixedDirectory); - - path = path.slice(maybeDirectoryHandleName.length + sep.length); - directoryHandle = maybeFixedDirectory; - } } - if (directoryHandle === undefined) { - return Promise.resolve({ - code: undefined, - message: - "フォルダへのアクセス許可がありません、ブラウザ版ではファイル単体の書き出しをサポートしていません", - }); - } + const fileName = resolveFileName(path); + const maybeDirectoryHandleName = resolveDirectoryName(path); + + const directoryHandle = await getDirectoryHandleFromDirectoryPath( + maybeDirectoryHandleName + ); + + directoryHandleMap.set(maybeDirectoryHandleName, directoryHandle); return directoryHandle - .getFileHandle(path, { create: true }) + .getFileHandle(fileName, { create: true }) .then(async (fileHandle) => { const writable = await fileHandle.createWritable(); await writable.write(obj.buffer); @@ -265,44 +270,22 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = export const checkFileExistsImpl: typeof window[typeof SandboxKey]["checkFileExists"] = async (file) => { - let directoryHandle: FileSystemDirectoryHandle | undefined = undefined; - let path = file; + const path = file; if (path.indexOf(sep) === -1) { - // FIXME: trueだとloopするはず - return Promise.resolve(false); - } else { - const maybeDirectoryHandleName = path.split(sep)[0]; - if (directoryHandleMap.has(maybeDirectoryHandleName)) { - path = path.slice(maybeDirectoryHandleName.length + sep.length); - directoryHandle = directoryHandleMap.get(maybeDirectoryHandleName); - } else { - // NOTE: fixedDirectoryの場合こっちに落ちる場合がある - const maybeFixedDirectory = await fetchStoredDirectoryHandle( - maybeDirectoryHandleName - ); - - if (maybeFixedDirectory === undefined) { - return Promise.resolve(false); - } - - if ( - !(await maybeFixedDirectory.requestPermission({ mode: "readwrite" })) - ) { - return Promise.resolve(false); - } - - directoryHandleMap.set(maybeDirectoryHandleName, maybeFixedDirectory); - - path = path.slice(maybeDirectoryHandleName.length + sep.length); - directoryHandle = maybeFixedDirectory; - } + throw new Error( + "ブラウザ版ではフォルダを固定しないファイルへのアクセスをサポートしていません" + ); } - if (directoryHandle === undefined) { - // FIXME: trueだとloopするはず - return Promise.resolve(false); - } + const fileName = resolveFileName(path); + const maybeDirectoryHandleName = resolveDirectoryName(path); + + const directoryHandle = await getDirectoryHandleFromDirectoryPath( + maybeDirectoryHandleName + ); + + directoryHandleMap.set(maybeDirectoryHandleName, directoryHandle); const fileEntries = []; for await (const [ @@ -314,5 +297,5 @@ export const checkFileExistsImpl: typeof window[typeof SandboxKey]["checkFileExi } } - return Promise.resolve(fileEntries.includes(path)); + return Promise.resolve(fileEntries.includes(fileName)); }; From 04084e8e7af7f39930a631a1c40f8846aead8ee2 Mon Sep 17 00:00:00 2001 From: yamachu Date: Mon, 3 Jul 2023 22:37:26 +0900 Subject: [PATCH 69/75] rename const name --- src/browser/storeImpl.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/browser/storeImpl.ts b/src/browser/storeImpl.ts index 91574feb42..08fb4aac49 100644 --- a/src/browser/storeImpl.ts +++ b/src/browser/storeImpl.ts @@ -8,7 +8,7 @@ import { } from "@/type/preload"; const dbName = "voicevox-web"; -const electronStoreKey = "electronStore"; +const settingStoreKey = "electronStore"; // FIXME: DBのschemaを変更したら、dbVersionを上げる // TODO: 気づけるようにしたい const dbVersion = 1; @@ -35,7 +35,7 @@ export const openDB = () => baseSchema.engineSettings = { [defaultVoicevoxEngineId]: engineSettingSchema.parse({}), }; - db.createObjectStore(electronStoreKey).add(baseSchema, entryKey); + db.createObjectStore(settingStoreKey).add(baseSchema, entryKey); // NOTE: fixedExportDirectoryを使用してファイルの書き出しをする際、 // audio.tsの現在の実装では、ディレクトリを選択するモーダルを表示しないようになっている @@ -61,8 +61,8 @@ export const setSettingEntry = async ( // TODO: Schemaに合っているか保存時にvalidationしたい return new Promise((resolve, reject) => { - const transaction = db.transaction(electronStoreKey, "readwrite"); - const store = transaction.objectStore(electronStoreKey); + const transaction = db.transaction(settingStoreKey, "readwrite"); + const store = transaction.objectStore(settingStoreKey); const getRequest = store.get(entryKey); getRequest.onsuccess = () => { const baseSchema = electronStoreSchema.parse(getRequest.result); @@ -88,8 +88,8 @@ export const getSettingEntry = async ( const db = await openDB(); return new Promise((resolve, reject) => { - const transaction = db.transaction(electronStoreKey, "readonly"); - const store = transaction.objectStore(electronStoreKey); + const transaction = db.transaction(settingStoreKey, "readonly"); + const store = transaction.objectStore(settingStoreKey); const request = store.get(entryKey); request.onsuccess = () => { resolve(request.result[key]); From 50bd37cd115b3467587ad70d3067316c3d655a6e Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 4 Jul 2023 23:37:21 +0900 Subject: [PATCH 70/75] =?UTF-8?q?=E5=8D=98=E4=B8=80=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=AFa=E3=82=BF=E3=82=B0=E3=81=A7?= =?UTF-8?q?=E3=83=80=E3=82=A6=E3=83=B3=E3=83=AD=E3=83=BC=E3=83=89=E3=81=95?= =?UTF-8?q?=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chromeだと認可を得ることが出来れば複数ダウンロードみたいのが出来るのでなんとかなる… --- src/browser/fileImpl.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 4d8f57b815..48a6a3641c 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -236,11 +236,15 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = const path = obj.filePath; if (path.indexOf(sep) === -1) { - return Promise.resolve({ - code: undefined, - message: - "フォルダへのアクセス許可がありません、ブラウザ版ではファイル単体の書き出しをサポートしていません", - }); + const aTag = document.createElement("a"); + const blob = URL.createObjectURL(new Blob([obj.buffer])); + aTag.href = blob; + aTag.download = path; + document.body.appendChild(aTag); + aTag.click(); + document.body.removeChild(aTag); + URL.revokeObjectURL(blob); + return; } const fileName = resolveFileName(path); @@ -273,9 +277,7 @@ export const checkFileExistsImpl: typeof window[typeof SandboxKey]["checkFileExi const path = file; if (path.indexOf(sep) === -1) { - throw new Error( - "ブラウザ版ではフォルダを固定しないファイルへのアクセスをサポートしていません" - ); + return Promise.resolve(false); } const fileName = resolveFileName(path); From 65d91dac50fb428664cae1c66d9f05674aceaf9e Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 8 Jul 2023 23:09:16 +0900 Subject: [PATCH 71/75] =?UTF-8?q?=E5=8D=98=E4=BD=93=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=AE=E6=9B=B8=E3=81=8D=E5=87=BA=E3=81=97?= =?UTF-8?q?=E3=81=AF=E3=80=81=E5=85=B1=E9=80=9A=E3=82=B3=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=A7=E7=94=9F=E6=88=90=E3=81=95=E3=82=8C=E3=82=8B=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E5=90=8D=E3=82=92=E4=BD=BF=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 68 ----------------------------------------- src/browser/sandbox.ts | 42 +++++++++++++++++++++---- 2 files changed, 36 insertions(+), 74 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 48a6a3641c..1059a3e4de 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -54,21 +54,6 @@ const showWritableDirectoryPicker = async (): Promise< // キャンセルするとエラーが投げられる .catch(() => undefined); // FIXME: このままだとダイアログ表示エラーと見分けがつかない -const showWritableFilePicker = async ({ - suggestedName, - fileType, -}: { - suggestedName?: string; - fileType: AcceptFileType; -}) => - window - .showSaveFilePicker({ - types: [fileType], - excludeAcceptAllOption: true, - suggestedName, - }) - .catch(() => undefined); - const showLoadableFilePicker = async ({ fileType, }: { @@ -82,25 +67,6 @@ const showLoadableFilePicker = async ({ }) .catch(() => undefined); -const requestSaveFileNameWithDirectoryPermission = async ({ - suggestedName, - fileType, -}: { - suggestedName?: string; - fileType: AcceptFileType; -}) => { - const fileHandle = await showWritableFilePicker({ - suggestedName, - fileType, - }); - if (fileHandle === undefined) { - return undefined; - } - - // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない - return fileHandle.name; -}; - const requestLoadFileNameWithDirectoryPermission = async ({ fileType, }: { @@ -117,27 +83,6 @@ const requestLoadFileNameWithDirectoryPermission = async ({ return fileHandle.map((v) => v.name); }; -export const showAudioSaveDialogImpl: typeof window[typeof SandboxKey]["showAudioSaveDialog"] = - async (obj: { title: string; defaultPath?: string }) => { - return requestSaveFileNameWithDirectoryPermission({ - suggestedName: obj.defaultPath, - fileType: { description: "Wave File", accept: { "audio/wav": [".wav"] } }, - }); - }; - -export const showTextSaveDialogImpl: typeof window[typeof SandboxKey]["showTextSaveDialog"] = - async (obj: { title: string; defaultPath?: string }) => { - return requestSaveFileNameWithDirectoryPermission({ - suggestedName: obj.defaultPath, - fileType: { - description: "Text File", - accept: { - "text/plain": [".txt"], - }, - }, - }); - }; - export const showOpenDirectoryDialogImpl: typeof window[typeof SandboxKey]["showOpenDirectoryDialog"] = async () => { const _directoryHandler = await showWritableDirectoryPicker(); @@ -152,19 +97,6 @@ export const showOpenDirectoryDialogImpl: typeof window[typeof SandboxKey]["show return _directoryHandler.name; }; -export const showProjectSaveDialogImpl: typeof window[typeof SandboxKey]["showProjectSaveDialog"] = - async (obj: { title: string; defaultPath?: string }) => { - return requestSaveFileNameWithDirectoryPermission({ - suggestedName: obj.defaultPath, - fileType: { - description: "VOICEVOX Project file", - accept: { - "application/json": [".vvproj"], - }, - }, - }); - }; - export const showProjectLoadDialogImpl: typeof window[typeof SandboxKey]["showProjectLoadDialog"] = async () => { return requestLoadFileNameWithDirectoryPermission({ diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 817072319c..83c12a3f4f 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -1,12 +1,9 @@ import { defaultEngine } from "./contract"; import { checkFileExistsImpl, - showAudioSaveDialogImpl, showImportFileDialogImpl, showOpenDirectoryDialogImpl, showProjectLoadDialogImpl, - showProjectSaveDialogImpl, - showTextSaveDialogImpl, writeFileImpl, } from "./fileImpl"; import { getSettingEntry, setSettingEntry } from "./storeImpl"; @@ -81,10 +78,32 @@ export const api: Sandbox = { return Promise.resolve({}); }, showAudioSaveDialog(obj: { title: string; defaultPath?: string }) { - return showAudioSaveDialogImpl(obj); + return new Promise((resolve, reject) => { + if (obj.defaultPath === undefined) { + reject( + // storeやvue componentからdefaultPathを設定していなかったらrejectされる + new Error( + "ブラウザ版ではファイルの保存機能が一部サポートされていません。" + ) + ); + } else { + resolve(obj.defaultPath); + } + }); }, showTextSaveDialog(obj: { title: string; defaultPath?: string }) { - return showTextSaveDialogImpl(obj); + return new Promise((resolve, reject) => { + if (obj.defaultPath === undefined) { + reject( + // storeやvue componentからdefaultPathを設定していなかったらrejectされる + new Error( + "ブラウザ版ではファイルの保存機能が一部サポートされていません。" + ) + ); + } else { + resolve(obj.defaultPath); + } + }); }, showVvppOpenDialog(obj: { title: string; defaultPath?: string }) { // NOTE: 今後接続先を変える手段としてVvppが使われるかもしれないので、そのタイミングで実装する @@ -96,7 +115,18 @@ export const api: Sandbox = { return showOpenDirectoryDialogImpl(obj); }, showProjectSaveDialog(obj: { title: string; defaultPath?: string }) { - return showProjectSaveDialogImpl(obj); + return new Promise((resolve, reject) => { + if (obj.defaultPath === undefined) { + reject( + // storeやvue componentからdefaultPathを設定していなかったらrejectされる + new Error( + "ブラウザ版ではファイルの保存機能が一部サポートされていません。" + ) + ); + } else { + resolve(obj.defaultPath); + } + }); }, showProjectLoadDialog(obj: { title: string }) { return showProjectLoadDialogImpl(obj); From b5d9294687344d1fd45d53380944159a64b9fc3b Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 8 Jul 2023 23:17:00 +0900 Subject: [PATCH 72/75] =?UTF-8?q?=E5=AE=9F=E8=A1=8C=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E3=82=92README=E3=81=AB=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 744072fef1..5d34b9408e 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,14 @@ npm run electron:serve 音声合成エンジンのリポジトリはこちらです +### ブラウザ版の実行(開発中) + +別途音声合成エンジンを起動し、以下を実行して表示された localhost へアクセスします。 + +```bash +npm run browser:serve +``` + ## ビルド ```bash From 2db58411825e88170e528b7f65693e7ce5e6cdea Mon Sep 17 00:00:00 2001 From: yamachu Date: Sat, 8 Jul 2023 23:30:21 +0900 Subject: [PATCH 73/75] =?UTF-8?q?writeFile=E3=81=AE=E5=9E=8B=E3=81=8C?= =?UTF-8?q?=E5=A4=89=E3=82=8F=E3=81=A3=E3=81=9F=E3=81=93=E3=81=A8=E3=81=B8?= =?UTF-8?q?=E3=81=AE=E8=BF=BD=E5=BE=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 1059a3e4de..6d0fe97596 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -1,7 +1,8 @@ import { sep } from "path"; import { directoryHandleStoreKey } from "./contract"; import { openDB } from "./storeImpl"; -import { SandboxKey, WriteFileErrorResult } from "@/type/preload"; +import { SandboxKey } from "@/type/preload"; +import { failure, success } from "@/type/result"; const storeDirectoryHandle = async ( directoryHandle: FileSystemDirectoryHandle @@ -176,7 +177,7 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = aTag.click(); document.body.removeChild(aTag); URL.revokeObjectURL(blob); - return; + return success(undefined); } const fileName = resolveFileName(path); @@ -195,12 +196,9 @@ export const writeFileImpl: typeof window[typeof SandboxKey]["writeFile"] = await writable.write(obj.buffer); return writable.close(); }) - .then(() => undefined) + .then(() => success(undefined)) .catch((e) => { - return { - code: undefined, - message: e.message as string, - } as WriteFileErrorResult; + return failure(e); }); }; From 4c0aefebf960af85cd9d373645178397c0f94a4a Mon Sep 17 00:00:00 2001 From: yamachu Date: Mon, 10 Jul 2023 23:56:31 +0900 Subject: [PATCH 74/75] =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=81=BF=E3=81=AF=E3=82=B5?= =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=88=E3=81=97=E3=81=A6=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=81=97=E3=80=81=E9=96=A2=E9=80=A3=E3=81=99=E3=82=8B=E3=83=80?= =?UTF-8?q?=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0=E3=81=AF=E4=B8=80=E6=97=A6?= =?UTF-8?q?=E6=B6=88=E3=81=97=E3=81=A6=E3=81=97=E3=81=BE=E3=81=8A=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 58 ----------------------------------------- src/browser/sandbox.ts | 14 +++++----- 2 files changed, 8 insertions(+), 64 deletions(-) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 6d0fe97596..73589e7c87 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -40,11 +40,6 @@ const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { const directoryHandleMap: Map = new Map(); -type AcceptFileType = { - description: string; - accept: Record; -}; - const showWritableDirectoryPicker = async (): Promise< FileSystemDirectoryHandle | undefined > => @@ -55,35 +50,6 @@ const showWritableDirectoryPicker = async (): Promise< // キャンセルするとエラーが投げられる .catch(() => undefined); // FIXME: このままだとダイアログ表示エラーと見分けがつかない -const showLoadableFilePicker = async ({ - fileType, -}: { - fileType: AcceptFileType; -}) => - window - .showOpenFilePicker({ - types: [fileType], - excludeAcceptAllOption: true, - multiple: false, - }) - .catch(() => undefined); - -const requestLoadFileNameWithDirectoryPermission = async ({ - fileType, -}: { - fileType: AcceptFileType; -}) => { - const fileHandle = await showLoadableFilePicker({ - fileType, - }); - if (fileHandle === undefined) { - return undefined; - } - - // NOTE: ディレクトリのハンドラと異なるディレクトリを選択されても検知できない - return fileHandle.map((v) => v.name); -}; - export const showOpenDirectoryDialogImpl: typeof window[typeof SandboxKey]["showOpenDirectoryDialog"] = async () => { const _directoryHandler = await showWritableDirectoryPicker(); @@ -98,30 +64,6 @@ export const showOpenDirectoryDialogImpl: typeof window[typeof SandboxKey]["show return _directoryHandler.name; }; -export const showProjectLoadDialogImpl: typeof window[typeof SandboxKey]["showProjectLoadDialog"] = - async () => { - return requestLoadFileNameWithDirectoryPermission({ - fileType: { - description: "VOICEVOX Project file", - accept: { - "application/json": [".vvproj"], - }, - }, - }); - }; - -export const showImportFileDialogImpl: typeof window[typeof SandboxKey]["showImportFileDialog"] = - async () => { - return requestLoadFileNameWithDirectoryPermission({ - fileType: { - description: "Text", - accept: { - "text/plain": [".txt"], - }, - }, - }).then((v) => v?.[0]); - }; - // separator 以前の文字列はディレクトリ名として扱う const resolveDirectoryName = (path: string) => path.split(sep)[0]; diff --git a/src/browser/sandbox.ts b/src/browser/sandbox.ts index 83c12a3f4f..2353dc7656 100644 --- a/src/browser/sandbox.ts +++ b/src/browser/sandbox.ts @@ -1,9 +1,7 @@ import { defaultEngine } from "./contract"; import { checkFileExistsImpl, - showImportFileDialogImpl, showOpenDirectoryDialogImpl, - showProjectLoadDialogImpl, writeFileImpl, } from "./fileImpl"; import { getSettingEntry, setSettingEntry } from "./storeImpl"; @@ -128,8 +126,10 @@ export const api: Sandbox = { } }); }, - showProjectLoadDialog(obj: { title: string }) { - return showProjectLoadDialogImpl(obj); + showProjectLoadDialog(/* obj: { title: string } */) { + throw new Error( + "ブラウザ版では現在ファイルの読み込みをサポートしていません" + ); }, showMessageDialog(obj: { type: "none" | "info" | "error" | "question" | "warning"; @@ -155,8 +155,10 @@ export const api: Sandbox = { `Not implemented: showQuestionDialog, request: ${JSON.stringify(obj)}` ); }, - showImportFileDialog(obj: { title: string }) { - return showImportFileDialogImpl(obj); + showImportFileDialog(/* obj: { title: string } */) { + throw new Error( + "ブラウザ版では現在ファイルの読み込みをサポートしていません" + ); }, writeFile(obj: { filePath: string; buffer: ArrayBuffer }) { return writeFileImpl(obj); From 8d45eec5ec89e4706b4052164ec09628b3ebd3b3 Mon Sep 17 00:00:00 2001 From: yamachu Date: Tue, 11 Jul 2023 00:03:57 +0900 Subject: [PATCH 75/75] =?UTF-8?q?directoryHandleMap=E3=81=A3=E3=81=A6?= =?UTF-8?q?=E4=BD=95=E3=81=AE=E3=81=9F=E3=82=81=E3=81=AB=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=81=AE=EF=BC=9F=E3=82=92=E6=98=8E=E3=82=89?= =?UTF-8?q?=E3=81=8B=E3=81=AB=E3=81=97=E3=81=A6=E3=81=8A=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/fileImpl.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/browser/fileImpl.ts b/src/browser/fileImpl.ts index 73589e7c87..3f8efdc527 100644 --- a/src/browser/fileImpl.ts +++ b/src/browser/fileImpl.ts @@ -38,6 +38,9 @@ const fetchStoredDirectoryHandle = async (maybeDirectoryHandleName: string) => { ); }; +// ディレクトリ名をキーとして、Write権限を持ったFileSystemDirectoryHandleを保持する +// writeFileに`ディレクトリ名/ファイル名`の形式でfilePathが渡ってくるため、 +// `/`で区切ってディレクトリ名を取得し、そのディレクトリ名をキーとしてdirectoryHandleMapからハンドラを取得し、fileWriteを可能にする const directoryHandleMap: Map = new Map(); const showWritableDirectoryPicker = async (): Promise<