diff --git a/IFLOW.md b/IFLOW.md new file mode 100644 index 0000000..0cf42ad --- /dev/null +++ b/IFLOW.md @@ -0,0 +1,94 @@ +# IFLOW 上下文文档 + +## 项目概述 + +这是一个基于 **Vite + Vue 3 + Electron** 的桌面应用程序开发模板,名为 **Vutron**。它提供了一个快速搭建跨平台桌面应用的环境,内置了热重载、TypeScript 支持、多语言支持、主题切换(暗黑/明亮)、全局状态管理(Pinia)等功能。 + +项目使用 `pnpm` 作为包管理器,主要技术栈包括: + +- 前端框架:Vue 3 (Composition API) +- 构建工具:Vite +- 桌面应用框架:Electron +- UI 库:Vuetify +- 状态管理:Pinia +- 国际化:vue-i18n +- 自动更新:electron-updater + +## 项目结构 + +``` +src/ +├── main/ # Electron 主进程代码 +│ ├── AutoUpdater.ts # 自动更新功能 +│ ├── IPCs.ts # IPC 通信处理 +│ ├── MainRunner.ts # 主窗口和错误窗口创建 +│ ├── index.dev.ts # 开发环境入口 +│ ├── index.ts # 生产环境入口 +│ ├── tray.ts # 系统托盘功能 +│ └── utils/ +│ └── Constants.ts # 常量定义 +├── preload/ # Electron 预加载脚本 +├── public/ # 静态资源 +└── renderer/ # 渲染进程(Vue 应用) + ├── App.vue # 根组件 + ├── main.ts # Vue 应用入口 + ├── components/ # 公共组件 + ├── screens/ # 页面组件 + ├── plugins/ # 插件配置(vuetify, i18n, pinia) + ├── router/ # 路由配置 + ├── store/ # Pinia 状态管理 + ├── locales/ # 语言包 + └── utils/ # 工具函数 + +buildAssets/ # 构建相关资源 +docs/ # 项目文档 +tests/ # 测试文件 +``` + +## 构建和运行 + +### 开发环境 + +1. 安装依赖: + + ```bash + pnpm install + ``` + +2. 启动开发服务器: + + ```bash + pnpm dev + ``` + + 这将启动 Vite 开发服务器,并在 Electron 中打开应用。 + +3. 构建项目: + + ```bash + pnpm build + ``` + + 构建应用到 `dist/` 目录,并使用 `electron-builder` 打包成可执行文件。 + +4. 运行测试: + ```bash + pnpm test + ``` + 运行 Playwright 自动化测试。 + +### 生产环境 + +构建后的应用位于 `release/` 目录下,根据不同平台生成对应的安装包。 + +## 开发约定 + +- 使用 TypeScript 进行开发,类型检查通过 `vue-tsc` 完成。 +- 代码风格遵循 ESLint 和 Prettier 规范。 +- 使用 Pinia 进行全局状态管理。 +- 国际化支持通过 vue-i18n 实现。 +- 主进程与渲染进程通过 IPC 进行通信。 +- 日志记录使用 `electron-log` 模块。 +- 自动更新功能通过 `electron-updater` 实现。 + +如果有修改代码,任务结束前需要先运行 `pnpm typecheck` 检查类型错误。不要直接运行 `pnpm dev` 或 `pnpm build`。 diff --git a/QWEN.md b/QWEN.md index 37555d8..ca54668 100644 --- a/QWEN.md +++ b/QWEN.md @@ -229,6 +229,6 @@ Building creates different distribution formats based on the platform: Artifacts are named with the format `${productName} ${version}_${arch}.${ext}` and stored in the `release/${version}` directory. -run `pnpm typecheck` before finish the task if need +run `pnpm typecheck` before finish the task if need,don't run `pnpm dev` or `pnpm build`. Always respond in 中文 diff --git a/package.json b/package.json index 2417561..9b9af49 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ "standard-version": "^9.5.0", "typescript": "^5.9.3", "typescript-eslint": "^8.46.1", + "unplugin-auto-import": "^20.2.0", "vite": "^7.1.10", "vite-plugin-electron": "^0.29.0", "vite-plugin-electron-renderer": "^0.14.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 394e0bc..d11cd02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,6 +117,9 @@ importers: typescript-eslint: specifier: ^8.46.1 version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + unplugin-auto-import: + specifier: ^20.2.0 + version: 20.2.0(@vueuse/core@14.0.0(vue@3.5.22(typescript@5.9.3))) vite: specifier: ^7.1.10 version: 7.1.10(@types/node@22.15.15)(jiti@2.6.1) @@ -1340,6 +1343,12 @@ packages: resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} engines: {'0': node >= 6.0} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + config-file-ts@0.2.8-rc1: resolution: {integrity: sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==} @@ -1697,6 +1706,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + eslint-compat-utils@0.5.1: resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} engines: {node: '>=12'} @@ -1824,6 +1837,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1831,6 +1847,9 @@ packages: exponential-backoff@3.1.2: resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -2377,6 +2396,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -2453,6 +2475,10 @@ packages: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} + local-pkg@1.1.2: + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} + engines: {node: '>=14'} + locate-path@2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} engines: {node: '>=4'} @@ -2641,6 +2667,9 @@ packages: engines: {node: '>=10'} hasBin: true + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + modify-values@1.0.1: resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} engines: {node: '>=0.10.0'} @@ -2833,6 +2862,9 @@ packages: resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} engines: {node: '>=4'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pe-library@0.4.1: resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==} engines: {node: '>=12', npm: '>=6'} @@ -2871,6 +2903,12 @@ packages: typescript: optional: true + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + playwright-core@1.56.1: resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} engines: {node: '>=18'} @@ -2957,6 +2995,9 @@ packages: resolution: {integrity: sha512-+37VW4m47xbKfh65Wn6Xr9fnYF3QvtRSgRCSh2gtEyXlUI1SzFZtUFdRKA5+7sJkZbMEVVwhy5SOYkWA3N7gLw==} engines: {node: '>=18.0.0'} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3102,6 +3143,9 @@ packages: sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} @@ -3291,6 +3335,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + sumchecker@3.0.1: resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} engines: {node: '>= 8.0'} @@ -3436,6 +3483,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} @@ -3448,6 +3498,10 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + unimport@5.5.0: + resolution: {integrity: sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==} + engines: {node: '>=18.12.0'} + unique-filename@2.0.1: resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -3464,6 +3518,26 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + unplugin-auto-import@20.2.0: + resolution: {integrity: sha512-vfBI/SvD9hJqYNinipVOAj5n8dS8DJXFlCKFR5iLDp2SaQwsfdnfLXgZ+34Kd3YY3YEY9omk8XQg0bwos3Q8ug==} + engines: {node: '>=14'} + peerDependencies: + '@nuxt/kit': ^4.0.0 + '@vueuse/core': '*' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@vueuse/core': + optional: true + + unplugin-utils@0.3.1: + resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==} + engines: {node: '>=20.19.0'} + + unplugin@2.3.10: + resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} + engines: {node: '>=18.12.0'} + unzip-crx-3@0.2.0: resolution: {integrity: sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==} @@ -3610,6 +3684,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -5094,6 +5171,10 @@ snapshots: readable-stream: 3.6.2 typedarray: 0.0.6 + confbox@0.1.8: {} + + confbox@0.2.2: {} + config-file-ts@0.2.8-rc1: dependencies: glob: 10.4.5 @@ -5664,6 +5745,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-compat-utils@0.5.1(eslint@9.37.0(jiti@2.6.1)): dependencies: eslint: 9.37.0(jiti@2.6.1) @@ -5834,10 +5917,16 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + esutils@2.0.3: {} exponential-backoff@3.1.2: {} + exsolve@1.0.7: {} + extract-zip@2.0.1: dependencies: debug: 4.4.0 @@ -6424,6 +6513,8 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -6495,6 +6586,12 @@ snapshots: pify: 3.0.0 strip-bom: 3.0.0 + local-pkg@1.1.2: + dependencies: + mlly: 1.8.0 + pkg-types: 2.3.0 + quansync: 0.2.11 + locate-path@2.0.0: dependencies: p-locate: 2.0.0 @@ -6685,6 +6782,13 @@ snapshots: mkdirp@1.0.4: {} + mlly@1.8.0: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + modify-values@1.0.1: {} ms@2.1.3: {} @@ -6883,6 +6987,8 @@ snapshots: dependencies: pify: 3.0.0 + pathe@2.0.3: {} + pe-library@0.4.1: {} pend@1.2.0: {} @@ -6906,6 +7012,18 @@ snapshots: optionalDependencies: typescript: 5.9.3 + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.0 + pathe: 2.0.3 + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + playwright-core@1.56.1: {} playwright@1.56.1: @@ -6970,6 +7088,8 @@ snapshots: qsu@1.10.2: {} + quansync@0.2.11: {} + queue-microtask@1.2.3: {} quick-lru@4.0.1: {} @@ -7163,6 +7283,8 @@ snapshots: sax@1.4.1: {} + scule@1.3.0: {} + semver-compare@1.0.0: optional: true @@ -7389,6 +7511,10 @@ snapshots: strip-json-comments@3.1.1: {} + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + sumchecker@3.0.1: dependencies: debug: 4.4.0 @@ -7551,6 +7677,8 @@ snapshots: typescript@5.9.3: {} + ufo@1.6.1: {} + uglify-js@3.19.3: optional: true @@ -7563,6 +7691,23 @@ snapshots: undici-types@6.21.0: {} + unimport@5.5.0: + dependencies: + acorn: 8.15.0 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + local-pkg: 1.1.2 + magic-string: 0.30.19 + mlly: 1.8.0 + pathe: 2.0.3 + picomatch: 4.0.3 + pkg-types: 2.3.0 + scule: 1.3.0 + strip-literal: 3.1.0 + tinyglobby: 0.2.15 + unplugin: 2.3.10 + unplugin-utils: 0.3.1 + unique-filename@2.0.1: dependencies: unique-slug: 3.0.0 @@ -7575,6 +7720,29 @@ snapshots: universalify@2.0.1: {} + unplugin-auto-import@20.2.0(@vueuse/core@14.0.0(vue@3.5.22(typescript@5.9.3))): + dependencies: + local-pkg: 1.1.2 + magic-string: 0.30.19 + picomatch: 4.0.3 + unimport: 5.5.0 + unplugin: 2.3.10 + unplugin-utils: 0.3.1 + optionalDependencies: + '@vueuse/core': 14.0.0(vue@3.5.22(typescript@5.9.3)) + + unplugin-utils@0.3.1: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.3 + + unplugin@2.3.10: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.15.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + unzip-crx-3@0.2.0: dependencies: jszip: 3.10.1 @@ -7700,6 +7868,8 @@ snapshots: dependencies: defaults: 1.0.4 + webpack-virtual-modules@0.6.2: {} + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/src/main/IPCs.ts b/src/main/IPCs.ts index 7cc84c3..6ae2406 100644 --- a/src/main/IPCs.ts +++ b/src/main/IPCs.ts @@ -2,13 +2,16 @@ import { ipcMain, shell, IpcMainEvent, dialog } from 'electron' import Constants from './utils/Constants' import { join, dirname } from 'path' import { - writeFileSync, - readFileSync, - existsSync, - ensureDirSync, - copyFileSync + writeFile, + readFile, + pathExists, + ensureDir, + copy, + readdir, + stat, } from 'fs-extra' import { autoUpdater } from 'electron-updater' +import { Template, TreeNode } from '../types' /* * IPC Communications @@ -75,7 +78,7 @@ export default class IPCs { const filePath = dialogResult.filePaths[0]; const fs = require('fs'); const isDirectory = fs.statSync(filePath).isDirectory(); - + return { canceled: false, filePath: filePath, @@ -86,28 +89,26 @@ export default class IPCs { return { canceled: true } }) - + // Find all files in a folder recursively ipcMain.handle( 'msgFindFilesInFolder', async (event: IpcMainEvent, folderPath: string) => { try { - const { readdirSync, statSync } = require('fs') - const { join } = require('path') const foundFiles: string[] = [] - const searchFiles = (dir: string) => { - const items = readdirSync(dir) + const searchFiles = async (dir: string) => { + const items = await readdir(dir) for (const item of items) { const fullPath = join(dir, item) - const stats = statSync(fullPath) + const stats = await stat(fullPath) if (stats.isDirectory()) { // Recursively search in subdirectories - searchFiles(fullPath) + await searchFiles(fullPath) } else if (stats.isFile()) { // Add file to the list foundFiles.push(fullPath) @@ -115,7 +116,7 @@ export default class IPCs { } } - searchFiles(folderPath) + await searchFiles(folderPath) return foundFiles } catch (error) { @@ -134,10 +135,10 @@ export default class IPCs { const filePath = join(userDataPath, fileName) // Ensure directory exists - ensureDirSync(userDataPath) + await ensureDir(userDataPath) - if (existsSync(filePath)) { - const data = readFileSync(filePath, 'utf-8') + if (await pathExists(filePath)) { + const data = await readFile(filePath, 'utf-8') return data } return null @@ -157,9 +158,9 @@ export default class IPCs { const filePath = join(userDataPath, fileName) // Ensure directory exists - ensureDirSync(userDataPath) + await ensureDir(userDataPath) - writeFileSync(filePath, data, 'utf-8') + await writeFile(filePath, data, 'utf-8') return true } catch (error) { console.error('Error saving data:', error) @@ -182,9 +183,9 @@ export default class IPCs { const userDataPath = join(Constants.APP_DATA_PATH, 'data') const filePath = join(userDataPath, 'templates.json') - let templates = [] - if (existsSync(filePath)) { - const templatesData = readFileSync(filePath, 'utf-8') + let templates: Template[] = [] + if (await pathExists(filePath)) { + const templatesData = await readFile(filePath, 'utf-8') templates = JSON.parse(templatesData) } @@ -200,14 +201,17 @@ export default class IPCs { // Create base directory const baseDirPath = join(targetPath, baseDirName) - ensureDirSync(baseDirPath) + await ensureDir(baseDirPath) // Helper function to traverse the tree and collect all nodes - const collectAllNodes = (nodes, allNodes = []) => { + const collectAllNodes = (nodes: TreeNode[], allNodes: TreeNode[] = [], parent?: TreeNode) => { for (const node of nodes) { - allNodes.push(node); + allNodes.push({ + ...node, + path: join(parent?.path || '', node.path) + }); if (node.children) { - collectAllNodes(node.children, allNodes); + collectAllNodes(node.children, allNodes, node); } } return allNodes; @@ -222,18 +226,22 @@ export default class IPCs { const nodePath = join(baseDirPath, node.path); if (node.type === 'directory') { - ensureDirSync(nodePath); + await ensureDir(nodePath); } else if (node.type === 'file') { const fileDir = dirname(nodePath); - ensureDirSync(fileDir); + await ensureDir(fileDir); - if (node.sourceFilePath) { - // Copy from source file - const sourceContent = readFileSync(node.sourceFilePath); - writeFileSync(nodePath, sourceContent); - } else if (node.content) { + if (node.sourceType === 'file') { + await copy(node.sourcePath, nodePath); + } else { // Write content directly - writeFileSync(nodePath, node.content, 'utf-8'); + const files = await readdir(node.sourcePath, { withFileTypes: true }) + for (const file of files) { + if (file.isFile() && file.name.startsWith(baseDirName)) { + const filePath = join(node.sourcePath, file.name); + await copy(filePath, nodePath); + } + } } } } diff --git a/src/renderer/App.vue b/src/renderer/App.vue index cd1d162..75ab147 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -2,6 +2,7 @@ import { DefaultLayout } from '@/renderer/components/layout' import GlobalDialog from '@/renderer/components/GlobalDialog.vue' import UpdateDialog from '@/renderer/components/UpdateDialog.vue' +import GlobalSnackbar from '@/renderer/components/GlobalSnackbar.vue' import { onMounted } from 'vue' import { useAppStore } from '@/renderer/store/app' @@ -75,6 +76,7 @@ const checkForUpdates = async () => { +