diff --git a/src/components/App.vue b/src/components/App.vue index a6418c2807..a536f798f4 100644 --- a/src/components/App.vue +++ b/src/components/App.vue @@ -32,6 +32,7 @@ import AllDialog from "@/components/Dialog/AllDialog.vue"; import MenuBar from "@/components/Menu/MenuBar/MenuBar.vue"; import { useMenuBarData as useTalkMenuBarData } from "@/components/Talk/menuBarData"; import { useMenuBarData as useSingMenuBarData } from "@/components/Sing/menuBarData"; +import { themeToCss } from "@/domain/theme"; const store = useStore(); @@ -79,6 +80,33 @@ watch( }, ); +// テーマの変更を監視してCSS変数を変更する +watch( + () => + [ + store.state.currentTheme, + store.state.availableThemes, + store.state.isVuexReady, + ] as const, + ([currentTheme, availableThemes, isVuexReady]) => { + const theme = availableThemes.find((value) => { + return value.name == currentTheme; + }); + + if (theme == undefined) { + // NOTE: Vuexが初期化されていない場合はまだテーマが読み込まれていないので無視 + if (isVuexReady) { + throw Error(`Theme not found: ${currentTheme}`); + } else { + return; + } + } + + themeToCss(theme); + }, + { immediate: true }, +); + // ソフトウェアを初期化 const { hotkeyManager } = useHotkeyManager(); const isEnginesReady = ref(false); diff --git a/src/components/Talk/TalkEditor.stories.ts b/src/components/Talk/TalkEditor.stories.ts index 17e43cbbd4..1c2f9b8c77 100644 --- a/src/components/Talk/TalkEditor.stories.ts +++ b/src/components/Talk/TalkEditor.stories.ts @@ -12,12 +12,20 @@ import { mockHost, } from "@/storybook/engineMock"; import { proxyStoreCreator } from "@/store/proxy"; -import { CharacterInfo, EngineId, SpeakerId, StyleId } from "@/type/preload"; +import { + CharacterInfo, + EngineId, + SpeakerId, + StyleId, + ThemeConf, +} from "@/type/preload"; import { getEngineManifestMock } from "@/storybook/engineMock/manifestMock"; import { getSpeakerInfoMock, getSpeakersMock, } from "@/storybook/engineMock/speakerResourceMock"; +import { themeToCss } from "@/domain/theme"; +import defaultTheme from "@/../public/themes/default.json"; const meta: Meta = { component: TalkEditor, @@ -33,7 +41,10 @@ const meta: Meta = { const store = createStoreWrapper({ proxyStoreDI: proxyStoreCreator(createOpenAPIEngineMock()), }); - store.dispatch("HYDRATE_SETTING_STORE"); // FIXME: 色設定取得のため。設定も読み込んでしまうため不要にしたい。 + + themeToCss(defaultTheme as ThemeConf); + + document.body.setAttribute("data-editor-font", "default"); // TODO: 共通化する // context.parameters.store = store; diff --git a/src/domain/theme.ts b/src/domain/theme.ts new file mode 100644 index 0000000000..eaaf76262b --- /dev/null +++ b/src/domain/theme.ts @@ -0,0 +1,37 @@ +import { colors, Dark, setCssVar } from "quasar"; +import { ThemeColorType, ThemeConf } from "@/type/preload"; + +/** テーマの設定をCSSへ反映する */ +export function themeToCss(theme: ThemeConf) { + for (const key in theme.colors) { + const color = theme.colors[key as ThemeColorType]; + const { r, g, b } = colors.hexToRgb(color); + document.documentElement.style.setProperty(`--color-${key}`, color); + document.documentElement.style.setProperty( + `--color-${key}-rgb`, + `${r}, ${g}, ${b}`, + ); + } + const mixColors: ThemeColorType[][] = [ + ["primary", "background"], + ["warning", "background"], + ]; + for (const [color1, color2] of mixColors) { + const color1Rgb = colors.hexToRgb(theme.colors[color1]); + const color2Rgb = colors.hexToRgb(theme.colors[color2]); + const r = Math.trunc((color1Rgb.r + color2Rgb.r) / 2); + const g = Math.trunc((color1Rgb.g + color2Rgb.g) / 2); + const b = Math.trunc((color1Rgb.b + color2Rgb.b) / 2); + const propertyName = `--color-mix-${color1}-${color2}-rgb`; + const cssColor = `${r}, ${g}, ${b}`; + document.documentElement.style.setProperty(propertyName, cssColor); + } + Dark.set(theme.isDark); + setCssVar("primary", theme.colors["primary"]); + setCssVar("warning", theme.colors["warning"]); + + document.documentElement.setAttribute( + "is-dark-theme", + theme.isDark ? "true" : "false", + ); +} diff --git a/src/store/setting.ts b/src/store/setting.ts index de6d909095..23056cd2ba 100644 --- a/src/store/setting.ts +++ b/src/store/setting.ts @@ -219,6 +219,8 @@ export const settingStore = createPartialStore({ state.currentTheme = currentTheme; }, action({ state, commit }, { currentTheme }: { currentTheme: string }) { + // メモ:テーマ周りリファクタリングしたのでモック挿せるはず。 + // うまくいけば、テーマ周りのリファクタリングだけでプルリク出す。 window.backend.setSetting("currentTheme", currentTheme); const theme = state.availableThemes.find((value) => { return value.name == currentTheme; @@ -228,38 +230,6 @@ export const settingStore = createPartialStore({ throw Error("Theme not found"); } - for (const key in theme.colors) { - const color = theme.colors[key as ThemeColorType]; - const { r, g, b } = colors.hexToRgb(color); - document.documentElement.style.setProperty(`--color-${key}`, color); - document.documentElement.style.setProperty( - `--color-${key}-rgb`, - `${r}, ${g}, ${b}`, - ); - } - const mixColors: ThemeColorType[][] = [ - ["primary", "background"], - ["warning", "background"], - ]; - for (const [color1, color2] of mixColors) { - const color1Rgb = colors.hexToRgb(theme.colors[color1]); - const color2Rgb = colors.hexToRgb(theme.colors[color2]); - const r = Math.trunc((color1Rgb.r + color2Rgb.r) / 2); - const g = Math.trunc((color1Rgb.g + color2Rgb.g) / 2); - const b = Math.trunc((color1Rgb.b + color2Rgb.b) / 2); - const propertyName = `--color-mix-${color1}-${color2}-rgb`; - const cssColor = `${r}, ${g}, ${b}`; - document.documentElement.style.setProperty(propertyName, cssColor); - } - Dark.set(theme.isDark); - setCssVar("primary", theme.colors["primary"]); - setCssVar("warning", theme.colors["warning"]); - - document.documentElement.setAttribute( - "is-dark-theme", - theme.isDark ? "true" : "false", - ); - window.backend.setNativeTheme(theme.isDark ? "dark" : "light"); commit("SET_CURRENT_THEME_SETTING", {