diff --git a/src/main/resolve/theme.ts b/src/main/resolve/theme.ts index 777179e..0c6b8eb 100644 --- a/src/main/resolve/theme.ts +++ b/src/main/resolve/theme.ts @@ -1,4 +1,4 @@ -import { copyFile, readdir, readFile } from 'fs/promises' +import { copyFile, readdir, readFile, writeFile } from 'fs/promises' import { themesDir } from '../utils/dirs' import path from 'path' import axios from 'axios' @@ -56,9 +56,17 @@ export async function importThemes(files: string[]): Promise { } } +export async function readTheme(theme: string): Promise { + if (!existsSync(path.join(themesDir(), theme))) return '' + return await readFile(path.join(themesDir(), theme), 'utf-8') +} + +export async function writeTheme(theme: string, css: string): Promise { + await writeFile(path.join(themesDir(), theme), css) +} + export async function applyTheme(theme: string): Promise { - if (!existsSync(path.join(themesDir(), theme))) return - const css = await readFile(path.join(themesDir(), theme), 'utf-8') + const css = await readTheme(theme) await mainWindow?.webContents.removeInsertedCSS(insertedCSSKey || '') insertedCSSKey = await mainWindow?.webContents.insertCSS(css) } diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index ed9bf95..d6a19c4 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -65,7 +65,14 @@ import { getInterfaces } from '../sys/interface' import { copyEnv } from '../resolve/tray' import { registerShortcut } from '../resolve/shortcut' import { mainWindow } from '..' -import { applyTheme, fetchThemes, importThemes, resolveThemes } from '../resolve/theme' +import { + applyTheme, + fetchThemes, + importThemes, + readTheme, + resolveThemes, + writeTheme +} from '../resolve/theme' import { subStoreCollections, subStoreSubs } from '../core/subStoreApi' import { logDir } from './dirs' import path from 'path' @@ -205,6 +212,8 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('resolveThemes', () => ipcErrorWrapper(resolveThemes)()) ipcMain.handle('fetchThemes', () => ipcErrorWrapper(fetchThemes)()) ipcMain.handle('importThemes', (_e, file) => ipcErrorWrapper(importThemes)(file)) + ipcMain.handle('readTheme', (_e, theme) => ipcErrorWrapper(readTheme)(theme)) + ipcMain.handle('writeTheme', (_e, theme, css) => ipcErrorWrapper(writeTheme)(theme, css)) ipcMain.handle('applyTheme', (_e, theme) => ipcErrorWrapper(applyTheme)(theme)) ipcMain.handle('copyEnv', (_e, type) => ipcErrorWrapper(copyEnv)(type)) ipcMain.handle('alert', (_e, msg) => { diff --git a/src/renderer/src/components/settings/css-editor-modal.tsx b/src/renderer/src/components/settings/css-editor-modal.tsx new file mode 100644 index 0000000..874f3d4 --- /dev/null +++ b/src/renderer/src/components/settings/css-editor-modal.tsx @@ -0,0 +1,54 @@ +import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react' +import { BaseEditor } from '@renderer/components/base/base-editor' +import { readTheme } from '@renderer/utils/ipc' +import React, { useEffect, useState } from 'react' +interface Props { + theme: string + onCancel: () => void + onConfirm: (script: string) => void +} +const CSSEditorModal: React.FC = (props) => { + const { theme, onCancel, onConfirm } = props + const [currData, setCurrData] = useState('') + + useEffect(() => { + if (theme) { + readTheme(theme).then((css) => { + setCurrData(css) + }) + } + }, [theme]) + + return ( + + + 编辑主题 + + setCurrData(value || '')} + /> + + + + + + + + ) +} + +export default CSSEditorModal diff --git a/src/renderer/src/components/settings/general-config.tsx b/src/renderer/src/components/settings/general-config.tsx index b478b76..d617ae6 100644 --- a/src/renderer/src/components/settings/general-config.tsx +++ b/src/renderer/src/components/settings/general-config.tsx @@ -5,6 +5,7 @@ import { Button, Input, Select, SelectItem, Switch, Tab, Tabs, Tooltip } from '@ import { BiCopy, BiSolidFileImport } from 'react-icons/bi' import useSWR from 'swr' import { + applyTheme, checkAutoRun, copyEnv, disableAutoRun, @@ -14,17 +15,21 @@ import { importThemes, relaunchApp, resolveThemes, - restartCore + restartCore, + writeTheme } from '@renderer/utils/ipc' import { useAppConfig } from '@renderer/hooks/use-app-config' import { platform } from '@renderer/utils/init' import { useTheme } from 'next-themes' import { IoIosHelpCircle, IoMdCloudDownload } from 'react-icons/io' +import { MdEditDocument } from 'react-icons/md' +import CSSEditorModal from './css-editor-modal' const GeneralConfig: React.FC = () => { const { data: enable, mutate: mutateEnable } = useSWR('checkAutoRun', checkAutoRun) const { appConfig, patchAppConfig } = useAppConfig() const [customThemes, setCustomThemes] = useState<{ key: string; label: string }[]>() + const [openCSSEditor, setOpenCSSEditor] = useState(false) const [fetching, setFetching] = useState(false) const { setTheme } = useTheme() const { @@ -49,6 +54,17 @@ const GeneralConfig: React.FC = () => { return ( <> + {openCSSEditor && ( + setOpenCSSEditor(false)} + onConfirm={async (css: string) => { + await writeTheme(customTheme, css) + await applyTheme(customTheme) + setOpenCSSEditor(false) + }} + /> + )} { > + } > diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index 2cb697e..7832cd0 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -343,6 +343,14 @@ export async function importThemes(files: string[]): Promise { return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('importThemes', files)) } +export async function readTheme(theme: string): Promise { + return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('readTheme', theme)) +} + +export async function writeTheme(theme: string, css: string): Promise { + return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('writeTheme', theme, css)) +} + let applyThemeRunning = false const waitList: string[] = [] export async function applyTheme(theme: string): Promise {