From 5090efcae6060d2a1e503f79c06a25a37236b185 Mon Sep 17 00:00:00 2001 From: sevenc-nanashi Date: Thu, 27 Feb 2025 15:10:17 +0900 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20=E8=A4=87=E8=A3=BD=E3=81=A8?= =?UTF-8?q?=E3=81=97=E3=81=A6=E4=BF=9D=E5=AD=98=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Menu/MenuBar/MenuBar.vue | 18 +++ src/domain/hotkeyAction.ts | 9 +- src/store/project.ts | 200 ++++++++++++++---------- src/store/type.ts | 4 + 4 files changed, 147 insertions(+), 84 deletions(-) diff --git a/src/components/Menu/MenuBar/MenuBar.vue b/src/components/Menu/MenuBar/MenuBar.vue index 22fd164ce6..2cfb5bffab 100644 --- a/src/components/Menu/MenuBar/MenuBar.vue +++ b/src/components/Menu/MenuBar/MenuBar.vue @@ -152,6 +152,12 @@ const saveProjectAs = async () => { } }; +const saveProjectCopy = async () => { + if (!uiLocked.value) { + await store.actions.SAVE_PROJECT_FILE_AS_COPY({}); + } +}; + const importProject = () => { if (!uiLocked.value) { void store.actions.LOAD_PROJECT_FILE({ type: "dialog" }); @@ -338,6 +344,14 @@ const menudata = computed(() => [ }, disableWhenUiLocked: true, }, + { + type: "button", + label: "プロジェクトを複製として保存", + onClick: async () => { + await saveProjectCopy(); + }, + disableWhenUiLocked: true, + }, { type: "button", label: "プロジェクトを読み込む", @@ -585,6 +599,10 @@ registerHotkeyForAllEditors({ callback: saveProjectAs, name: "プロジェクトを名前を付けて保存", }); +registerHotkeyForAllEditors({ + callback: saveProjectCopy, + name: "プロジェクトを複製として保存", +}); registerHotkeyForAllEditors({ callback: importProject, name: "プロジェクトを読み込む", diff --git a/src/domain/hotkeyAction.ts b/src/domain/hotkeyAction.ts index 6c92c63556..547984009a 100644 --- a/src/domain/hotkeyAction.ts +++ b/src/domain/hotkeyAction.ts @@ -28,6 +28,7 @@ export const hotkeyActionNameSchema = z.enum([ "新規プロジェクト", "プロジェクトを名前を付けて保存", "プロジェクトを上書き保存", + "プロジェクトを複製として保存", "プロジェクトを読み込む", "テキストを読み込む", "全体のイントネーションをリセット", @@ -146,13 +147,17 @@ export function getDefaultHotkeySettings({ action: "全画面表示を切り替え", combination: HotkeyCombination(!isMac ? "F11" : "Ctrl Meta F"), }, + { + action: "プロジェクトを上書き保存", + combination: HotkeyCombination(!isMac ? "Ctrl S" : "Meta S"), + }, { action: "プロジェクトを名前を付けて保存", combination: HotkeyCombination(!isMac ? "Ctrl Shift S" : "Shift Meta S"), }, { - action: "プロジェクトを上書き保存", - combination: HotkeyCombination(!isMac ? "Ctrl S" : "Meta S"), + action: "プロジェクトを複製として保存", + combination: HotkeyCombination(!isMac ? "Ctrl Alt S" : "Alt Meta S"), }, { action: "プロジェクトを読み込む", diff --git a/src/store/project.ts b/src/store/project.ts index 9bf44f82c6..ed93350d2d 100755 --- a/src/store/project.ts +++ b/src/store/project.ts @@ -29,6 +29,7 @@ import { } from "@/components/Dialog/Dialog"; import { uuid4 } from "@/helpers/random"; import { getAppInfos } from "@/domain/appInfo"; +import { errorToMessage } from "@/helpers/errorHelper"; export const projectStoreState: ProjectStoreState = { savedLastCommandIds: { talk: null, song: null }, @@ -246,104 +247,139 @@ export const projectStore = createPartialStore({ ), }, - SAVE_PROJECT_FILE: { + SAVE_PROJECT_FILE_AS_COPY: { /** - * プロジェクトファイルを保存する。保存の成否が返る。 + * プロジェクトファイルを複製として保存する。保存の成否が返る。 * エラー発生時はダイアログが表示される。 */ - action: createUILockAction( - async (context, { overwrite }: { overwrite?: boolean }) => { - let filePath = context.state.projectFilePath; - try { - if (!overwrite || !filePath) { - let defaultPath: string; - - if (!filePath) { - // if new project: use generated name - defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; - } else { - // if saveAs for existing project: use current project path - defaultPath = filePath; - } - - // Write the current status to a project file. - const ret = await window.backend.showSaveFileDialog({ - title: "プロジェクトファイルの保存", - name: "VOICEVOX Project file", - extensions: ["vvproj"], - defaultPath, - }); - if (ret == undefined) { - return false; - } - filePath = ret; - } - if ( - context.state.projectFilePath && - context.state.projectFilePath != filePath - ) { - await showMessageDialog({ - type: "info", - title: "保存", - message: `編集中のプロジェクトが ${filePath} に切り替わりました。`, - }); + action: createUILockAction(async (context, { filePath: filePathArg }) => { + let filePath = filePathArg; + try { + if (!filePath) { + let defaultPath: string; + + if (!filePath) { + // if new project: use generated name + defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; + } else { + // if saveAs for existing project: use current project path + defaultPath = filePath; } - await context.actions.APPEND_RECENTLY_USED_PROJECT({ - filePath, + // Write the current status to a project file. + const ret = await window.backend.showSaveFileDialog({ + title: "プロジェクトファイルの保存", + name: "VOICEVOX Project file", + extensions: ["vvproj"], + defaultPath, }); - const appVersion = getAppInfos().version; - const { - audioItems, + if (ret == undefined) { + return false; + } + filePath = ret; + } + + const appVersion = getAppInfos().version; + const { + audioItems, + audioKeys, + tpqn, + tempos, + timeSignatures, + tracks, + trackOrder, + } = context.state; + const projectData: LatestProjectType = { + appVersion, + talk: { audioKeys, + audioItems, + }, + song: { tpqn, tempos, timeSignatures, - tracks, + tracks: Object.fromEntries(tracks), trackOrder, - } = context.state; - const projectData: LatestProjectType = { - appVersion, - talk: { - audioKeys, - audioItems, - }, - song: { - tpqn, - tempos, - timeSignatures, - tracks: Object.fromEntries(tracks), - trackOrder, - }, - }; - - const buf = new TextEncoder().encode( - JSON.stringify(projectData), - ).buffer; - await window.backend - .writeFile({ - filePath, - buffer: buf, - }) - .then(getValueOrThrow); + }, + }; + + const buf = new TextEncoder().encode( + JSON.stringify(projectData), + ).buffer; + await window.backend + .writeFile({ + filePath, + buffer: buf, + }) + .then(getValueOrThrow); + return true; + } catch (err) { + window.backend.logError(err); + await showAlertDialog({ + title: "エラー", + message: `プロジェクトファイルの保存に失敗しました。\n${errorToMessage(err)}`, + }); + return false; + } + }), + }, + + SAVE_PROJECT_FILE: { + /** + * プロジェクトファイルを保存し、現在のプロジェクトファイルのパスを更新する。保存の成否が返る。 + */ + action: createUILockAction( + async (context, { overwrite }: { overwrite?: boolean }) => { + let filePath = context.state.projectFilePath; + if (!overwrite || !filePath) { + let defaultPath: string; + + if (!filePath) { + // if new project: use generated name + defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; + } else { + // if saveAs for existing project: use current project path + defaultPath = filePath; + } + + // Write the current status to a project file. + const ret = await window.backend.showSaveFileDialog({ + title: "プロジェクトファイルの保存", + name: "VOICEVOX Project file", + extensions: ["vvproj"], + defaultPath, + }); + if (ret == undefined) { + return false; + } + filePath = ret; + } + if ( + context.state.projectFilePath && + context.state.projectFilePath != filePath + ) { + await showMessageDialog({ + type: "info", + title: "保存", + message: `編集中のプロジェクトが ${filePath} に切り替わりました。`, + }); + } + + await context.actions.APPEND_RECENTLY_USED_PROJECT({ + filePath, + }); + const result = await context.actions.SAVE_PROJECT_FILE_AS_COPY({ + filePath, + }); + if (result) { context.mutations.SET_PROJECT_FILEPATH({ filePath }); context.mutations.SET_SAVED_LAST_COMMAND_IDS( context.getters.LAST_COMMAND_IDS, ); - return true; - } catch (err) { - window.backend.logError(err); - const message = (() => { - if (typeof err === "string") return err; - if (!(err instanceof Error)) return "エラーが発生しました。"; - return err.message; - })(); - await showAlertDialog({ - title: "エラー", - message: `プロジェクトファイルの保存に失敗しました。\n${message}`, - }); - return false; } + + return result; }, ), }, diff --git a/src/store/type.ts b/src/store/type.ts index c39a074478..e26ab779fd 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -1853,6 +1853,10 @@ export type ProjectStoreTypes = { action(payload: { overwrite?: boolean }): boolean; }; + SAVE_PROJECT_FILE_AS_COPY: { + action(payload: { filePath?: string }): boolean; + }; + SAVE_OR_DISCARD_PROJECT_FILE: { action(palyoad: { additionalMessage?: string; From e8f2fdcc4dd5784c5df0ff7ba17540ea5b991d81 Mon Sep 17 00:00:00 2001 From: sevenc-nanashi Date: Thu, 27 Feb 2025 15:32:46 +0900 Subject: [PATCH 02/19] =?UTF-8?q?test:=20snapshot=E3=82=92=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/__snapshots__/configManager.spec.ts.snap | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/unit/backend/common/__snapshots__/configManager.spec.ts.snap b/tests/unit/backend/common/__snapshots__/configManager.spec.ts.snap index 23598f3943..748860aa41 100644 --- a/tests/unit/backend/common/__snapshots__/configManager.spec.ts.snap +++ b/tests/unit/backend/common/__snapshots__/configManager.spec.ts.snap @@ -19,7 +19,7 @@ exports[`0.13.0からマイグレーションできる 1`] = ` "enablePreset": false, "enableRubyNotation": false, "engineSettings": { - "00000000-0000-0000-0000-000000000000": { + "074fc39e-678b-4c13-8916-ffca8d505d1d": { "outputSamplingRate": "engineDefault", "useGpu": false, }, @@ -112,13 +112,17 @@ exports[`0.13.0からマイグレーションできる 1`] = ` "action": "全画面表示を切り替え", "combination": "F11", }, + { + "action": "プロジェクトを上書き保存", + "combination": "Ctrl S", + }, { "action": "プロジェクトを名前を付けて保存", "combination": "Ctrl Shift S", }, { - "action": "プロジェクトを上書き保存", - "combination": "Ctrl S", + "action": "プロジェクトを複製として保存", + "combination": "Ctrl Alt S", }, { "action": "プロジェクトを読み込む", From e1d6cae4745c04965507c6b2863e499e86c3eb8e Mon Sep 17 00:00:00 2001 From: sevenc-nanashi Date: Thu, 27 Feb 2025 15:46:15 +0900 Subject: [PATCH 03/19] =?UTF-8?q?test:=20.env.test.local=E3=81=8C=E4=B8=8A?= =?UTF-8?q?=E6=9B=B8=E3=81=8D=E3=81=97=E3=81=A6=E3=81=84=E3=81=9F...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/common/__snapshots__/configManager.spec.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/backend/common/__snapshots__/configManager.spec.ts.snap b/tests/unit/backend/common/__snapshots__/configManager.spec.ts.snap index 748860aa41..a73d4a5ec4 100644 --- a/tests/unit/backend/common/__snapshots__/configManager.spec.ts.snap +++ b/tests/unit/backend/common/__snapshots__/configManager.spec.ts.snap @@ -19,7 +19,7 @@ exports[`0.13.0からマイグレーションできる 1`] = ` "enablePreset": false, "enableRubyNotation": false, "engineSettings": { - "074fc39e-678b-4c13-8916-ffca8d505d1d": { + "00000000-0000-0000-0000-000000000000": { "outputSamplingRate": "engineDefault", "useGpu": false, }, From 79f66d90cdd63415fc9635876ac6a202551360d8 Mon Sep 17 00:00:00 2001 From: sevenc-nanashi Date: Thu, 27 Feb 2025 16:39:44 +0900 Subject: [PATCH 04/19] =?UTF-8?q?chore:=20=E9=A0=86=E5=BA=8F=E3=82=92?= =?UTF-8?q?=E6=8F=83=E3=81=88=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/hotkeyAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/hotkeyAction.ts b/src/domain/hotkeyAction.ts index 547984009a..f9bc1dd192 100644 --- a/src/domain/hotkeyAction.ts +++ b/src/domain/hotkeyAction.ts @@ -26,8 +26,8 @@ export const hotkeyActionNameSchema = z.enum([ "元に戻す", "やり直す", "新規プロジェクト", - "プロジェクトを名前を付けて保存", "プロジェクトを上書き保存", + "プロジェクトを名前を付けて保存", "プロジェクトを複製として保存", "プロジェクトを読み込む", "テキストを読み込む", From 02d5074bbca1837005f5764e14e25916c841cb08 Mon Sep 17 00:00:00 2001 From: sevenc-nanashi Date: Thu, 27 Feb 2025 16:48:37 +0900 Subject: [PATCH 05/19] =?UTF-8?q?chore:=20=E9=96=A2=E6=95=B0=E3=82=92?= =?UTF-8?q?=E5=88=86=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/project.ts | 133 ++++++++++++++++++++----------------------- src/store/type.ts | 8 +++ 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/src/store/project.ts b/src/store/project.ts index ed93350d2d..2413d703ba 100755 --- a/src/store/project.ts +++ b/src/store/project.ts @@ -256,63 +256,12 @@ export const projectStore = createPartialStore({ let filePath = filePathArg; try { if (!filePath) { - let defaultPath: string; - + filePath = await context.actions.PROMPT_PROJECT_SAVE_FILE_PATH({}); if (!filePath) { - // if new project: use generated name - defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; - } else { - // if saveAs for existing project: use current project path - defaultPath = filePath; - } - - // Write the current status to a project file. - const ret = await window.backend.showSaveFileDialog({ - title: "プロジェクトファイルの保存", - name: "VOICEVOX Project file", - extensions: ["vvproj"], - defaultPath, - }); - if (ret == undefined) { return false; } - filePath = ret; } - const appVersion = getAppInfos().version; - const { - audioItems, - audioKeys, - tpqn, - tempos, - timeSignatures, - tracks, - trackOrder, - } = context.state; - const projectData: LatestProjectType = { - appVersion, - talk: { - audioKeys, - audioItems, - }, - song: { - tpqn, - tempos, - timeSignatures, - tracks: Object.fromEntries(tracks), - trackOrder, - }, - }; - - const buf = new TextEncoder().encode( - JSON.stringify(projectData), - ).buffer; - await window.backend - .writeFile({ - filePath, - buffer: buf, - }) - .then(getValueOrThrow); return true; } catch (err) { window.backend.logError(err); @@ -333,27 +282,12 @@ export const projectStore = createPartialStore({ async (context, { overwrite }: { overwrite?: boolean }) => { let filePath = context.state.projectFilePath; if (!overwrite || !filePath) { - let defaultPath: string; - - if (!filePath) { - // if new project: use generated name - defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; - } else { - // if saveAs for existing project: use current project path - defaultPath = filePath; - } - - // Write the current status to a project file. - const ret = await window.backend.showSaveFileDialog({ - title: "プロジェクトファイルの保存", - name: "VOICEVOX Project file", - extensions: ["vvproj"], - defaultPath, + filePath = await context.actions.PROMPT_PROJECT_SAVE_FILE_PATH({ + defaultFilePath: filePath, }); - if (ret == undefined) { + if (!filePath) { return false; } - filePath = ret; } if ( context.state.projectFilePath && @@ -384,6 +318,65 @@ export const projectStore = createPartialStore({ ), }, + PROMPT_PROJECT_SAVE_FILE_PATH: { + async action(context, { defaultFilePath }) { + let defaultPath: string; + + if (!defaultFilePath) { + // if new project: use generated name + defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; + } else { + // if saveAs for existing project: use current project path + defaultPath = defaultFilePath; + } + + // Write the current status to a project file. + return await window.backend.showSaveFileDialog({ + title: "プロジェクトファイルの保存", + name: "VOICEVOX Project file", + extensions: ["vvproj"], + defaultPath, + }); + }, + }, + + WRITE_PROJECT_FILE: { + action: createUILockAction(async (context, { filePath }) => { + const appVersion = getAppInfos().version; + const { + audioItems, + audioKeys, + tpqn, + tempos, + timeSignatures, + tracks, + trackOrder, + } = context.state; + const projectData: LatestProjectType = { + appVersion, + talk: { + audioKeys, + audioItems, + }, + song: { + tpqn, + tempos, + timeSignatures, + tracks: Object.fromEntries(tracks), + trackOrder, + }, + }; + + const buf = new TextEncoder().encode(JSON.stringify(projectData)).buffer; + await window.backend + .writeFile({ + filePath, + buffer: buf, + }) + .then(getValueOrThrow); + }), + }, + /** * プロジェクトファイルを保存するか破棄するかキャンセルするかのダイアログを出して、保存する場合は保存する。 * 何を選択したかが返る。 diff --git a/src/store/type.ts b/src/store/type.ts index e26ab779fd..60c633f41c 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -1857,6 +1857,14 @@ export type ProjectStoreTypes = { action(payload: { filePath?: string }): boolean; }; + PROMPT_PROJECT_SAVE_FILE_PATH: { + action(payload: { defaultFilePath?: string }): Promise; + }; + + WRITE_PROJECT_FILE: { + action(payload: { filePath: string }): Promise; + }; + SAVE_OR_DISCARD_PROJECT_FILE: { action(palyoad: { additionalMessage?: string; From 3f80b219119d7a690a8caaea5acf74f55bb55cb8 Mon Sep 17 00:00:00 2001 From: Hiroshiba Kazuyuki Date: Thu, 27 Feb 2025 22:03:20 +0900 Subject: [PATCH 06/19] =?UTF-8?q?refactor:=20=E3=83=97=E3=83=AD=E3=82=B8?= =?UTF-8?q?=E3=82=A7=E3=82=AF=E3=83=88=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E3=82=92=E4=BF=9D=E5=AD=98=E3=81=99=E3=82=8B=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E3=82=92=E3=83=A6=E3=83=BC=E3=82=B9=E3=82=B1=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=81=AB=E5=BF=9C=E3=81=98=E3=81=A6=EF=BC=93=E3=81=A4=E3=81=AB?= =?UTF-8?q?=E5=88=86=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/project.ts | 142 ++++++++++++++++++++++++------------------- src/store/type.ts | 12 ++-- 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/src/store/project.ts b/src/store/project.ts index 2413d703ba..58c254dca0 100755 --- a/src/store/project.ts +++ b/src/store/project.ts @@ -247,75 +247,81 @@ export const projectStore = createPartialStore({ ), }, - SAVE_PROJECT_FILE_AS_COPY: { + SAVE_PROJECT_FILE_OVERWRITE: { /** - * プロジェクトファイルを複製として保存する。保存の成否が返る。 + * プロジェクトファイルを上書き保存する。 + * 現在のプロジェクトファイルが未設定の場合は名前をつけて保存する。 * エラー発生時はダイアログが表示される。 */ - action: createUILockAction(async (context, { filePath: filePathArg }) => { - let filePath = filePathArg; - try { - if (!filePath) { - filePath = await context.actions.PROMPT_PROJECT_SAVE_FILE_PATH({}); - if (!filePath) { - return false; - } - } - - return true; - } catch (err) { - window.backend.logError(err); - await showAlertDialog({ - title: "エラー", - message: `プロジェクトファイルの保存に失敗しました。\n${errorToMessage(err)}`, - }); - return false; + action: createUILockAction(async (context) => { + const filePath = context.state.projectFilePath; + if (!filePath) { + await context.actions.SAVE_PROJECT_FILE_AS({}); + return; } + + const result = await context.actions.WRITE_PROJECT_FILE({ + filePath, + }); + if (!result) return; + + await context.actions.APPEND_RECENTLY_USED_PROJECT({ + filePath, + }); + context.mutations.SET_SAVED_LAST_COMMAND_IDS( + context.getters.LAST_COMMAND_IDS, + ); }), }, - SAVE_PROJECT_FILE: { + SAVE_PROJECT_FILE_AS: { /** - * プロジェクトファイルを保存し、現在のプロジェクトファイルのパスを更新する。保存の成否が返る。 + * プロジェクトファイルを名前をつけて保存し、現在のプロジェクトファイルのパスを更新する。 + * エラー発生時はダイアログが表示される。 */ - action: createUILockAction( - async (context, { overwrite }: { overwrite?: boolean }) => { - let filePath = context.state.projectFilePath; - if (!overwrite || !filePath) { - filePath = await context.actions.PROMPT_PROJECT_SAVE_FILE_PATH({ - defaultFilePath: filePath, - }); - if (!filePath) { - return false; - } - } - if ( - context.state.projectFilePath && - context.state.projectFilePath != filePath - ) { - await showMessageDialog({ - type: "info", - title: "保存", - message: `編集中のプロジェクトが ${filePath} に切り替わりました。`, - }); - } + action: createUILockAction(async (context, {}) => { + const filePath = await context.actions.PROMPT_PROJECT_SAVE_FILE_PATH({}); + if (!filePath) return; - await context.actions.APPEND_RECENTLY_USED_PROJECT({ - filePath, - }); - const result = await context.actions.SAVE_PROJECT_FILE_AS_COPY({ - filePath, + const result = await context.actions.WRITE_PROJECT_FILE({ + filePath, + }); + if (!result) return; + + if ( + context.state.projectFilePath && + context.state.projectFilePath != filePath + ) { + context.mutations.SET_PROJECT_FILEPATH({ filePath }); + await showMessageDialog({ + type: "info", + title: "保存", + message: `編集中のプロジェクトが ${filePath} に切り替わりました。`, }); - if (result) { - context.mutations.SET_PROJECT_FILEPATH({ filePath }); - context.mutations.SET_SAVED_LAST_COMMAND_IDS( - context.getters.LAST_COMMAND_IDS, - ); - } + } - return result; - }, - ), + await context.actions.APPEND_RECENTLY_USED_PROJECT({ + filePath, + }); + context.mutations.SET_SAVED_LAST_COMMAND_IDS( + context.getters.LAST_COMMAND_IDS, + ); + }), + }, + + SAVE_PROJECT_FILE_AS_COPY: { + /** + * プロジェクトファイルを複製として保存する。 + * エラー発生時はダイアログが表示される。 + */ + action: createUILockAction(async (context, {}) => { + const filePath = await context.actions.PROMPT_PROJECT_SAVE_FILE_PATH({}); + if (!filePath) return; + + await context.actions.WRITE_PROJECT_FILE({ + filePath, + }); + }), }, PROMPT_PROJECT_SAVE_FILE_PATH: { @@ -368,12 +374,22 @@ export const projectStore = createPartialStore({ }; const buf = new TextEncoder().encode(JSON.stringify(projectData)).buffer; - await window.backend - .writeFile({ - filePath, - buffer: buf, - }) - .then(getValueOrThrow); + try { + await window.backend + .writeFile({ + filePath, + buffer: buf, + }) + .then(getValueOrThrow); + return true; + } catch (err) { + window.backend.logError(err); + await showAlertDialog({ + title: "エラー", + message: `プロジェクトファイルの保存に失敗しました。\n${errorToMessage(err)}`, + }); + return false; + } }), }, diff --git a/src/store/type.ts b/src/store/type.ts index 60c633f41c..10babb508a 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -1849,12 +1849,16 @@ export type ProjectStoreTypes = { ): boolean; }; - SAVE_PROJECT_FILE: { - action(payload: { overwrite?: boolean }): boolean; + SAVE_PROJECT_FILE_OVERWRITE: { + action(payload: {}): void; + }; + + SAVE_PROJECT_FILE_AS: { + action(payload: {}): void; }; SAVE_PROJECT_FILE_AS_COPY: { - action(payload: { filePath?: string }): boolean; + action(payload: {}): void; }; PROMPT_PROJECT_SAVE_FILE_PATH: { @@ -1862,7 +1866,7 @@ export type ProjectStoreTypes = { }; WRITE_PROJECT_FILE: { - action(payload: { filePath: string }): Promise; + action(payload: { filePath: string }): Promise; }; SAVE_OR_DISCARD_PROJECT_FILE: { From 58fd8cb5a93995ac99be228c801af598fd731435 Mon Sep 17 00:00:00 2001 From: Hiroshiba Kazuyuki Date: Thu, 27 Feb 2025 22:04:17 +0900 Subject: [PATCH 07/19] =?UTF-8?q?project/index.ts=E3=81=AB=E7=A7=BB?= =?UTF-8?q?=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/{project.ts => project/index.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/store/{project.ts => project/index.ts} (99%) diff --git a/src/store/project.ts b/src/store/project/index.ts similarity index 99% rename from src/store/project.ts rename to src/store/project/index.ts index 58c254dca0..e4217380b7 100755 --- a/src/store/project.ts +++ b/src/store/project/index.ts @@ -1,4 +1,4 @@ -import { createPartialStore, DotNotationDispatch } from "./vuex"; +import { createPartialStore, DotNotationDispatch } from "../vuex"; import { createUILockAction } from "@/store/ui"; import { AllActions, From 320318f5f6cdfab256460862666637c96ef7977f Mon Sep 17 00:00:00 2001 From: Hiroshiba Kazuyuki Date: Thu, 27 Feb 2025 22:05:30 +0900 Subject: [PATCH 08/19] =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=81=A6=E3=82=82?= =?UTF-8?q?=E3=82=8F=E3=81=8B=E3=82=8B=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E7=9C=81=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/project/index.ts | 3 --- src/store/project/saveProjectHelper.ts | 0 2 files changed, 3 deletions(-) create mode 100644 src/store/project/saveProjectHelper.ts diff --git a/src/store/project/index.ts b/src/store/project/index.ts index e4217380b7..6c13dde591 100755 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -329,14 +329,11 @@ export const projectStore = createPartialStore({ let defaultPath: string; if (!defaultFilePath) { - // if new project: use generated name defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; } else { - // if saveAs for existing project: use current project path defaultPath = defaultFilePath; } - // Write the current status to a project file. return await window.backend.showSaveFileDialog({ title: "プロジェクトファイルの保存", name: "VOICEVOX Project file", diff --git a/src/store/project/saveProjectHelper.ts b/src/store/project/saveProjectHelper.ts new file mode 100644 index 0000000000..e69de29bb2 From aa913da583319dbce9925518f7f7f770a700dd64 Mon Sep 17 00:00:00 2001 From: Hiroshiba Kazuyuki Date: Thu, 27 Feb 2025 22:25:35 +0900 Subject: [PATCH 09/19] =?UTF-8?q?promptProjectSaveFilePath=E3=81=A8writePr?= =?UTF-8?q?ojectFile=E3=82=92=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/store/project/index.ts | 103 ++++--------------------- src/store/project/saveProjectHelper.ts | 67 ++++++++++++++++ src/store/type.ts | 25 +++--- 3 files changed, 97 insertions(+), 98 deletions(-) diff --git a/src/store/project/index.ts b/src/store/project/index.ts index 6c13dde591..32c5a45bf8 100755 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -30,6 +30,10 @@ import { import { uuid4 } from "@/helpers/random"; import { getAppInfos } from "@/domain/appInfo"; import { errorToMessage } from "@/helpers/errorHelper"; +import { + promptProjectSaveFilePath, + writeProjectFile, +} from "./saveProjectHelper"; export const projectStoreState: ProjectStoreState = { savedLastCommandIds: { talk: null, song: null }, @@ -256,14 +260,11 @@ export const projectStore = createPartialStore({ action: createUILockAction(async (context) => { const filePath = context.state.projectFilePath; if (!filePath) { - await context.actions.SAVE_PROJECT_FILE_AS({}); - return; + return await context.actions.SAVE_PROJECT_FILE_AS({}); } - const result = await context.actions.WRITE_PROJECT_FILE({ - filePath, - }); - if (!result) return; + const result = await writeProjectFile(context, filePath); + if (!result) return false; await context.actions.APPEND_RECENTLY_USED_PROJECT({ filePath, @@ -271,6 +272,7 @@ export const projectStore = createPartialStore({ context.mutations.SET_SAVED_LAST_COMMAND_IDS( context.getters.LAST_COMMAND_IDS, ); + return true; }), }, @@ -280,13 +282,11 @@ export const projectStore = createPartialStore({ * エラー発生時はダイアログが表示される。 */ action: createUILockAction(async (context, {}) => { - const filePath = await context.actions.PROMPT_PROJECT_SAVE_FILE_PATH({}); - if (!filePath) return; + const filePath = await promptProjectSaveFilePath(context); + if (!filePath) return false; - const result = await context.actions.WRITE_PROJECT_FILE({ - filePath, - }); - if (!result) return; + const result = await writeProjectFile(context, filePath); + if (!result) return false; if ( context.state.projectFilePath && @@ -306,6 +306,7 @@ export const projectStore = createPartialStore({ context.mutations.SET_SAVED_LAST_COMMAND_IDS( context.getters.LAST_COMMAND_IDS, ); + return true; }), }, @@ -315,78 +316,10 @@ export const projectStore = createPartialStore({ * エラー発生時はダイアログが表示される。 */ action: createUILockAction(async (context, {}) => { - const filePath = await context.actions.PROMPT_PROJECT_SAVE_FILE_PATH({}); - if (!filePath) return; + const filePath = await promptProjectSaveFilePath(context); + if (!filePath) return false; - await context.actions.WRITE_PROJECT_FILE({ - filePath, - }); - }), - }, - - PROMPT_PROJECT_SAVE_FILE_PATH: { - async action(context, { defaultFilePath }) { - let defaultPath: string; - - if (!defaultFilePath) { - defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; - } else { - defaultPath = defaultFilePath; - } - - return await window.backend.showSaveFileDialog({ - title: "プロジェクトファイルの保存", - name: "VOICEVOX Project file", - extensions: ["vvproj"], - defaultPath, - }); - }, - }, - - WRITE_PROJECT_FILE: { - action: createUILockAction(async (context, { filePath }) => { - const appVersion = getAppInfos().version; - const { - audioItems, - audioKeys, - tpqn, - tempos, - timeSignatures, - tracks, - trackOrder, - } = context.state; - const projectData: LatestProjectType = { - appVersion, - talk: { - audioKeys, - audioItems, - }, - song: { - tpqn, - tempos, - timeSignatures, - tracks: Object.fromEntries(tracks), - trackOrder, - }, - }; - - const buf = new TextEncoder().encode(JSON.stringify(projectData)).buffer; - try { - await window.backend - .writeFile({ - filePath, - buffer: buf, - }) - .then(getValueOrThrow); - return true; - } catch (err) { - window.backend.logError(err); - await showAlertDialog({ - title: "エラー", - message: `プロジェクトファイルの保存に失敗しました。\n${errorToMessage(err)}`, - }); - return false; - } + return await writeProjectFile(context, filePath); }), }, @@ -414,9 +347,7 @@ export const projectStore = createPartialStore({ cancel: 0, }); if (result == 2) { - const saved = await actions.SAVE_PROJECT_FILE({ - overwrite: true, - }); + const saved = await actions.SAVE_PROJECT_FILE_OVERWRITE({}); return saved ? "saved" : "canceled"; } else if (result == 1) { return "discarded"; diff --git a/src/store/project/saveProjectHelper.ts b/src/store/project/saveProjectHelper.ts index e69de29bb2..f1d613a81e 100644 --- a/src/store/project/saveProjectHelper.ts +++ b/src/store/project/saveProjectHelper.ts @@ -0,0 +1,67 @@ +import { showAlertDialog } from "@/components/Dialog/Dialog"; +import { getAppInfos } from "@/domain/appInfo"; +import { LatestProjectType } from "@/domain/project/schema"; +import { errorToMessage } from "@/helpers/errorHelper"; +import { getValueOrThrow } from "@/type/result"; +import { ActionContext } from "../type"; + +export async function promptProjectSaveFilePath( + context: ActionContext, +): Promise { + const defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; + + return await window.backend.showSaveFileDialog({ + title: "プロジェクトファイルの保存", + name: "VOICEVOX Project file", + extensions: ["vvproj"], + defaultPath, + }); +} + +export async function writeProjectFile( + context: ActionContext, + filePath: string, +): Promise { + const appVersion = getAppInfos().version; + const { + audioItems, + audioKeys, + tpqn, + tempos, + timeSignatures, + tracks, + trackOrder, + } = context.state; + const projectData: LatestProjectType = { + appVersion, + talk: { + audioKeys, + audioItems, + }, + song: { + tpqn, + tempos, + timeSignatures, + tracks: Object.fromEntries(tracks), + trackOrder, + }, + }; + + const buf = new TextEncoder().encode(JSON.stringify(projectData)).buffer; + try { + await window.backend + .writeFile({ + filePath, + buffer: buf, + }) + .then(getValueOrThrow); + return true; + } catch (err) { + window.backend.logError(err); + await showAlertDialog({ + title: "エラー", + message: `プロジェクトファイルの保存に失敗しました。\n${errorToMessage(err)}`, + }); + return false; + } +} diff --git a/src/store/type.ts b/src/store/type.ts index 10babb508a..4b5710c051 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -9,6 +9,7 @@ import { StoreOptions, PayloadFunction, Store, + DotNotationActionContext, } from "./vuex"; import { createCommandMutationTree, PayloadRecipeTree } from "./command"; import { @@ -1850,27 +1851,19 @@ export type ProjectStoreTypes = { }; SAVE_PROJECT_FILE_OVERWRITE: { - action(payload: {}): void; + action(payload: {}): Promise; }; SAVE_PROJECT_FILE_AS: { - action(payload: {}): void; + action(payload: {}): Promise; }; SAVE_PROJECT_FILE_AS_COPY: { - action(payload: {}): void; - }; - - PROMPT_PROJECT_SAVE_FILE_PATH: { - action(payload: { defaultFilePath?: string }): Promise; - }; - - WRITE_PROJECT_FILE: { - action(payload: { filePath: string }): Promise; + action(payload: {}): Promise; }; SAVE_OR_DISCARD_PROJECT_FILE: { - action(palyoad: { + action(payload: { additionalMessage?: string; }): "saved" | "discarded" | "canceled"; }; @@ -2388,6 +2381,14 @@ export type AllGetters = StoreType; export type AllMutations = StoreType; export type AllActions = StoreType; +export type ActionContext = DotNotationActionContext< + State, + State, + AllGetters, + AllActions, + AllMutations +>; + export const commandMutationsCreator = ( arg: PayloadRecipeTree, editor: EditorType, From ac26e8e7ce609fe8e4842be6951dcd6b2be3ec3d Mon Sep 17 00:00:00 2001 From: Hiroshiba Kazuyuki Date: Thu, 27 Feb 2025 22:36:53 +0900 Subject: [PATCH 10/19] =?UTF-8?q?updateCurrentProject=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E3=82=92=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/store/project/index.ts | 22 ++++------------------ src/store/project/saveProjectHelper.ts | 14 +++++++++++++- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/store/project/index.ts b/src/store/project/index.ts index 32c5a45bf8..88fdf90155 100755 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -28,10 +28,9 @@ import { showQuestionDialog, } from "@/components/Dialog/Dialog"; import { uuid4 } from "@/helpers/random"; -import { getAppInfos } from "@/domain/appInfo"; -import { errorToMessage } from "@/helpers/errorHelper"; import { promptProjectSaveFilePath, + updateCurrentProject, writeProjectFile, } from "./saveProjectHelper"; @@ -266,12 +265,7 @@ export const projectStore = createPartialStore({ const result = await writeProjectFile(context, filePath); if (!result) return false; - await context.actions.APPEND_RECENTLY_USED_PROJECT({ - filePath, - }); - context.mutations.SET_SAVED_LAST_COMMAND_IDS( - context.getters.LAST_COMMAND_IDS, - ); + await updateCurrentProject(context, filePath); return true; }), }, @@ -288,10 +282,7 @@ export const projectStore = createPartialStore({ const result = await writeProjectFile(context, filePath); if (!result) return false; - if ( - context.state.projectFilePath && - context.state.projectFilePath != filePath - ) { + if (context.state.projectFilePath !== filePath) { context.mutations.SET_PROJECT_FILEPATH({ filePath }); await showMessageDialog({ type: "info", @@ -300,12 +291,7 @@ export const projectStore = createPartialStore({ }); } - await context.actions.APPEND_RECENTLY_USED_PROJECT({ - filePath, - }); - context.mutations.SET_SAVED_LAST_COMMAND_IDS( - context.getters.LAST_COMMAND_IDS, - ); + await updateCurrentProject(context, filePath); return true; }), }, diff --git a/src/store/project/saveProjectHelper.ts b/src/store/project/saveProjectHelper.ts index f1d613a81e..e224274349 100644 --- a/src/store/project/saveProjectHelper.ts +++ b/src/store/project/saveProjectHelper.ts @@ -1,4 +1,4 @@ -import { showAlertDialog } from "@/components/Dialog/Dialog"; +import { showAlertDialog, showMessageDialog } from "@/components/Dialog/Dialog"; import { getAppInfos } from "@/domain/appInfo"; import { LatestProjectType } from "@/domain/project/schema"; import { errorToMessage } from "@/helpers/errorHelper"; @@ -65,3 +65,15 @@ export async function writeProjectFile( return false; } } + +export async function updateCurrentProject( + context: ActionContext, + filePath: string, +) { + await context.actions.APPEND_RECENTLY_USED_PROJECT({ + filePath, + }); + context.mutations.SET_SAVED_LAST_COMMAND_IDS( + context.getters.LAST_COMMAND_IDS, + ); +} From a4edeccb2739900ce7c6cc348adb02d3ba18c2ad Mon Sep 17 00:00:00 2001 From: Hiroshiba Kazuyuki Date: Thu, 27 Feb 2025 23:10:19 +0900 Subject: [PATCH 11/19] =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=80?= =?UTF-8?q?=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0=E8=A1=A8=E7=A4=BA=E3=81=AE?= =?UTF-8?q?=E3=81=9F=E3=82=81=E3=81=AEshowErrorDialog=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Dialog/Dialog.ts | 9 ++++++ src/store/project/index.ts | 23 ++++++++++---- src/store/project/saveProjectHelper.ts | 43 ++++++++++++++++---------- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/components/Dialog/Dialog.ts b/src/components/Dialog/Dialog.ts index c29b0cd5cb..02dfadc77f 100644 --- a/src/components/Dialog/Dialog.ts +++ b/src/components/Dialog/Dialog.ts @@ -12,6 +12,7 @@ import { } from "@/store/type"; import { DotNotationDispatch } from "@/store/vuex"; import { withProgress } from "@/store/ui"; +import { errorToMessage } from "@/helpers/errorHelper"; type MediaType = "audio" | "text" | "project" | "label"; @@ -86,6 +87,14 @@ export const showAlertDialog = async ( }); }; +/** 例外からエラーダイアログを表示する便利関数 */ +export const showErrorDialog = async (title: string, e: unknown) => { + return showAlertDialog({ + title, + message: errorToMessage(e), + }); +}; + /** 続行することが望まれそうな場合の質問ダイアログ */ export const showConfirmDialog = async (options: ConfirmDialogOptions) => { options.cancel ??= "キャンセル"; diff --git a/src/store/project/index.ts b/src/store/project/index.ts index 88fdf90155..f6b425b871 100755 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -29,8 +29,9 @@ import { } from "@/components/Dialog/Dialog"; import { uuid4 } from "@/helpers/random"; import { + executeWritePromiseOrDialog, promptProjectSaveFilePath, - updateCurrentProject, + handleCurrentProjectSave, writeProjectFile, } from "./saveProjectHelper"; @@ -254,6 +255,7 @@ export const projectStore = createPartialStore({ /** * プロジェクトファイルを上書き保存する。 * 現在のプロジェクトファイルが未設定の場合は名前をつけて保存する。 + * ファイルを保存できた場合はtrueが、キャンセルしたか例外が発生した場合はfalseが返る。 * エラー発生時はダイアログが表示される。 */ action: createUILockAction(async (context) => { @@ -262,10 +264,12 @@ export const projectStore = createPartialStore({ return await context.actions.SAVE_PROJECT_FILE_AS({}); } - const result = await writeProjectFile(context, filePath); + const result = await executeWritePromiseOrDialog( + writeProjectFile(context, filePath), + ); if (!result) return false; - await updateCurrentProject(context, filePath); + await handleCurrentProjectSave(context, filePath); return true; }), }, @@ -273,13 +277,16 @@ export const projectStore = createPartialStore({ SAVE_PROJECT_FILE_AS: { /** * プロジェクトファイルを名前をつけて保存し、現在のプロジェクトファイルのパスを更新する。 + * ファイルを保存できた場合はtrueが、キャンセルしたか例外が発生した場合はfalseが返る。 * エラー発生時はダイアログが表示される。 */ action: createUILockAction(async (context, {}) => { const filePath = await promptProjectSaveFilePath(context); if (!filePath) return false; - const result = await writeProjectFile(context, filePath); + const result = await executeWritePromiseOrDialog( + writeProjectFile(context, filePath), + ); if (!result) return false; if (context.state.projectFilePath !== filePath) { @@ -291,7 +298,7 @@ export const projectStore = createPartialStore({ }); } - await updateCurrentProject(context, filePath); + await handleCurrentProjectSave(context, filePath); return true; }), }, @@ -299,13 +306,17 @@ export const projectStore = createPartialStore({ SAVE_PROJECT_FILE_AS_COPY: { /** * プロジェクトファイルを複製として保存する。 + * ファイルを保存できた場合はtrueが、キャンセルしたか例外が発生した場合はfalseが返る。 * エラー発生時はダイアログが表示される。 */ action: createUILockAction(async (context, {}) => { const filePath = await promptProjectSaveFilePath(context); if (!filePath) return false; - return await writeProjectFile(context, filePath); + const result = await executeWritePromiseOrDialog( + writeProjectFile(context, filePath), + ); + return result; }), }, diff --git a/src/store/project/saveProjectHelper.ts b/src/store/project/saveProjectHelper.ts index e224274349..25cc528676 100644 --- a/src/store/project/saveProjectHelper.ts +++ b/src/store/project/saveProjectHelper.ts @@ -1,8 +1,8 @@ -import { showAlertDialog, showMessageDialog } from "@/components/Dialog/Dialog"; +import { showErrorDialog } from "@/components/Dialog/Dialog"; import { getAppInfos } from "@/domain/appInfo"; import { LatestProjectType } from "@/domain/project/schema"; -import { errorToMessage } from "@/helpers/errorHelper"; -import { getValueOrThrow } from "@/type/result"; +import { DisplayableError, errorToMessage } from "@/helpers/errorHelper"; +import { ResultError } from "@/type/result"; import { ActionContext } from "../type"; export async function promptProjectSaveFilePath( @@ -18,10 +18,13 @@ export async function promptProjectSaveFilePath( }); } +/** + * @throws ファイルの保存に失敗した場合 + */ export async function writeProjectFile( context: ActionContext, filePath: string, -): Promise { +) { const appVersion = getAppInfos().version; const { audioItems, @@ -48,25 +51,31 @@ export async function writeProjectFile( }; const buf = new TextEncoder().encode(JSON.stringify(projectData)).buffer; + const result = await window.backend.writeFile({ + filePath, + buffer: buf, + }); + if (!result.ok) { + throw new DisplayableError("ファイルの保存に失敗しました。", { + cause: new ResultError(result), + }); + } +} + +export async function executeWritePromiseOrDialog( + savePromise: Promise, +): Promise { try { - await window.backend - .writeFile({ - filePath, - buffer: buf, - }) - .then(getValueOrThrow); + await savePromise; return true; - } catch (err) { - window.backend.logError(err); - await showAlertDialog({ - title: "エラー", - message: `プロジェクトファイルの保存に失敗しました。\n${errorToMessage(err)}`, - }); + } catch (e) { + window.backend.logError(e); + await showErrorDialog("プロジェクトファイルの保存に失敗しました", e); return false; } } -export async function updateCurrentProject( +export async function handleCurrentProjectSave( context: ActionContext, filePath: string, ) { From c7b2843ff44e93f7b78c3ce3576f7afc6613f563 Mon Sep 17 00:00:00 2001 From: Hiroshiba Kazuyuki Date: Thu, 27 Feb 2025 23:14:00 +0900 Subject: [PATCH 12/19] =?UTF-8?q?=E5=A4=89=E6=9B=B4=E6=BC=8F=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Menu/MenuBar/MenuBar.vue | 4 ++-- src/components/Talk/ToolBar.vue | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Menu/MenuBar/MenuBar.vue b/src/components/Menu/MenuBar/MenuBar.vue index 2cfb5bffab..e880479ee0 100644 --- a/src/components/Menu/MenuBar/MenuBar.vue +++ b/src/components/Menu/MenuBar/MenuBar.vue @@ -142,13 +142,13 @@ const createNewProject = async () => { const saveProject = async () => { if (!uiLocked.value) { - await store.actions.SAVE_PROJECT_FILE({ overwrite: true }); + await store.actions.SAVE_PROJECT_FILE_OVERWRITE({}); } }; const saveProjectAs = async () => { if (!uiLocked.value) { - await store.actions.SAVE_PROJECT_FILE({}); + await store.actions.SAVE_PROJECT_FILE_AS({}); } }; diff --git a/src/components/Talk/ToolBar.vue b/src/components/Talk/ToolBar.vue index 12b1bbbc86..4dfcd01354 100644 --- a/src/components/Talk/ToolBar.vue +++ b/src/components/Talk/ToolBar.vue @@ -142,7 +142,7 @@ const generateAndConnectAndSaveAudio = async () => { }); }; const saveProject = async () => { - await store.actions.SAVE_PROJECT_FILE({ overwrite: true }); + await store.actions.SAVE_PROJECT_FILE_OVERWRITE({}); }; const importTextFile = () => { void store.actions.COMMAND_IMPORT_FROM_FILE({ type: "dialog" }); From f5b44fbb3fbf7e03224720fe358aaf6aef4b7507 Mon Sep 17 00:00:00 2001 From: Hiroshiba Kazuyuki Date: Thu, 27 Feb 2025 23:22:09 +0900 Subject: [PATCH 13/19] =?UTF-8?q?saveProjectHelper=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=81=AE=E3=82=A4=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=88=E3=82=92?= =?UTF-8?q?=E6=95=B4=E7=90=86=E3=81=97=E3=80=81=E9=87=8D=E8=A4=87=E3=82=92?= =?UTF-8?q?=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/project/index.ts | 12 ++++++------ src/store/project/saveProjectHelper.ts | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/store/project/index.ts b/src/store/project/index.ts index f6b425b871..fa517b68d3 100755 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -1,4 +1,10 @@ import { createPartialStore, DotNotationDispatch } from "../vuex"; +import { + executeWritePromiseOrDialog, + promptProjectSaveFilePath, + handleCurrentProjectSave, + writeProjectFile, +} from "./saveProjectHelper"; import { createUILockAction } from "@/store/ui"; import { AllActions, @@ -28,12 +34,6 @@ import { showQuestionDialog, } from "@/components/Dialog/Dialog"; import { uuid4 } from "@/helpers/random"; -import { - executeWritePromiseOrDialog, - promptProjectSaveFilePath, - handleCurrentProjectSave, - writeProjectFile, -} from "./saveProjectHelper"; export const projectStoreState: ProjectStoreState = { savedLastCommandIds: { talk: null, song: null }, diff --git a/src/store/project/saveProjectHelper.ts b/src/store/project/saveProjectHelper.ts index 25cc528676..c1479ddb38 100644 --- a/src/store/project/saveProjectHelper.ts +++ b/src/store/project/saveProjectHelper.ts @@ -1,9 +1,9 @@ +import { ActionContext } from "../type"; import { showErrorDialog } from "@/components/Dialog/Dialog"; import { getAppInfos } from "@/domain/appInfo"; import { LatestProjectType } from "@/domain/project/schema"; -import { DisplayableError, errorToMessage } from "@/helpers/errorHelper"; +import { DisplayableError } from "@/helpers/errorHelper"; import { ResultError } from "@/type/result"; -import { ActionContext } from "../type"; export async function promptProjectSaveFilePath( context: ActionContext, From bd262945bdfa0fbbb3922b2f2df080d019edd952 Mon Sep 17 00:00:00 2001 From: Hiroshiba Date: Fri, 28 Feb 2025 08:10:47 +0900 Subject: [PATCH 14/19] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=82=92=E5=89=8A=E9=99=A4=E3=81=97=E3=80=81?= =?UTF-8?q?=E3=83=97=E3=83=AD=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E4=BF=9D=E5=AD=98=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E7=B0=A1=E7=B4=A0=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/project/index.ts | 63 ++------------------------------------ src/store/type.ts | 18 ++--------- 2 files changed, 5 insertions(+), 76 deletions(-) diff --git a/src/store/project/index.ts b/src/store/project/index.ts index b943b63126..62b95db9dd 100755 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -280,7 +280,7 @@ export const projectStore = createPartialStore({ * ファイルを保存できた場合はtrueが、キャンセルしたか例外が発生した場合はfalseが返る。 * エラー発生時はダイアログが表示される。 */ - action: createUILockAction(async (context, {}) => { + action: createUILockAction(async (context) => { const filePath = await promptProjectSaveFilePath(context); if (!filePath) return false; @@ -309,7 +309,7 @@ export const projectStore = createPartialStore({ * ファイルを保存できた場合はtrueが、キャンセルしたか例外が発生した場合はfalseが返る。 * エラー発生時はダイアログが表示される。 */ - action: createUILockAction(async (context, {}) => { + action: createUILockAction(async (context) => { const filePath = await promptProjectSaveFilePath(context); if (!filePath) return false; @@ -320,65 +320,6 @@ export const projectStore = createPartialStore({ }), }, - PROMPT_PROJECT_SAVE_FILE_PATH: { - async action(context, { defaultFilePath }) { - let defaultPath: string; - - if (!defaultFilePath) { - // if new project: use generated name - defaultPath = `${context.getters.DEFAULT_PROJECT_FILE_BASE_NAME}.vvproj`; - } else { - // if saveAs for existing project: use current project path - defaultPath = defaultFilePath; - } - - // Write the current status to a project file. - return await window.backend.showSaveFileDialog({ - title: "プロジェクトファイルの保存", - name: "VOICEVOX Project file", - extensions: ["vvproj"], - defaultPath, - }); - }, - }, - - WRITE_PROJECT_FILE: { - action: async (context, { filePath }) => { - const appVersion = getAppInfos().version; - const { - audioItems, - audioKeys, - tpqn, - tempos, - timeSignatures, - tracks, - trackOrder, - } = context.state; - const projectData: LatestProjectType = { - appVersion, - talk: { - audioKeys, - audioItems, - }, - song: { - tpqn, - tempos, - timeSignatures, - tracks: Object.fromEntries(tracks), - trackOrder, - }, - }; - - const buf = new TextEncoder().encode(JSON.stringify(projectData)).buffer; - await window.backend - .writeFile({ - filePath, - buffer: buf, - }) - .then(getValueOrThrow); - }, - }, - /** * プロジェクトファイルを保存するか破棄するかキャンセルするかのダイアログを出して、保存する場合は保存する。 * 何を選択したかが返る。 diff --git a/src/store/type.ts b/src/store/type.ts index 0b2802683a..c928b66607 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -1853,27 +1853,15 @@ export type ProjectStoreTypes = { }; SAVE_PROJECT_FILE_OVERWRITE: { - action(payload: {}): Promise; + action(): Promise; }; SAVE_PROJECT_FILE_AS: { - action(payload: {}): Promise; + action(): Promise; }; SAVE_PROJECT_FILE_AS_COPY: { - action(payload: {}): Promise; - }; - - SAVE_PROJECT_FILE_AS_COPY: { - action(payload: { filePath?: string }): boolean; - }; - - PROMPT_PROJECT_SAVE_FILE_PATH: { - action(payload: { defaultFilePath?: string }): Promise; - }; - - WRITE_PROJECT_FILE: { - action(payload: { filePath: string }): Promise; + action(): Promise; }; SAVE_OR_DISCARD_PROJECT_FILE: { From a01bb0d8f31a71b443c66dd9d91b89a08a360e5e Mon Sep 17 00:00:00 2001 From: Hiroshiba Date: Fri, 28 Feb 2025 08:11:02 +0900 Subject: [PATCH 15/19] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=82=92=E5=89=8A=E9=99=A4=E3=81=97=E3=80=81?= =?UTF-8?q?=E3=83=97=E3=83=AD=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E4=BF=9D=E5=AD=98=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E7=B0=A1=E7=B4=A0=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/project/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/project/index.ts b/src/store/project/index.ts index 62b95db9dd..d4f55b0bff 100755 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -261,7 +261,7 @@ export const projectStore = createPartialStore({ action: createUILockAction(async (context) => { const filePath = context.state.projectFilePath; if (!filePath) { - return await context.actions.SAVE_PROJECT_FILE_AS({}); + return await context.actions.SAVE_PROJECT_FILE_AS(); } const result = await executeWritePromiseOrDialog( @@ -344,7 +344,7 @@ export const projectStore = createPartialStore({ cancel: 0, }); if (result == 2) { - const saved = await actions.SAVE_PROJECT_FILE_OVERWRITE({}); + const saved = await actions.SAVE_PROJECT_FILE_OVERWRITE(); return saved ? "saved" : "canceled"; } else if (result == 1) { return "discarded"; From 5ecfbc7457bebcf3e83cf15fa0acfbec1c37df24 Mon Sep 17 00:00:00 2001 From: Hiroshiba Date: Fri, 28 Feb 2025 08:12:48 +0900 Subject: [PATCH 16/19] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=82=92=E5=89=8A=E9=99=A4=E3=81=97=E3=80=81?= =?UTF-8?q?=E3=83=97=E3=83=AD=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=A9=9F=E8=83=BD=E3=82=92=E7=B0=A1=E7=B4=A0=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Menu/MenuBar/MenuBar.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Menu/MenuBar/MenuBar.vue b/src/components/Menu/MenuBar/MenuBar.vue index 96a60ba070..c6d83496a9 100644 --- a/src/components/Menu/MenuBar/MenuBar.vue +++ b/src/components/Menu/MenuBar/MenuBar.vue @@ -142,19 +142,19 @@ const createNewProject = async () => { const saveProject = async () => { if (!uiLocked.value) { - await store.actions.SAVE_PROJECT_FILE_OVERWRITE({}); + await store.actions.SAVE_PROJECT_FILE_OVERWRITE(); } }; const saveProjectAs = async () => { if (!uiLocked.value) { - await store.actions.SAVE_PROJECT_FILE_AS({}); + await store.actions.SAVE_PROJECT_FILE_AS(); } }; const saveProjectCopy = async () => { if (!uiLocked.value) { - await store.actions.SAVE_PROJECT_FILE_AS_COPY({}); + await store.actions.SAVE_PROJECT_FILE_AS_COPY(); } }; From ba07b368e3a29efc3b9409dfe8271396a6070283 Mon Sep 17 00:00:00 2001 From: Hiroshiba Date: Fri, 28 Feb 2025 08:14:22 +0900 Subject: [PATCH 17/19] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=82=92=E5=89=8A=E9=99=A4=E3=81=97=E3=80=81?= =?UTF-8?q?=E3=83=97=E3=83=AD=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E4=BF=9D=E5=AD=98=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E7=B0=A1=E7=B4=A0=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Talk/ToolBar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Talk/ToolBar.vue b/src/components/Talk/ToolBar.vue index 4dfcd01354..0ecca782e2 100644 --- a/src/components/Talk/ToolBar.vue +++ b/src/components/Talk/ToolBar.vue @@ -142,7 +142,7 @@ const generateAndConnectAndSaveAudio = async () => { }); }; const saveProject = async () => { - await store.actions.SAVE_PROJECT_FILE_OVERWRITE({}); + await store.actions.SAVE_PROJECT_FILE_OVERWRITE(); }; const importTextFile = () => { void store.actions.COMMAND_IMPORT_FROM_FILE({ type: "dialog" }); From 71e90b7b9709af5ab68dee9aa69592d2a93c452b Mon Sep 17 00:00:00 2001 From: Hiroshiba Date: Fri, 28 Feb 2025 08:23:52 +0900 Subject: [PATCH 18/19] =?UTF-8?q?refactor:=20=E7=8F=BE=E5=9C=A8=E3=81=AE?= =?UTF-8?q?=E3=83=97=E3=83=AD=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E9=96=A2=E6=95=B0=E3=81=AE=E5=90=8D=E5=89=8D=E3=82=92?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=81=97=E3=80=81=E3=82=B3=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=AE=E5=8F=AF=E8=AA=AD=E6=80=A7=E3=82=92=E5=90=91=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/project/index.ts | 6 +++--- src/store/project/saveProjectHelper.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/store/project/index.ts b/src/store/project/index.ts index d4f55b0bff..71aa508b62 100755 --- a/src/store/project/index.ts +++ b/src/store/project/index.ts @@ -2,7 +2,7 @@ import { createPartialStore, DotNotationDispatch } from "../vuex"; import { executeWritePromiseOrDialog, promptProjectSaveFilePath, - handleCurrentProjectSave, + markCurrentProjectAsSaved, writeProjectFile, } from "./saveProjectHelper"; import { createUILockAction } from "@/store/ui"; @@ -269,7 +269,7 @@ export const projectStore = createPartialStore({ ); if (!result) return false; - await handleCurrentProjectSave(context, filePath); + await markCurrentProjectAsSaved(context, filePath); return true; }), }, @@ -298,7 +298,7 @@ export const projectStore = createPartialStore({ }); } - await handleCurrentProjectSave(context, filePath); + await markCurrentProjectAsSaved(context, filePath); return true; }), }, diff --git a/src/store/project/saveProjectHelper.ts b/src/store/project/saveProjectHelper.ts index c1479ddb38..440172bfb0 100644 --- a/src/store/project/saveProjectHelper.ts +++ b/src/store/project/saveProjectHelper.ts @@ -75,7 +75,7 @@ export async function executeWritePromiseOrDialog( } } -export async function handleCurrentProjectSave( +export async function markCurrentProjectAsSaved( context: ActionContext, filePath: string, ) { From 039b1197149f4e14984acca71bc908163c844ec7 Mon Sep 17 00:00:00 2001 From: Hiroshiba Date: Fri, 28 Feb 2025 08:39:18 +0900 Subject: [PATCH 19/19] =?UTF-8?q?refactor:=20ResultError=E3=82=AF=E3=83=A9?= =?UTF-8?q?=E3=82=B9=E3=81=AE=E3=82=B3=E3=83=B3=E3=82=B9=E3=83=88=E3=83=A9?= =?UTF-8?q?=E3=82=AF=E3=82=BF=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/type/result.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/type/result.ts b/src/type/result.ts index c7f3a4effb..1fee31abb7 100644 --- a/src/type/result.ts +++ b/src/type/result.ts @@ -93,7 +93,8 @@ export class ResultError< public code: E; constructor(public readonly result: FailureResult) { - super(`${result.code}: ${result.error.message}`, { cause: result.error }); + super(`${result.code}`, { cause: result.error }); + this.name = "ResultError"; this.code = result.code; } }