From d79ce2992d094b4866618805e1cafc9a166df018 Mon Sep 17 00:00:00 2001 From: chanLee <2535849195@qq.com> Date: Wed, 24 Dec 2025 22:57:21 +0800 Subject: [PATCH 1/5] refactor(ipc): refactor ipc and some file layout --- src/hooks/useContent.ts | 3 +- src/hooks/useContext.ts | 15 +- src/hooks/useFile.ts | 30 ++-- src/hooks/useFont.ts | 3 +- src/hooks/useTab.ts | 21 +-- src/hooks/useTheme.ts | 5 +- src/hooks/useTitle.ts | 3 +- src/hooks/useUpdateDialog.ts | 3 +- src/hooks/useWorkSpace.ts | 7 +- src/plugins/customPastePlugin.ts | 5 +- src/plugins/imagePathPlugin.ts | 4 +- src/renderer/components/About.vue | 3 +- src/renderer/components/StatusBar.vue | 3 +- src/renderer/components/TabBar.vue | 2 +- src/renderer/components/ThemeEditor.vue | 5 +- src/renderer/components/TitleBar.vue | 14 +- src/renderer/components/ui/tree/context.ts | 2 +- src/renderer/context/index.ts | 2 +- .../{shared => hooks}/createContext.ts | 0 src/renderer/hooks/useContext.ts | 0 src/renderer/services/deprecated/index.ts | 137 ++++++++++++++++++ src/renderer/services/index.ts | 4 + src/renderer/shared/platform.ts | 7 + 23 files changed, 220 insertions(+), 58 deletions(-) rename src/renderer/{shared => hooks}/createContext.ts (100%) delete mode 100644 src/renderer/hooks/useContext.ts create mode 100644 src/renderer/services/deprecated/index.ts create mode 100644 src/renderer/services/index.ts create mode 100644 src/renderer/shared/platform.ts diff --git a/src/hooks/useContent.ts b/src/hooks/useContent.ts index 31756b1..4139ee1 100644 --- a/src/hooks/useContent.ts +++ b/src/hooks/useContent.ts @@ -1,4 +1,5 @@ import { computed, ref, watch } from 'vue' +import { changeSaveStatus } from '@/renderer/services' import useTab from './useTab' const contentInfo = { @@ -20,7 +21,7 @@ watch(isModified, (newValue) => { // 只有在有内容时才通知主进程保存状态 // 如果 markdown 为空且 originalContent 也为空,说明是新建文档,不需要通知 if (contentInfo.markdown.value || contentInfo.originalContent.value) { - window.electronAPI.changeSaveStatus(!newValue) // 通知主进程保存状态, 修改后(isModified==true) isSaved 为 false + changeSaveStatus(!newValue) // 通知主进程保存状态, 修改后(isModified==true) isSaved 为 false } }, { immediate: true }) diff --git a/src/hooks/useContext.ts b/src/hooks/useContext.ts index 13b82b1..c8ab955 100644 --- a/src/hooks/useContext.ts +++ b/src/hooks/useContext.ts @@ -1,4 +1,5 @@ import { nextTick, ref, watch } from 'vue' +import { closeDiscard, on as onIpc } from '@/renderer/services' import emitter from '../renderer/events' import useContent from './useContent' import useFile from './useFile' @@ -70,7 +71,7 @@ export function useContext() { if (unsavedTabs.length === 0) { // 没有未保存的tab,直接关闭 - window.electronAPI.closeDiscard() + closeDiscard() return } @@ -101,21 +102,21 @@ export function useContext() { } // 所有tab都处理完成,关闭应用 - window.electronAPI.closeDiscard() + closeDiscard() } // 监听关闭确认事件 - window.electronAPI.on('close:confirm', async () => { + onIpc('close:confirm', async () => { await handleAppCloseConfirm() }) // 监听保存触发事件 - window.electronAPI.on('trigger-save', async () => { + onIpc('trigger-save', async () => { await onSave() }) // 监听自定义主题保存事件 - window.electronAPI.on('custom-theme-saved', (theme) => { + onIpc('custom-theme-saved', (theme) => { // 重新获取主题列表以包含新保存的主题 const { setTheme } = useTheme() setTheme(theme.name) @@ -181,7 +182,7 @@ export function useContext() { // 保存成功 if (isLastTab) { // 如果是最后一个tab,关闭应用 - window.electronAPI.closeDiscard() + closeDiscard() } else { // 否则关闭tab close(tabId) @@ -199,7 +200,7 @@ export function useContext() { if (isLastTab) { // 如果是最后一个tab,关闭应用 - window.electronAPI.closeDiscard() + closeDiscard() } else { // 否则关闭tab close(tabId) diff --git a/src/hooks/useFile.ts b/src/hooks/useFile.ts index d59888d..752c955 100644 --- a/src/hooks/useFile.ts +++ b/src/hooks/useFile.ts @@ -3,6 +3,7 @@ import type { Tab } from '@/types/tab' import { nextTick, onUnmounted } from 'vue' import { processImagePaths } from '@/plugins/imagePathPlugin' import emitter from '@/renderer/events' +import { getIsReadOnly, getPathForFile, on as onIpc, onOpenFileAtLaunch, openFile, readFileByPath, removeListener, saveFileAs } from '@/renderer/services' import useContent from './useContent' import useTab from './useTab' import useTitle from './useTitle' @@ -27,7 +28,7 @@ const { async function onOpen(result?: { filePath: string, content: string } | null) { if (!result) { - result = await window.electronAPI.openFile() + result = await openFile() } if (result) { filePath.value = result.filePath @@ -47,11 +48,11 @@ async function onOpen(result?: { filePath: string, content: string } | null) { markdown.value = tab.content originalContent.value = result.content tab.isModified = false - tab.readOnly = await window.electronAPI.getIsReadOnly(result.filePath) + tab.readOnly = await getIsReadOnly(result.filePath) } else { // 创建新tab const tab = await createTabFromFile(result.filePath, result.content) - tab.readOnly = await window.electronAPI.getIsReadOnly(result.filePath) + tab.readOnly = await getIsReadOnly(result.filePath) // 更新当前内容状态 markdown.value = tab.content originalContent.value = result.content @@ -82,7 +83,7 @@ async function onSaveAs() { // 先更新当前tab的内容 updateCurrentTabContent(markdown.value) - const result = await window.electronAPI.saveFileAs(markdown.value) + const result = await saveFileAs(markdown.value) if (result) { // 更新当前tab的文件路径 if (currentTab.value) { @@ -105,10 +106,10 @@ function registerMenuEventsOnce() { return hasRegistered = true - window.electronAPI?.onOpenFileAtLaunch?.(async ({ filePath: launchFilePath, content }) => { + onOpenFileAtLaunch?.(async ({ filePath: launchFilePath, content }) => { // 创建新tab const tab = await createTabFromFile(launchFilePath, content) - tab.readOnly = await window.electronAPI.getIsReadOnly(launchFilePath) + tab.readOnly = await getIsReadOnly(launchFilePath) // 更新当前内容状态 markdown.value = tab.content @@ -121,8 +122,8 @@ function registerMenuEventsOnce() { }) }) - window.electronAPI.on?.('menu-open', onOpen) - window.electronAPI.on?.('menu-save', onSave) + onIpc('menu-open', onOpen) + onIpc('menu-save', onSave) // 拖拽打开 Markdown 文件 const handleDragOver = (event: DragEvent) => { @@ -186,9 +187,10 @@ function registerMenuEventsOnce() { try { // 使用 webUtils.getPathForFile 方法 - const pathResult = window.electronAPI.getPathForFile(mdFile) + const pathResult = getPathForFile(mdFile) fullPath = pathResult || null } catch (error) { + console.error('获取文件完整路径失败:', error) } if (!fullPath) { @@ -199,7 +201,7 @@ function registerMenuEventsOnce() { } if (fullPath) { - const result = await window.electronAPI.readFileByPath(fullPath) + const result = await readFileByPath(fullPath) if (result) { if (userChoice === 'overwrite') { // 覆盖更新当前tab的文件信息 @@ -210,11 +212,11 @@ function registerMenuEventsOnce() { markdown.value = processedContent filePath.value = result.filePath originalContent.value = result.content - currentTab.value!.readOnly = await window.electronAPI.getIsReadOnly(result.filePath) + currentTab.value!.readOnly = await getIsReadOnly(result.filePath) } else { // 创建新tab const tab = await createTabFromFile(result.filePath, result.content) - tab.readOnly = await window.electronAPI.getIsReadOnly(result.filePath) + tab.readOnly = await getIsReadOnly(result.filePath) // 更新当前内容 markdown.value = tab.content filePath.value = result.filePath @@ -297,8 +299,8 @@ emitter.on('tab:switch', tabSwitch) export default function useFile() { onUnmounted(() => { - window.electronAPI?.removeListener?.('menu-open', onOpen) - window.electronAPI?.removeListener?.('menu-save', onSave) + removeListener('menu-open', onOpen) + removeListener('menu-save', onSave) }) return { onOpen, diff --git a/src/hooks/useFont.ts b/src/hooks/useFont.ts index 5ea7a2b..2cc400a 100644 --- a/src/hooks/useFont.ts +++ b/src/hooks/useFont.ts @@ -1,6 +1,7 @@ import type { Font, FontConfig, FontList, FontSizeConfig, FontSizeType, FontType } from '@/types/font' import { computed, ref } from 'vue' import { fontCssVariables, fontSizeCssVariables, fontSizeOptions } from '@/config/fonts' +import { getSystemFonts } from '@/renderer/services' import { useConfig } from './useConfig' // 系统字体列表 @@ -18,7 +19,7 @@ const currentFontSize = computed(() => getConf('font').size) async function init() { // 获取系统字体列表 try { - const systemFonts = await window.electronAPI.getSystemFonts() + const systemFonts = await getSystemFonts() // Font 对象数组 fontList.value = systemFonts.map((fontName) => { const name = fontName.replace(/^['"]|['"]$/g, '') diff --git a/src/hooks/useTab.ts b/src/hooks/useTab.ts index a934c7b..e96607c 100644 --- a/src/hooks/useTab.ts +++ b/src/hooks/useTab.ts @@ -5,6 +5,7 @@ import autotoast from 'autotoast.js' import { computed, nextTick, ref, watch } from 'vue' import { processImagePaths, setCurrentMarkdownFilePath } from '@/plugins/imagePathPlugin' import emitter from '@/renderer/events' +import { closeDiscard, getIsReadOnly, on as onIpc, onOpenFileAtLaunch, readFileByPath, saveFile, watchFiles } from '@/renderer/services' import { createInertiaScroll } from '@/utils/inertiaScroll' import { randomUUID } from '@/utils/tool' import { isShowOutline } from './useOutline' @@ -28,7 +29,7 @@ const defaultTab: Tab = { } tabs.value.push(defaultTab) activeTabId.value = defaultTab.id -window.electronAPI?.onOpenFileAtLaunch((_payload) => { +onOpenFileAtLaunch((_payload) => { if (tabs.value.length === 1 && tabs.value[0].id === defaultTabUUid && !tabs.value[0].isModified) { tabs.value = [] } @@ -142,7 +143,7 @@ async function saveCurrentTab(): Promise { return false try { - const saved = await window.electronAPI.saveFile(currentTab.filePath, currentTab.content) + const saved = await saveFile(currentTab.filePath, currentTab.content) if (saved) { currentTab.filePath = saved currentTab.name = getFileName(saved) // 更新标签名称 @@ -170,7 +171,7 @@ async function createTabFromFile(filePath: string, content: string): Promise { } // 读取文件内容 - const result = await window.electronAPI.readFileByPath(filePath) + const result = await readFileByPath(filePath) if (result) { // 如果当前有且只有一个默认未命名且未修改的tab,则复用该tab if ( @@ -207,7 +208,7 @@ async function openFile(filePath: string): Promise { } else { // 创建新tab const newTab = await createTabFromFile(result.filePath, result.content) - newTab.readOnly = await window.electronAPI?.getIsReadOnly(filePath) || false + newTab.readOnly = await getIsReadOnly(filePath) || false // 切换新tab switchToTab(newTab.id) @@ -377,7 +378,7 @@ function closeWithConfirm(id: string) { // 如果没有未保存内容 if (isLastTab) { // 如果是最后一个tab,直接关闭应用 - window.electronAPI.closeDiscard() + closeDiscard() } else { // 否则直接关闭tab close(id) @@ -446,7 +447,7 @@ watch( // 通知ipc console.log('通知ipc', newFilePaths) - window.electronAPI.watchFiles(newFilePaths) + watchFiles(newFilePaths) }, { immediate: true, @@ -454,13 +455,13 @@ watch( ) // 文件变动回调事件 -window.electronAPI.on?.('file:changed', async (paths) => { +onIpc('file:changed', async (paths) => { const tab = tabs.value.find(tab => tab.filePath === paths) if (!tab) return if (!tab.isModified) { - const result = await window.electronAPI.readFileByPath(paths) + const result = await readFileByPath(paths) if (!result) return // 处理图片路径 @@ -489,7 +490,7 @@ window.electronAPI.on?.('file:changed', async (paths) => { } // 读取新文件内容 - const result = await window.electronAPI.readFileByPath(paths) + const result = await readFileByPath(paths) if (!result) return diff --git a/src/hooks/useTheme.ts b/src/hooks/useTheme.ts index c2ca85a..c4dc37a 100644 --- a/src/hooks/useTheme.ts +++ b/src/hooks/useTheme.ts @@ -2,6 +2,7 @@ import type { Theme, ThemeName } from '@/types/theme' import autotoast from 'autotoast.js' import { getCurrentInstance, onMounted, onUnmounted, ref, toRaw } from 'vue' import { cssVarsDesMap, themeNameMap } from '@/config/theme' +import { openThemeEditor } from '@/renderer/services' import { isThemeObject } from '@/types/theme' import themeManager from '@/utils/themeManager' import { randomUUID } from '@/utils/tool' @@ -248,7 +249,7 @@ function addTempTheme(themeName?: ThemeName) { // 存储到 localStorage setEditingThemeToStorage(themeName) - window.electronAPI.openThemeEditor() + openThemeEditor() } else { // 新增主题:基于当前主题创建新主题 const themeList = themes.value.length ? themes.value : getThemes() @@ -288,7 +289,7 @@ function addTempTheme(themeName?: ThemeName) { } } - window.electronAPI.openThemeEditor() + openThemeEditor() } // 删除本地主题 diff --git a/src/hooks/useTitle.ts b/src/hooks/useTitle.ts index e4c100d..ed97b7b 100644 --- a/src/hooks/useTitle.ts +++ b/src/hooks/useTitle.ts @@ -1,4 +1,5 @@ import { computed, ref } from 'vue' +import { setTitle } from '@/renderer/services' import useContent from './useContent' const { filePath, isModified } = useContent() @@ -13,7 +14,7 @@ const fileName = computed(() => { function updateTitle() { const name = (fileName.value || 'Untitled') const prefix = isModified.value ? '*' : '' - window.electronAPI.setTitle(`${prefix}${name}`) + setTitle(`${prefix}${name}`) title.value = `${prefix}${name}` } diff --git a/src/hooks/useUpdateDialog.ts b/src/hooks/useUpdateDialog.ts index 9362432..e8abb3a 100644 --- a/src/hooks/useUpdateDialog.ts +++ b/src/hooks/useUpdateDialog.ts @@ -1,5 +1,6 @@ import autotoast from 'autotoast.js' import { ref } from 'vue' +import { openExternal } from '@/renderer/services' export function useUpdateDialog() { const isDialogVisible = ref(false) @@ -25,7 +26,7 @@ export function useUpdateDialog() { return } // 打开浏览器下载页面 - window.electronAPI.openExternal(downloadUrl) + openExternal(downloadUrl) hideDialog() } function handleLater() { diff --git a/src/hooks/useWorkSpace.ts b/src/hooks/useWorkSpace.ts index 39749c7..a54a052 100644 --- a/src/hooks/useWorkSpace.ts +++ b/src/hooks/useWorkSpace.ts @@ -1,5 +1,6 @@ import toast from 'autotoast.js' import { ref, watch } from 'vue' +import { getDirectoryFiles, showOpenDialog } from '@/renderer/services' import useTab from './useTab' const { tabs, currentTab } = useTab() @@ -35,7 +36,7 @@ async function getWorkSpace() { try { isLoading.value = true - const result = await window.electronAPI.getDirectoryFiles(directoryPath) + const result = await getDirectoryFiles(directoryPath) if (!result) return @@ -56,7 +57,7 @@ async function getWorkSpace() { // 打开选择文件夹对话框 async function setWorkSpace() { try { - const result = await window.electronAPI.showOpenDialog({ + const result = await showOpenDialog({ properties: ['openDirectory'], title: '选择文件夹文件夹', }) @@ -68,7 +69,7 @@ async function setWorkSpace() { workSpace.value = null // 获取选择的文件夹内容 - const directoryFiles = await window.electronAPI.getDirectoryFiles(selectedPath) + const directoryFiles = await getDirectoryFiles(selectedPath) if (directoryFiles && directoryFiles.length > 0) { workSpace.value = directoryFiles diff --git a/src/plugins/customPastePlugin.ts b/src/plugins/customPastePlugin.ts index a089519..672a2e2 100644 --- a/src/plugins/customPastePlugin.ts +++ b/src/plugins/customPastePlugin.ts @@ -1,6 +1,7 @@ import type { Uploader } from '@milkdown/kit/plugin/upload' import type { Node, Schema } from '@milkdown/kit/prose/model' import { uploadImage } from '@/api' +import { getFilePathInClipboard, writeTempImage } from '@/renderer/services' export const uploader: Uploader = async (files, schema) => { const images: File[] = [] @@ -57,14 +58,14 @@ async function upload(image: File, nodes: Node[], schema: Schema) { nodes.push(schema.nodes.image.createAndFill({ src, alt: image.name }) as Node) } async function local(image: File, nodes: Node[], schema: Schema) { - const filePath = await window.electronAPI.getFilePathInClipboard() + const filePath = await getFilePathInClipboard() if (filePath) { nodes.push(schema.nodes.image.createAndFill({ src: filePath, alt: image.name }) as Node) } else { const arrayBuffer = await image.arrayBuffer() const buffer = new Uint8Array(arrayBuffer) // Convert Uint8Array to ArrayBuffer to satisfy the ArrayBufferLike parameter - const tempPath = await window.electronAPI.writeTempImage(buffer, localStorage.getItem('localImagePath') || '/temp') + const tempPath = await writeTempImage(buffer, localStorage.getItem('localImagePath') || '/temp') nodes.push(schema.nodes.image.createAndFill({ src: tempPath, alt: image.name }) as Node) } } diff --git a/src/plugins/imagePathPlugin.ts b/src/plugins/imagePathPlugin.ts index 10b4c01..5f6eafa 100644 --- a/src/plugins/imagePathPlugin.ts +++ b/src/plugins/imagePathPlugin.ts @@ -1,3 +1,5 @@ +import { resolveImagePath } from '@/renderer/services' + // 用于存储当前 Markdown 文件路径的全局变量 let currentMarkdownFilePath: string | null = null @@ -24,7 +26,7 @@ export async function processImagePaths(markdownContent: string, markdownFilePat // 只处理相对路径 if (!imagePath.startsWith('http') && !imagePath.startsWith('file://') && !imagePath.startsWith('data:')) { try { - const resolvedPath = await window.electronAPI.resolveImagePath(markdownFilePath, imagePath) + const resolvedPath = await resolveImagePath(markdownFilePath, imagePath) if (resolvedPath !== imagePath) { const newImageMarkdown = `![${alt}](${resolvedPath})` diff --git a/src/renderer/components/About.vue b/src/renderer/components/About.vue index db1e6e6..cf0c97d 100644 --- a/src/renderer/components/About.vue +++ b/src/renderer/components/About.vue @@ -4,9 +4,10 @@ import { checkUpdate } from '@/api/update' import logo from '@/assets/icons/milkup.ico' import { version } from '../../../package.json' import emitter from '../events' +import { openExternal } from '../services' function openByDefaultBrowser(url: string) { - window.electronAPI.openExternal(url) + openExternal(url) } const updateInfo = JSON.parse(localStorage.getItem('updateInfo') || '{}') diff --git a/src/renderer/components/StatusBar.vue b/src/renderer/components/StatusBar.vue index e341cd7..c91d916 100644 --- a/src/renderer/components/StatusBar.vue +++ b/src/renderer/components/StatusBar.vue @@ -2,6 +2,7 @@ import { computed, ref } from 'vue' import { toggleShowOutline } from '@/hooks/useOutline' import useSourceCode from '@/hooks/useSourceCode' +import { on as onIpc } from '../services' const props = defineProps<{ content: string @@ -39,7 +40,7 @@ function countMarkdownChars(text: string): number { const base64Regex = /data:image\/[a-zA-Z]+;base64,[a-zA-Z0-9+/=]+/g return (text.replaceAll(' ', '').replace(base64Regex, 'image').trim() || '').split('').length } -window.electronAPI.on('view:toggleView', () => { +onIpc('view:toggleView', () => { toggleSourceCode() }) diff --git a/src/renderer/components/TabBar.vue b/src/renderer/components/TabBar.vue index 2137576..27f4d01 100644 --- a/src/renderer/components/TabBar.vue +++ b/src/renderer/components/TabBar.vue @@ -3,6 +3,7 @@ import { onMounted, onUnmounted, ref } from 'vue' import { vDraggable } from 'vue-draggable-plus' import useFile from '@/hooks/useFile' import useTab from '@/hooks/useTab' +import { isMac } from '@/renderer/shared/platform' const { formattedTabs, @@ -20,7 +21,6 @@ const { createNewFile } = useFile() // 拦截 ctrl/cmd + w 快捷键关闭tab function handleCloseTabShortcut(e: KeyboardEvent) { - const isMac = window.electronAPI.platform === 'darwin' if ((isMac ? e.metaKey : e.ctrlKey) && e.key.toLowerCase() === 'w') { e.preventDefault() if (activeTabId.value) { diff --git a/src/renderer/components/ThemeEditor.vue b/src/renderer/components/ThemeEditor.vue index 52af9cc..c8f5a02 100644 --- a/src/renderer/components/ThemeEditor.vue +++ b/src/renderer/components/ThemeEditor.vue @@ -3,11 +3,12 @@ import type { ThemeName } from '@/types/theme' import autotoast from 'autotoast.js' import { onMounted, onUnmounted, ref } from 'vue' import useTheme from '@/hooks/useTheme' +import { themeEditorWindowControl } from '@/renderer/services' +import { isWin } from '@/renderer/shared/platform' import ColorPicker from '@/ui/ColorPicker.vue' import MilkdownEditor from './MilkdownEditor.vue' const { tempTheme, getAllCssVarsDes, getThemeByCn, addTempTheme, saveTheme, getEditingThemeFromStorage, clearEditingThemeFromStorage } = useTheme() -const isWin = window.electronAPI.platform === 'win32' // 原始主题备份 const originalThemeBackup = ref(null) @@ -39,7 +40,7 @@ function handleClose() { clearEditingThemeFromStorage() if (window.electronAPI) { - window.electronAPI.themeEditorWindowControl('close') + themeEditorWindowControl('close') } } diff --git a/src/renderer/components/TitleBar.vue b/src/renderer/components/TitleBar.vue index 35cfa67..6d01f63 100644 --- a/src/renderer/components/TitleBar.vue +++ b/src/renderer/components/TitleBar.vue @@ -1,24 +1,22 @@ diff --git a/src/renderer/components/ui/tree/context.ts b/src/renderer/components/ui/tree/context.ts index d9a851b..c49e2d0 100644 --- a/src/renderer/components/ui/tree/context.ts +++ b/src/renderer/components/ui/tree/context.ts @@ -1,7 +1,7 @@ import type { ComputedRef, Reactive } from 'vue' import type { TreeNode } from './types' import type { TransitionEffect } from '@/utils/heightTransition' -import { createContext } from '@renderer/shared/createContext' +import { createContext } from '@renderer/hooks/createContext' export interface TreeContext { expandedNodes: Reactive> diff --git a/src/renderer/context/index.ts b/src/renderer/context/index.ts index 9c03f02..29a8b51 100644 --- a/src/renderer/context/index.ts +++ b/src/renderer/context/index.ts @@ -1,5 +1,5 @@ import type { Ref } from 'vue' -import { createContext } from '@renderer/shared/createContext' +import { createContext } from '@renderer/hooks/createContext' import { defineComponent, ref } from 'vue' interface MilkupContext { diff --git a/src/renderer/shared/createContext.ts b/src/renderer/hooks/createContext.ts similarity index 100% rename from src/renderer/shared/createContext.ts rename to src/renderer/hooks/createContext.ts diff --git a/src/renderer/hooks/useContext.ts b/src/renderer/hooks/useContext.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/renderer/services/deprecated/index.ts b/src/renderer/services/deprecated/index.ts new file mode 100644 index 0000000..953d670 --- /dev/null +++ b/src/renderer/services/deprecated/index.ts @@ -0,0 +1,137 @@ +/** + * TODO: 当前文件仅用于迁移旧代码,未来会被删除 + * 未来期望将同类型的 API 迁移到各自文件夹下 + */ + +function openFile() { + return window.electronAPI.openFile() +} + +function saveFile(filePath: string | null, content: string) { + return window.electronAPI.saveFile(filePath, content) +} + +function saveFileAs(content: string) { + return window.electronAPI.saveFileAs(content) +} + +function on(channel: string, listener: (...args: any[]) => void) { + return window.electronAPI.on(channel, listener) +} + +function removeListener(channel: string, listener: (...args: any[]) => void) { + return window.electronAPI.removeListener(channel, listener) +} + +function setTitle(filePath: string | null) { + return window.electronAPI.setTitle(filePath) +} + +function changeSaveStatus(isSaved: boolean) { + return window.electronAPI.changeSaveStatus(isSaved) +} + +function windowControl(action: 'minimize' | 'maximize' | 'close') { + return window.electronAPI.windowControl(action) +} + +function closeDiscard() { + return window.electronAPI.closeDiscard() +} + +function onOpenFileAtLaunch(cb: (payload: { filePath: string, content: string }) => void) { + return window.electronAPI.onOpenFileAtLaunch(cb) +} + +function openExternal(url: string) { + return window.electronAPI.openExternal(url) +} + +function getFilePathInClipboard() { + return window.electronAPI.getFilePathInClipboard() +} + +function writeTempImage(file: File, tempPath: string) { + return window.electronAPI.writeTempImage(file as any, tempPath) +} + +function resolveImagePath(markdownFilePath: string, imagePath: string) { + return window.electronAPI.resolveImagePath(markdownFilePath, imagePath) +} + +function readFileByPath(filePath: string) { + return window.electronAPI.readFileByPath(filePath) +} + +function showOverwriteConfirm(fileName: string) { + return window.electronAPI.showOverwriteConfirm(fileName) +} + +function showCloseConfirm(fileName: string) { + return window.electronAPI.showCloseConfirm(fileName) +} + +function showOpenDialog(options: any) { + return window.electronAPI.showOpenDialog(options) +} + +function getPathForFile(file: File) { + return window.electronAPI.getPathForFile(file as any) +} + +function getSystemFonts() { + return window.electronAPI.getSystemFonts() +} + +function getDirectoryFiles(dirPath: string) { + return window.electronAPI.getDirectoryFiles(dirPath) +} + +function openThemeEditor(theme?: any) { + return window.electronAPI.openThemeEditor(theme) +} + +function themeEditorWindowControl(action: 'minimize' | 'maximize' | 'close') { + return window.electronAPI.themeEditorWindowControl(action) +} + +function saveCustomTheme(theme: any) { + return window.electronAPI.saveCustomTheme(theme) +} + +function getIsReadOnly(filePath: string) { + return window.electronAPI.getIsReadOnly(filePath) +} + +function watchFiles(filePaths: string[]) { + return window.electronAPI.watchFiles(filePaths) +} + +export { + changeSaveStatus, + closeDiscard, + getDirectoryFiles, + getFilePathInClipboard, + getIsReadOnly, + getPathForFile, + getSystemFonts, + on, + onOpenFileAtLaunch, + openExternal, + openFile, + openThemeEditor, + readFileByPath, + removeListener, + resolveImagePath, + saveCustomTheme, + saveFile, + saveFileAs, + setTitle, + showCloseConfirm, + showOpenDialog, + showOverwriteConfirm, + themeEditorWindowControl, + watchFiles, + windowControl, + writeTempImage, +} diff --git a/src/renderer/services/index.ts b/src/renderer/services/index.ts new file mode 100644 index 0000000..727f1f3 --- /dev/null +++ b/src/renderer/services/index.ts @@ -0,0 +1,4 @@ +/** + * TODO: 仅用于迁移旧代码,未来会被删除 + */ +export * from './deprecated' diff --git a/src/renderer/shared/platform.ts b/src/renderer/shared/platform.ts new file mode 100644 index 0000000..c1f1e59 --- /dev/null +++ b/src/renderer/shared/platform.ts @@ -0,0 +1,7 @@ +const platform = window.electronAPI.platform + +const isMac = platform === 'darwin' +const isWin = platform === 'win32' +const isLinux = platform === 'linux' + +export { isLinux, isMac, isWin, platform } From 809e2033e945b2ed3f264982096f6d61e9ad8b8a Mon Sep 17 00:00:00 2001 From: chanLee <2535849195@qq.com> Date: Thu, 25 Dec 2025 01:04:34 +0800 Subject: [PATCH 2/5] refactor: refactored components and file structure --- lang/index.js | 4 +- src/renderer/App.vue | 12 +- src/renderer/{components => }/ThemeEditor.vue | 2 +- src/renderer/components/TreeNode.vue | 182 ------------------ src/renderer/components/VditorEditor.vue | 84 -------- .../{ => core/dialog}/ReloadConfirmDialog.vue | 0 .../{ => core/dialog}/SaveConfirmDialog.vue | 0 .../{ => core/dialog}/UpdateConfirmDialog.vue | 0 src/renderer/components/core/dialog/index.ts | 3 + .../editor}/MarkdownSourceEditor.vue | 0 .../{ => core/editor}/MilkdownEditor.vue | 8 +- src/renderer/components/core/editor/index.ts | 2 + .../components/{ => core/layout}/About.vue | 6 +- .../{ => core/layout}/AppearancePage.vue | 0 .../{ => core/layout}/FileOptions.vue | 17 +- .../components/{ => core/layout}/FontPage.vue | 2 +- .../{ => core/layout}/ImageConfig.vue | 0 .../components/{ => core/layout}/Language.vue | 0 .../components/{ => core/layout}/MenuBar.vue | 2 +- .../{ => core/layout}/MenuDropDown.vue | 2 +- .../{ => core/layout}/OtherSetting.vue | 2 +- .../components/{ => core/layout}/Outline.vue | 0 .../core/layout/ReloadConfirmDialog.vue | 173 +++++++++++++++++ .../{ => core/layout}/SettingBase.vue | 0 .../{ => core/layout}/SpellCheckSetter.vue | 0 .../{ => core/layout}/StatusBar.vue | 2 +- .../components/{ => core/layout}/TabBar.vue | 0 .../{ => core/layout}/ThemePage.vue | 2 +- .../components/{ => core/layout}/TitleBar.vue | 6 +- .../{ => core/layout}/UploadConfig.vue | 0 .../{ => core/layout}/WorkSpace.vue | 0 src/renderer/components/core/layout/index.ts | 3 + src/renderer/theme-editor-main.ts | 2 +- 33 files changed, 217 insertions(+), 299 deletions(-) rename src/renderer/{components => }/ThemeEditor.vue (99%) delete mode 100644 src/renderer/components/TreeNode.vue delete mode 100644 src/renderer/components/VditorEditor.vue rename src/renderer/components/{ => core/dialog}/ReloadConfirmDialog.vue (100%) rename src/renderer/components/{ => core/dialog}/SaveConfirmDialog.vue (100%) rename src/renderer/components/{ => core/dialog}/UpdateConfirmDialog.vue (100%) create mode 100644 src/renderer/components/core/dialog/index.ts rename src/renderer/components/{ => core/editor}/MarkdownSourceEditor.vue (100%) rename src/renderer/components/{ => core/editor}/MilkdownEditor.vue (98%) create mode 100644 src/renderer/components/core/editor/index.ts rename src/renderer/components/{ => core/layout}/About.vue (94%) rename src/renderer/components/{ => core/layout}/AppearancePage.vue (100%) rename src/renderer/components/{ => core/layout}/FileOptions.vue (89%) rename src/renderer/components/{ => core/layout}/FontPage.vue (99%) rename src/renderer/components/{ => core/layout}/ImageConfig.vue (100%) rename src/renderer/components/{ => core/layout}/Language.vue (100%) rename src/renderer/components/{ => core/layout}/MenuBar.vue (98%) rename src/renderer/components/{ => core/layout}/MenuDropDown.vue (97%) rename src/renderer/components/{ => core/layout}/OtherSetting.vue (98%) rename src/renderer/components/{ => core/layout}/Outline.vue (100%) create mode 100644 src/renderer/components/core/layout/ReloadConfirmDialog.vue rename src/renderer/components/{ => core/layout}/SettingBase.vue (100%) rename src/renderer/components/{ => core/layout}/SpellCheckSetter.vue (100%) rename src/renderer/components/{ => core/layout}/StatusBar.vue (97%) rename src/renderer/components/{ => core/layout}/TabBar.vue (100%) rename src/renderer/components/{ => core/layout}/ThemePage.vue (99%) rename src/renderer/components/{ => core/layout}/TitleBar.vue (96%) rename src/renderer/components/{ => core/layout}/UploadConfig.vue (100%) rename src/renderer/components/{ => core/layout}/WorkSpace.vue (100%) create mode 100644 src/renderer/components/core/layout/index.ts diff --git a/lang/index.js b/lang/index.js index b7c7383..1403582 100644 --- a/lang/index.js +++ b/lang/index.js @@ -1,5 +1,5 @@ - // 导入国际化JSON文件 + // 导入国际化JSON文件(合并模式) import langJSON from './index.json' (function () { // 定义翻译函数 @@ -43,7 +43,7 @@ globalThis.$t = globalThis.$t || $t; // 将简单翻译函数挂载到globalThis对象上 globalThis.$$t = $$t; - // 定义从JSON文件中获取指定键的语言对象的方法 + // 定义从JSON文件中获取指定键的语言对象的方法(合并模式) globalThis._getJSONKey = function (key, insertJSONObj = undefined) { // 获取JSON对象 const JSONObj = insertJSONObj; diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 7346e26..3d33dd7 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -1,14 +1,9 @@ - - - - diff --git a/src/renderer/components/VditorEditor.vue b/src/renderer/components/VditorEditor.vue deleted file mode 100644 index e1cc016..0000000 --- a/src/renderer/components/VditorEditor.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - diff --git a/src/renderer/components/ReloadConfirmDialog.vue b/src/renderer/components/core/dialog/ReloadConfirmDialog.vue similarity index 100% rename from src/renderer/components/ReloadConfirmDialog.vue rename to src/renderer/components/core/dialog/ReloadConfirmDialog.vue diff --git a/src/renderer/components/SaveConfirmDialog.vue b/src/renderer/components/core/dialog/SaveConfirmDialog.vue similarity index 100% rename from src/renderer/components/SaveConfirmDialog.vue rename to src/renderer/components/core/dialog/SaveConfirmDialog.vue diff --git a/src/renderer/components/UpdateConfirmDialog.vue b/src/renderer/components/core/dialog/UpdateConfirmDialog.vue similarity index 100% rename from src/renderer/components/UpdateConfirmDialog.vue rename to src/renderer/components/core/dialog/UpdateConfirmDialog.vue diff --git a/src/renderer/components/core/dialog/index.ts b/src/renderer/components/core/dialog/index.ts new file mode 100644 index 0000000..6c3a1b3 --- /dev/null +++ b/src/renderer/components/core/dialog/index.ts @@ -0,0 +1,3 @@ +export { default as ReloadConfirmDialog } from './ReloadConfirmDialog.vue' +export { default as SaveConfirmDialog } from './SaveConfirmDialog.vue' +export { default as UpdateConfirmDialog } from './UpdateConfirmDialog.vue' diff --git a/src/renderer/components/MarkdownSourceEditor.vue b/src/renderer/components/core/editor/MarkdownSourceEditor.vue similarity index 100% rename from src/renderer/components/MarkdownSourceEditor.vue rename to src/renderer/components/core/editor/MarkdownSourceEditor.vue diff --git a/src/renderer/components/MilkdownEditor.vue b/src/renderer/components/core/editor/MilkdownEditor.vue similarity index 98% rename from src/renderer/components/MilkdownEditor.vue rename to src/renderer/components/core/editor/MilkdownEditor.vue index 804c7a1..49958b1 100644 --- a/src/renderer/components/MilkdownEditor.vue +++ b/src/renderer/components/core/editor/MilkdownEditor.vue @@ -14,7 +14,7 @@ import useTab from '@/hooks/useTab' import { uploader } from '@/plugins/customPastePlugin' import { htmlPlugin } from '@/plugins/hybridHtmlPlugin/rawHtmlPlugin' import { diagram } from '@/plugins/mermaidPlugin' -import emitter from '../events' +import emitter from '@/renderer/events' const props = defineProps<{ modelValue: string @@ -107,7 +107,11 @@ onMounted(async () => { .use(htmlPlugin) .use(diagram) .use(commonmark) - props.readOnly && crepe.setReadonly(true) + + if (props.readOnly) { + crepe.setReadonly(true) + } + await crepe.create() editor.ctx.update(uploadConfig.key, prev => ({ ...prev, uploader })) diff --git a/src/renderer/components/core/editor/index.ts b/src/renderer/components/core/editor/index.ts new file mode 100644 index 0000000..8c11bf5 --- /dev/null +++ b/src/renderer/components/core/editor/index.ts @@ -0,0 +1,2 @@ +export { default as MarkdownSourceEditor } from './MarkdownSourceEditor.vue' +export { default as MilkdownEditor } from './MilkdownEditor.vue' diff --git a/src/renderer/components/About.vue b/src/renderer/components/core/layout/About.vue similarity index 94% rename from src/renderer/components/About.vue rename to src/renderer/components/core/layout/About.vue index cf0c97d..a36544a 100644 --- a/src/renderer/components/About.vue +++ b/src/renderer/components/core/layout/About.vue @@ -2,9 +2,9 @@ import autotoast from 'autotoast.js' import { checkUpdate } from '@/api/update' import logo from '@/assets/icons/milkup.ico' -import { version } from '../../../package.json' -import emitter from '../events' -import { openExternal } from '../services' +import emitter from '@/renderer/events' +import { openExternal } from '@/renderer/services' +import { version } from '../../../../../package.json' function openByDefaultBrowser(url: string) { openExternal(url) diff --git a/src/renderer/components/AppearancePage.vue b/src/renderer/components/core/layout/AppearancePage.vue similarity index 100% rename from src/renderer/components/AppearancePage.vue rename to src/renderer/components/core/layout/AppearancePage.vue diff --git a/src/renderer/components/FileOptions.vue b/src/renderer/components/core/layout/FileOptions.vue similarity index 89% rename from src/renderer/components/FileOptions.vue rename to src/renderer/components/core/layout/FileOptions.vue index bc274be..7d934c7 100644 --- a/src/renderer/components/FileOptions.vue +++ b/src/renderer/components/core/layout/FileOptions.vue @@ -1,14 +1,19 @@ + + + + diff --git a/src/renderer/components/SettingBase.vue b/src/renderer/components/core/layout/SettingBase.vue similarity index 100% rename from src/renderer/components/SettingBase.vue rename to src/renderer/components/core/layout/SettingBase.vue diff --git a/src/renderer/components/SpellCheckSetter.vue b/src/renderer/components/core/layout/SpellCheckSetter.vue similarity index 100% rename from src/renderer/components/SpellCheckSetter.vue rename to src/renderer/components/core/layout/SpellCheckSetter.vue diff --git a/src/renderer/components/StatusBar.vue b/src/renderer/components/core/layout/StatusBar.vue similarity index 97% rename from src/renderer/components/StatusBar.vue rename to src/renderer/components/core/layout/StatusBar.vue index c91d916..dd41e0a 100644 --- a/src/renderer/components/StatusBar.vue +++ b/src/renderer/components/core/layout/StatusBar.vue @@ -2,7 +2,7 @@ import { computed, ref } from 'vue' import { toggleShowOutline } from '@/hooks/useOutline' import useSourceCode from '@/hooks/useSourceCode' -import { on as onIpc } from '../services' +import { on as onIpc } from '@/renderer/services' const props = defineProps<{ content: string diff --git a/src/renderer/components/TabBar.vue b/src/renderer/components/core/layout/TabBar.vue similarity index 100% rename from src/renderer/components/TabBar.vue rename to src/renderer/components/core/layout/TabBar.vue diff --git a/src/renderer/components/ThemePage.vue b/src/renderer/components/core/layout/ThemePage.vue similarity index 99% rename from src/renderer/components/ThemePage.vue rename to src/renderer/components/core/layout/ThemePage.vue index 56304fd..946df09 100644 --- a/src/renderer/components/ThemePage.vue +++ b/src/renderer/components/core/layout/ThemePage.vue @@ -2,7 +2,7 @@ import { onMounted } from 'vue' import useTheme from '@/hooks/useTheme' -const { themes, currentTheme, init, setTheme, addTempTheme, removeTheme, exportTheme, importTheme } = useTheme() +const { themes, currentTheme, init, setTheme, addTempTheme, removeTheme, exportTheme } = useTheme() onMounted(() => { init() diff --git a/src/renderer/components/TitleBar.vue b/src/renderer/components/core/layout/TitleBar.vue similarity index 96% rename from src/renderer/components/TitleBar.vue rename to src/renderer/components/core/layout/TitleBar.vue index 6d01f63..9601e9a 100644 --- a/src/renderer/components/TitleBar.vue +++ b/src/renderer/components/core/layout/TitleBar.vue @@ -7,14 +7,14 @@ import TabBar from './TabBar.vue' const isFullScreen = ref(false) function minimize() { - windowControl?.('minimize') + windowControl('minimize') } function toggleMaximize() { isFullScreen.value = !isFullScreen.value - windowControl?.('maximize') + windowControl('maximize') } async function close() { - windowControl?.('close') + windowControl('close') } onIpc('close', () => { close() diff --git a/src/renderer/components/UploadConfig.vue b/src/renderer/components/core/layout/UploadConfig.vue similarity index 100% rename from src/renderer/components/UploadConfig.vue rename to src/renderer/components/core/layout/UploadConfig.vue diff --git a/src/renderer/components/WorkSpace.vue b/src/renderer/components/core/layout/WorkSpace.vue similarity index 100% rename from src/renderer/components/WorkSpace.vue rename to src/renderer/components/core/layout/WorkSpace.vue diff --git a/src/renderer/components/core/layout/index.ts b/src/renderer/components/core/layout/index.ts new file mode 100644 index 0000000..e342961 --- /dev/null +++ b/src/renderer/components/core/layout/index.ts @@ -0,0 +1,3 @@ +export { default as Outline } from './Outline.vue' +export { default as StatusBar } from './StatusBar.vue' +export { default as TitleBar } from './TitleBar.vue' diff --git a/src/renderer/theme-editor-main.ts b/src/renderer/theme-editor-main.ts index a4ae47d..2a21693 100644 --- a/src/renderer/theme-editor-main.ts +++ b/src/renderer/theme-editor-main.ts @@ -3,7 +3,7 @@ import '../../lang/index.js' import { createApp, onMounted } from 'vue' import useTheme from '@/hooks/useTheme' -import ThemeEditor from './components/ThemeEditor.vue' +import ThemeEditor from './ThemeEditor.vue' import './style.less' import '@/themes/theme-main.less' From fb583af209db953ee846057f2e02a200b8ecf646 Mon Sep 17 00:00:00 2001 From: chanLee <2535849195@qq.com> Date: Fri, 26 Dec 2025 00:39:13 +0800 Subject: [PATCH 3/5] refactor: refactored events --- lang/index.json | 8 ++++++++ src/renderer/ThemeEditor.vue | 2 +- .../components/core/layout/MenuDropDown.vue | 12 ++++-------- src/renderer/components/core/layout/TabBar.vue | 18 ++++++------------ .../components/ui/color-picker/ColorPicker.vue | 15 ++++----------- .../ui/virtual-select/VirtualSelect.vue | 11 +++-------- src/renderer/theme-editor.html | 2 +- src/ui/ColorPicker.vue | 15 ++++----------- 8 files changed, 31 insertions(+), 52 deletions(-) diff --git a/lang/index.json b/lang/index.json index 4d90af6..a7dc0c5 100644 --- a/lang/index.json +++ b/lang/index.json @@ -2318,5 +2318,13 @@ "ru": "", "en": "", "fr": "" + }, + "9j0b1b6g": { + "zh-cn": "\n
\n\n# MilkUp\n\n
\n\n> 这是一段测试文字\n\n
\n\n***\n\n
\n\n* item1\n* item2\n* item3\n\n代码:\n\n```JavaScript\nconst text = \"Hello Word!\"\nconsole.log(text)\n```\n\n
\n\n| 1 | 2 | 3 |\n| :- | :- | :- |\n| 1 | 2 | 3 |\n| 1 | 2 | 3 |\n\n\n", + "ja": "", + "ko": "", + "ru": "", + "en": "", + "fr": "" } } diff --git a/src/renderer/ThemeEditor.vue b/src/renderer/ThemeEditor.vue index 4a949a6..a9425b1 100644 --- a/src/renderer/ThemeEditor.vue +++ b/src/renderer/ThemeEditor.vue @@ -1,12 +1,12 @@