diff --git a/src/components/SettingDialog.vue b/src/components/SettingDialog.vue index f518e4d051..03a1a62f4f 100644 --- a/src/components/SettingDialog.vue +++ b/src/components/SettingDialog.vue @@ -1041,18 +1041,25 @@ const availableThemeNameComputed = computed(() => { const editorFont = computed(() => store.state.editorFont); const changeEditorFont = (editorFont: EditorFontType) => { - store.dispatch("SET_EDITOR_FONT", { editorFont }); + store.dispatch("SET_ROOT_MISC_SETTING", { + key: "editorFont", + value: editorFont, + }); }; const enableMultiEngine = computed(() => store.state.enableMultiEngine); const setEnableMultiEngine = (enableMultiEngine: boolean) => { - store.dispatch("SET_ENABLE_MULTI_ENGINE", { enableMultiEngine }); + store.dispatch("SET_ROOT_MISC_SETTING", { + key: "enableMultiEngine", + value: enableMultiEngine, + }); }; const showTextLineNumber = computed(() => store.state.showTextLineNumber); const changeShowTextLineNumber = (showTextLineNumber: boolean) => { - store.dispatch("SET_SHOW_TEXT_LINE_NUMBER", { - showTextLineNumber, + store.dispatch("SET_ROOT_MISC_SETTING", { + key: "showTextLineNumber", + value: showTextLineNumber, }); }; @@ -1063,8 +1070,9 @@ const showAddAudioItemButton = computed( const changeShowAddAudioItemButton = async ( showAddAudioItemButton: boolean ) => { - store.dispatch("SET_SHOW_ADD_AUDIO_ITEM_BUTTON", { - showAddAudioItemButton, + store.dispatch("SET_ROOT_MISC_SETTING", { + key: "showAddAudioItemButton", + value: showAddAudioItemButton, }); // 設定をオフにする場合はヒントを表示 @@ -1076,8 +1084,9 @@ const changeShowAddAudioItemButton = async ( }); if (result === "CANCEL") { // キャンセルしたら設定を元に戻す - store.dispatch("SET_SHOW_ADD_AUDIO_ITEM_BUTTON", { - showAddAudioItemButton: true, + store.dispatch("SET_ROOT_MISC_SETTING", { + key: "showAddAudioItemButton", + value: true, }); } } @@ -1272,7 +1281,10 @@ const splitTextWhenPaste = computed(() => store.state.splitTextWhenPaste); const changeSplitTextWhenPaste = ( splitTextWhenPaste: SplitTextWhenPasteType ) => { - store.dispatch("SET_SPLIT_TEXT_WHEN_PASTE", { splitTextWhenPaste }); + store.dispatch("SET_ROOT_MISC_SETTING", { + key: "splitTextWhenPaste", + value: splitTextWhenPaste, + }); }; const showsFilePatternEditDialog = ref(false); diff --git a/src/store/setting.ts b/src/store/setting.ts index 98a7f609ec..9b4a0e18d3 100644 --- a/src/store/setting.ts +++ b/src/store/setting.ts @@ -15,7 +15,9 @@ import { ToolbarSetting, EngineId, ConfirmedTips, + RootMiscSetting as RootMiscSetting, } from "@/type/preload"; +import { IsEqual } from "@/type/utility"; const hotkeyFunctionCache: Record HotkeyReturnType> = {}; @@ -89,22 +91,6 @@ export const settingStore = createPartialStore({ }); } - dispatch("SET_EDITOR_FONT", { - editorFont: await window.electron.getSetting("editorFont"), - }); - - dispatch("SET_SHOW_TEXT_LINE_NUMBER", { - showTextLineNumber: await window.electron.getSetting( - "showTextLineNumber" - ), - }); - - dispatch("SET_SHOW_ADD_AUDIO_ITEM_BUTTON", { - showAddAudioItemButton: await window.electron.getSetting( - "showAddAudioItemButton" - ), - }); - dispatch("SET_ACCEPT_RETRIEVE_TELEMETRY", { acceptRetrieveTelemetry: await window.electron.getSetting( "acceptRetrieveTelemetry" @@ -129,16 +115,6 @@ export const settingStore = createPartialStore({ ), }); - commit("SET_SPLIT_TEXT_WHEN_PASTE", { - splitTextWhenPaste: await window.electron.getSetting( - "splitTextWhenPaste" - ), - }); - - commit("SET_SPLITTER_POSITION", { - splitterPosition: await window.electron.getSetting("splitterPosition"), - }); - commit("SET_CONFIRMED_TIPS", { confirmedTips: await window.electron.getSetting("confirmedTips"), }); @@ -157,11 +133,30 @@ export const settingStore = createPartialStore({ }); } - commit("SET_ENABLE_MULTI_ENGINE", { - enableMultiEngine: await window.electron.getSetting( - "enableMultiEngine" - ), - }); + const rootMiscSettingKeys = [ + "editorFont", + "showTextLineNumber", + "showAddAudioItemButton", + "splitTextWhenPaste", + "splitterPosition", + "enableMultiEngine", + ] as const; + + // rootMiscSettingKeysに値を足し忘れていたときに型エラーを出す検出用コード + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _: IsEqual< + keyof RootMiscSetting, + typeof rootMiscSettingKeys[number] + > = true; + + for (const key of rootMiscSettingKeys) { + commit("SET_ROOT_MISC_SETTING", { + // NOTE: Vuexの型処理でUnionが解かれてしまうのを迂回している + // FIXME: このワークアラウンドをなくす + key: key as never, + value: await window.electron.getSetting(key), + }); + } }, }, @@ -225,6 +220,20 @@ export const settingStore = createPartialStore({ }, }, + SET_ROOT_MISC_SETTING: { + mutation(state, { key, value }) { + // NOTE: Vuexの型処理でUnionが解かれてしまうのを迂回している + // FIXME: このワークアラウンドをなくす + state[key as never] = value; + }, + action({ commit }, { key, value }) { + window.electron.setSetting(key, value); + // NOTE: Vuexの型処理でUnionが解かれてしまうのを迂回している + // FIXME: このワークアラウンドをなくす + commit("SET_ROOT_MISC_SETTING", { key: key as never, value }); + }, + }, + SET_THEME_SETTING: { mutation( state, @@ -285,43 +294,6 @@ export const settingStore = createPartialStore({ }, }, - SET_EDITOR_FONT: { - mutation(state, { editorFont }) { - state.editorFont = editorFont; - }, - action({ commit }, { editorFont }) { - window.electron.setSetting("editorFont", editorFont); - commit("SET_EDITOR_FONT", { editorFont }); - }, - }, - - SET_SHOW_TEXT_LINE_NUMBER: { - mutation(state, { showTextLineNumber }) { - state.showTextLineNumber = showTextLineNumber; - }, - action({ commit }, { showTextLineNumber }) { - window.electron.setSetting("showTextLineNumber", showTextLineNumber); - commit("SET_SHOW_TEXT_LINE_NUMBER", { - showTextLineNumber, - }); - }, - }, - - SET_SHOW_ADD_AUDIO_ITEM_BUTTON: { - mutation(state, { showAddAudioItemButton }) { - state.showAddAudioItemButton = showAddAudioItemButton; - }, - action({ commit }, { showAddAudioItemButton }) { - window.electron.setSetting( - "showAddAudioItemButton", - showAddAudioItemButton - ); - commit("SET_SHOW_ADD_AUDIO_ITEM_BUTTON", { - showAddAudioItemButton, - }); - }, - }, - SET_ACCEPT_RETRIEVE_TELEMETRY: { mutation(state, { acceptRetrieveTelemetry }) { state.acceptRetrieveTelemetry = acceptRetrieveTelemetry; @@ -366,26 +338,6 @@ export const settingStore = createPartialStore({ }, }, - SET_SPLIT_TEXT_WHEN_PASTE: { - mutation(state, { splitTextWhenPaste }) { - state.splitTextWhenPaste = splitTextWhenPaste; - }, - action({ commit }, { splitTextWhenPaste }) { - window.electron.setSetting("splitTextWhenPaste", splitTextWhenPaste); - commit("SET_SPLIT_TEXT_WHEN_PASTE", { splitTextWhenPaste }); - }, - }, - - SET_SPLITTER_POSITION: { - mutation(state, { splitterPosition }) { - state.splitterPosition = splitterPosition; - }, - action({ commit }, { splitterPosition }) { - window.electron.setSetting("splitterPosition", splitterPosition); - commit("SET_SPLITTER_POSITION", { splitterPosition }); - }, - }, - SET_CONFIRMED_TIPS: { mutation(state, { confirmedTips }) { state.confirmedTips = confirmedTips; @@ -436,16 +388,6 @@ export const settingStore = createPartialStore({ }, }, - SET_ENABLE_MULTI_ENGINE: { - mutation(state, { enableMultiEngine }) { - state.enableMultiEngine = enableMultiEngine; - }, - action({ commit }, { enableMultiEngine }) { - window.electron.setSetting("enableMultiEngine", enableMultiEngine); - commit("SET_ENABLE_MULTI_ENGINE", { enableMultiEngine }); - }, - }, - CHANGE_USE_GPU: { /** * CPU/GPUモードを切り替えようとする。 diff --git a/src/store/type.ts b/src/store/type.ts index b7006cda73..22dae4cc20 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -33,11 +33,8 @@ import { MorphingInfo, ActivePointScrollMode, EngineInfo, - SplitTextWhenPasteType, - SplitterPosition, ConfirmedTips, EngineDirValidationResult, - EditorFontType, EngineSettings, MorphableTargetInfoTable, EngineSetting, @@ -48,6 +45,7 @@ import { StyleId, AudioKey, PresetKey, + RootMiscSetting, } from "@/type/preload"; import { IEngineConnectorFactory } from "@/infrastructures/EngineConnector"; import { @@ -1040,17 +1038,19 @@ export type SettingStoreState = { engineInfos: Record; engineManifests: Record; themeSetting: ThemeSetting; - editorFont: EditorFontType; - showTextLineNumber: boolean; - showAddAudioItemButton: boolean; acceptRetrieveTelemetry: AcceptRetrieveTelemetryStatus; experimentalSetting: ExperimentalSetting; - splitTextWhenPaste: SplitTextWhenPasteType; - splitterPosition: SplitterPosition; confirmedTips: ConfirmedTips; engineSettings: EngineSettings; - enableMultiEngine: boolean; -}; +} & RootMiscSetting; + +// keyとvalueの型を連動するようにしたPayloadを作る +type KeyValuePayload = K extends keyof R + ? { + key: K; + value: R[K]; + } + : never; export type SettingStoreTypes = { HYDRATE_SETTING_STORE: { @@ -1072,26 +1072,16 @@ export type SettingStoreTypes = { action(payload: { data: ToolbarSetting }): void; }; + SET_ROOT_MISC_SETTING: { + mutation: KeyValuePayload; + action(payload: KeyValuePayload): void; + }; + SET_THEME_SETTING: { mutation: { currentTheme: string; themes?: ThemeConf[] }; action(payload: { currentTheme: string }): void; }; - SET_EDITOR_FONT: { - mutation: { editorFont: EditorFontType }; - action(payload: { editorFont: EditorFontType }): void; - }; - - SET_SHOW_TEXT_LINE_NUMBER: { - mutation: { showTextLineNumber: boolean }; - action(payload: { showTextLineNumber: boolean }): void; - }; - - SET_SHOW_ADD_AUDIO_ITEM_BUTTON: { - mutation: { showAddAudioItemButton: boolean }; - action(payload: { showAddAudioItemButton: boolean }): void; - }; - SET_ACCEPT_RETRIEVE_TELEMETRY: { mutation: { acceptRetrieveTelemetry: AcceptRetrieveTelemetryStatus }; action(payload: { @@ -1109,16 +1099,6 @@ export type SettingStoreTypes = { action(payload: { experimentalSetting: ExperimentalSetting }): void; }; - SET_SPLIT_TEXT_WHEN_PASTE: { - mutation: { splitTextWhenPaste: SplitTextWhenPasteType }; - action(payload: { splitTextWhenPaste: SplitTextWhenPasteType }): void; - }; - - SET_SPLITTER_POSITION: { - mutation: { splitterPosition: SplitterPosition }; - action(payload: { splitterPosition: SplitterPosition }): void; - }; - SET_CONFIRMED_TIPS: { mutation: { confirmedTips: ConfirmedTips }; action(payload: { confirmedTips: ConfirmedTips }): void; @@ -1140,11 +1120,6 @@ export type SettingStoreTypes = { }): Promise; }; - SET_ENABLE_MULTI_ENGINE: { - mutation: { enableMultiEngine: boolean }; - action(payload: { enableMultiEngine: boolean }): void; - }; - CHANGE_USE_GPU: { action(payload: { useGpu: boolean; engineId: EngineId }): Promise; }; diff --git a/src/type/preload.ts b/src/type/preload.ts index ef3820ffb7..e1548db2db 100644 --- a/src/type/preload.ts +++ b/src/type/preload.ts @@ -508,92 +508,101 @@ export type ConfirmedTips = { notifyOnGenerate: boolean; // 音声書き出し時の通知 }; -export const configSchema = z.object({ - inheritAudioInfo: z.boolean().default(true), - activePointScrollMode: z.enum(["CONTINUOUSLY", "PAGE", "OFF"]).default("OFF"), - savingSetting: z - .object({ - fileEncoding: z.enum(["UTF-8", "Shift_JIS"]).default("UTF-8"), - fileNamePattern: z.string().default(""), - fixedExportEnabled: z.boolean().default(false), - avoidOverwrite: z.boolean().default(false), - fixedExportDir: z.string().default(""), - exportLab: z.boolean().default(false), - exportText: z.boolean().default(false), - outputStereo: z.boolean().default(false), - audioOutputDevice: z.string().default(""), - }) - .default({}), - hotkeySettings: hotkeySettingSchema.array().default(defaultHotkeySettings), - toolbarSetting: toolbarSettingSchema - .array() - .default(defaultToolbarButtonSetting), - engineSettings: z.record(engineIdSchema, engineSettingSchema).default({}), - userCharacterOrder: speakerIdSchema.array().default([]), - defaultStyleIds: z - .object({ - engineId: engineIdSchema - .or(z.literal(EngineId("00000000-0000-0000-0000-000000000000"))) - .default(EngineId("00000000-0000-0000-0000-000000000000")), - speakerUuid: speakerIdSchema, - defaultStyleId: styleIdSchema, - }) - .array() - .default([]), - presets: z - .object({ - items: z - .record( - presetKeySchema, - z.object({ - name: z.string(), - speedScale: z.number(), - pitchScale: z.number(), - intonationScale: z.number(), - volumeScale: z.number(), - prePhonemeLength: z.number(), - postPhonemeLength: z.number(), - morphingInfo: z - .object({ - rate: z.number(), - targetEngineId: engineIdSchema, - targetSpeakerId: speakerIdSchema, - targetStyleId: styleIdSchema, - }) - .optional(), - }) - ) - .default({}), - keys: presetKeySchema.array().default([]), - }) - .default({}), - defaultPresetKeys: z.record(voiceIdSchema, presetKeySchema).default({}), - currentTheme: z.string().default("Default"), +// ルート直下にある雑多な設定値 +export const RootMiscSettingSchema = z.object({ editorFont: z.enum(["default", "os"]).default("default"), showTextLineNumber: z.boolean().default(false), showAddAudioItemButton: z.boolean().default(true), - experimentalSetting: experimentalSettingSchema.default({}), - acceptRetrieveTelemetry: z - .enum(["Unconfirmed", "Accepted", "Refused"]) - .default("Unconfirmed"), - acceptTerms: z - .enum(["Unconfirmed", "Accepted", "Rejected"]) - .default("Unconfirmed"), splitTextWhenPaste: z .enum(["PERIOD_AND_NEW_LINE", "NEW_LINE", "OFF"]) .default("PERIOD_AND_NEW_LINE"), splitterPosition: splitterPositionSchema.default({}), - confirmedTips: z - .object({ - tweakableSliderByScroll: z.boolean().default(false), - engineStartedOnAltPort: z.boolean().default(false), - notifyOnGenerate: z.boolean().default(false), - }) - .default({}), - registeredEngineDirs: z.string().array().default([]), - recentlyUsedProjects: z.string().array().default([]), enableMultiEngine: z.boolean().default(false), }); +export type RootMiscSetting = z.infer; + +export const configSchema = z + .object({ + inheritAudioInfo: z.boolean().default(true), + activePointScrollMode: z + .enum(["CONTINUOUSLY", "PAGE", "OFF"]) + .default("OFF"), + savingSetting: z + .object({ + fileEncoding: z.enum(["UTF-8", "Shift_JIS"]).default("UTF-8"), + fileNamePattern: z.string().default(""), + fixedExportEnabled: z.boolean().default(false), + avoidOverwrite: z.boolean().default(false), + fixedExportDir: z.string().default(""), + exportLab: z.boolean().default(false), + exportText: z.boolean().default(false), + outputStereo: z.boolean().default(false), + audioOutputDevice: z.string().default(""), + }) + .default({}), + hotkeySettings: hotkeySettingSchema.array().default(defaultHotkeySettings), + toolbarSetting: toolbarSettingSchema + .array() + .default(defaultToolbarButtonSetting), + engineSettings: z.record(engineIdSchema, engineSettingSchema).default({}), + userCharacterOrder: speakerIdSchema.array().default([]), + defaultStyleIds: z + .object({ + engineId: engineIdSchema + .or(z.literal(EngineId("00000000-0000-0000-0000-000000000000"))) + .default(EngineId("00000000-0000-0000-0000-000000000000")), + speakerUuid: speakerIdSchema, + defaultStyleId: styleIdSchema, + }) + .array() + .default([]), + presets: z + .object({ + items: z + .record( + presetKeySchema, + z.object({ + name: z.string(), + speedScale: z.number(), + pitchScale: z.number(), + intonationScale: z.number(), + volumeScale: z.number(), + prePhonemeLength: z.number(), + postPhonemeLength: z.number(), + morphingInfo: z + .object({ + rate: z.number(), + targetEngineId: engineIdSchema, + targetSpeakerId: speakerIdSchema, + targetStyleId: styleIdSchema, + }) + .optional(), + }) + ) + .default({}), + keys: presetKeySchema.array().default([]), + }) + .default({}), + defaultPresetKeys: z.record(voiceIdSchema, presetKeySchema).default({}), + currentTheme: z.string().default("Default"), + experimentalSetting: experimentalSettingSchema.default({}), + acceptRetrieveTelemetry: z + .enum(["Unconfirmed", "Accepted", "Refused"]) + .default("Unconfirmed"), + acceptTerms: z + .enum(["Unconfirmed", "Accepted", "Rejected"]) + .default("Unconfirmed"), + confirmedTips: z + .object({ + tweakableSliderByScroll: z.boolean().default(false), + engineStartedOnAltPort: z.boolean().default(false), + notifyOnGenerate: z.boolean().default(false), + }) + .default({}), + registeredEngineDirs: z.string().array().default([]), + recentlyUsedProjects: z.string().array().default([]), + }) + .merge(RootMiscSettingSchema); export type ConfigType = z.infer; export const envEngineInfoSchema = z.object({ diff --git a/src/type/utility.ts b/src/type/utility.ts new file mode 100644 index 0000000000..365c30db99 --- /dev/null +++ b/src/type/utility.ts @@ -0,0 +1,6 @@ +// XとYが同じ型かどうかを判定する +export type IsEqual = (() => T extends X ? 1 : 2) extends < + T +>() => T extends Y ? 1 : 2 + ? true + : false; diff --git a/src/views/EditorHome.vue b/src/views/EditorHome.vue index a34c16c412..827f55ab75 100644 --- a/src/views/EditorHome.vue +++ b/src/views/EditorHome.vue @@ -358,8 +358,9 @@ const updateSplitterPosition = async ( ...splitterPosition.value, [propertyName]: newValue, }; - store.dispatch("SET_SPLITTER_POSITION", { - splitterPosition: newSplitterPosition, + await store.dispatch("SET_ROOT_MISC_SETTING", { + key: "splitterPosition", + value: newSplitterPosition, }); };