From b6964f5fbeda7ac6b531653bcecf5bfd37f39cc2 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Thu, 27 Mar 2025 00:55:24 +0800 Subject: [PATCH] =?UTF-8?q?style:=20=E7=BB=9F=E4=B8=80=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BC=A9=E8=BF=9B=E4=B8=BA2=E4=B8=AA=E7=A9=BA=E6=A0=BC?= =?UTF-8?q?=EF=BC=8C=E6=8F=90=E5=8D=87=E4=BB=A3=E7=A0=81=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此次提交主要将代码中的缩进统一调整为2个空格,替换原有的4个空格或Tab缩进。这一改动不影响代码功能,但有助于提升代码的一致性和可读性。 --- .prettierrc | 60 +- .vscode/extensions.json | 2 +- .vscode/launch.json | 74 +- .vscode/settings.json | 32 +- config/index.ts | 54 +- electron-builder.yml | 62 +- electron.vite.config.ts | 180 +-- package.json | 152 +-- pnpm-workspace.yaml | 2 +- resources/fuck.html | 17 +- src/common/_ioc.main.ts | 8 +- src/common/event/PlatForm/index.ts | 36 +- src/common/event/PlatForm/main/command.ts | 124 +-- src/common/event/Tabs/index.ts | 100 +- src/common/event/Tabs/main/command.ts | 88 +- src/common/event/update/index.ts | 6 +- src/common/event/update/main/command.ts | 8 +- src/common/event/update/main/index.ts | 2 +- src/common/lib/_Base.ts | 16 +- src/common/lib/abstract.ts | 78 +- src/common/lib/browser.ts | 44 +- src/common/lib/electron.ts | 26 +- src/common/usePlatform.ts | 8 +- src/main/App copy.ts | 278 ++--- src/main/App.ts | 120 +- src/main/_ioc.ts | 6 +- src/main/_iocClass.ts | 24 +- src/main/base/base.ts | 6 +- src/main/controller/BasicService.ts | 30 +- src/main/controller/TabsService.ts | 54 +- src/main/controller/_ioc.ts | 6 +- src/main/index.ts | 90 +- src/main/modules/_ioc.ts | 38 +- src/main/modules/api/index.ts | 152 +-- src/main/modules/api/readme.md | 1 - src/main/modules/api/test.ts | 1 - src/main/modules/commands/index.ts | 166 +-- src/main/modules/db/custom.ts | 44 +- src/main/modules/db/index.ts | 116 +- src/main/modules/setting/index.ts | 356 +++--- src/main/modules/tabs/Constant.ts | 8 +- src/main/modules/tabs/Tab.ts | 484 ++++---- src/main/modules/tabs/index.ts | 204 ++-- src/main/modules/updater/hot/index.ts | 126 +-- src/main/modules/updater/index.ts | 218 ++-- src/main/modules/window-manager/index.ts | 612 +++++----- src/main/modules/window-manager/windowsMap.ts | 216 ++-- src/main/modules/zephyr/index.ts | 734 ++++++------ src/main/utils/index.ts | 28 +- src/preload/call.ts | 106 +- src/preload/index.ts | 84 +- src/renderer/about.html | 48 +- src/renderer/auto-imports.d.ts | 1177 ++++++++++---------- src/renderer/index.html | 27 +- src/renderer/src/App.vue | 32 +- src/renderer/src/assets/libs/scrollbot.ts | 427 ++++--- src/renderer/src/assets/style/_common.scss | 38 +- src/renderer/src/bridge/PopupMenu.ts | 98 +- src/renderer/src/components/AdjustLine.vue | 758 ++++++------- .../CodeEditor/PlaceholderContentWidget.ts | 86 +- src/renderer/src/components/CodeEditor/a.d.ts | 2 +- .../src/components/CodeEditor/code-editor.vue | 441 ++++---- src/renderer/src/components/CodeEditor/readme.md | 2 +- src/renderer/src/components/CodeEditor/utils.ts | 58 +- src/renderer/src/components/NavBar.vue | 138 +-- src/renderer/src/components/Versions.vue | 14 +- src/renderer/src/composables/useTest.ts | 2 +- src/renderer/src/env.d.ts | 8 +- src/renderer/src/i18n/index.ts | 14 +- src/renderer/src/layouts/default.vue | 12 +- src/renderer/src/main.ts | 8 +- src/renderer/src/pages/[...all].vue | 2 +- src/renderer/src/pages/_ui/App.vue | 14 +- src/renderer/src/pages/_ui/Browser.vue | 404 +++---- src/renderer/src/pages/about/index.vue | 58 +- src/renderer/src/pages/browser.vue | 218 ++-- src/renderer/src/pages/index.vue | 42 +- src/renderer/src/router/index.ts | 6 +- src/renderer/src/shims.d.ts | 2 +- src/renderer/src/utils/index.ts | 8 +- src/types/global.d.ts | 25 +- src/types/popup-menu.ts | 10 +- uno.config.ts | 136 +-- vue-macros.config.ts | 2 +- 推荐.md | 4 - 85 files changed, 4908 insertions(+), 4900 deletions(-) diff --git a/.prettierrc b/.prettierrc index ed0ee55..3d6170c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,32 +1,32 @@ { - "tabWidth": 2, - "useTabs": false, - "semi": false, - "singleQuote": false, - "trailingComma": "all", - "bracketSpacing": true, - "arrowParens": "avoid", - "printWidth": 140, - "htmlWhitespaceSensitivity": "ignore", - "proseWrap": "preserve", - "endOfLine": "auto", - "vueIndentScriptAndStyle": true, - "embeddedLanguageFormatting": "auto", - "jsxSingleQuote": false, - "jsxBracketSameLine": false, - "quoteProps": "as-needed", - "overrides": [ - { - "files": "*.json", - "options": { - "printWidth": 80 - } - }, - { - "files": ["*.vue", "*.tsx"], - "options": { - "singleAttributePerLine": false - } - } - ] + "tabWidth": 2, + "useTabs": false, + "semi": false, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "avoid", + "printWidth": 140, + "htmlWhitespaceSensitivity": "ignore", + "proseWrap": "preserve", + "endOfLine": "auto", + "vueIndentScriptAndStyle": true, + "embeddedLanguageFormatting": "auto", + "jsxSingleQuote": false, + "jsxBracketSameLine": false, + "quoteProps": "as-needed", + "overrides": [ + { + "files": "*.json", + "options": { + "printWidth": 80 + } + }, + { + "files": ["*.vue", "*.tsx"], + "options": { + "singleAttributePerLine": false + } + } + ] } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 82d8334..b669e89 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["dbaeumer.vscode-eslint", "lokalise.i18n-ally"] + "recommendations": ["dbaeumer.vscode-eslint", "lokalise.i18n-ally"] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 344cf45..0b6b9a6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,39 +1,39 @@ { - "version": "0.2.0", - "configurations": [ - { - "name": "Debug Main Process", - "type": "node", - "request": "launch", - "cwd": "${workspaceRoot}", - "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", - "windows": { - "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" - }, - "runtimeArgs": ["--sourcemap"], - "env": { - "REMOTE_DEBUGGING_PORT": "9222" - } - }, - { - "name": "Debug Renderer Process", - "port": 9222, - "request": "attach", - "type": "chrome", - "webRoot": "${workspaceFolder}/src/renderer", - "timeout": 60000, - "presentation": { - "hidden": true - } - } - ], - "compounds": [ - { - "name": "Debug All", - "configurations": ["Debug Main Process", "Debug Renderer Process"], - "presentation": { - "order": 1 - } - } - ] + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Main Process", + "type": "node", + "request": "launch", + "cwd": "${workspaceRoot}", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", + "windows": { + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" + }, + "runtimeArgs": ["--sourcemap"], + "env": { + "REMOTE_DEBUGGING_PORT": "9222" + } + }, + { + "name": "Debug Renderer Process", + "port": 9222, + "request": "attach", + "type": "chrome", + "webRoot": "${workspaceFolder}/src/renderer", + "timeout": 60000, + "presentation": { + "hidden": true + } + } + ], + "compounds": [ + { + "name": "Debug All", + "configurations": ["Debug Main Process", "Debug Renderer Process"], + "presentation": { + "order": 1 + } + } + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 4c61b65..d59e66d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,18 +1,18 @@ { - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "i18n-ally.localesPaths": ["packages/locales/languages"], - "i18n-ally.sourceLanguage": "zh", - "i18n-ally.displayLanguage": "zh", - "i18n-ally.keystyle": "nested", - "i18n-ally.extract.autoDetect": true, - "i18n-ally.enabledFrameworks": ["vue"], - "i18n-ally.enabledParsers": ["json"] + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "i18n-ally.localesPaths": ["packages/locales/languages"], + "i18n-ally.sourceLanguage": "zh", + "i18n-ally.displayLanguage": "zh", + "i18n-ally.keystyle": "nested", + "i18n-ally.extract.autoDetect": true, + "i18n-ally.enabledFrameworks": ["vue"], + "i18n-ally.enabledParsers": ["json"] } diff --git a/config/index.ts b/config/index.ts index aa7e86b..e23e906 100644 --- a/config/index.ts +++ b/config/index.ts @@ -7,38 +7,38 @@ type LogoType = "logo" | "bg" // 配置接口定义 interface IDefaultConfig { - language: LanguageType - "common.theme": ThemeType - "desktop:wallpaper": string - "update.repo"?: string - "update.owner"?: string - "update.allowDowngrade": boolean - "update.allowPrerelease": boolean - "editor.bg": string - "editor.logoType": LogoType - "editor.fontFamily": string - storagePath: string + language: LanguageType + "common.theme": ThemeType + "desktop:wallpaper": string + "update.repo"?: string + "update.owner"?: string + "update.allowDowngrade": boolean + "update.allowPrerelease": boolean + "editor.bg": string + "editor.logoType": LogoType + "editor.fontFamily": string + storagePath: string } interface IConfig { - app_title: string - default_config: IDefaultConfig + app_title: string + default_config: IDefaultConfig } // 默认配置导出 export default { - app_title: "zephyr", // 和风 - default_config: { - storagePath: "$storagePath$", - language: "zh", - "common.theme": "auto", - "desktop:wallpaper": "", - "editor.bg": "", - "editor.logoType": "logo", - "editor.fontFamily": "Cascadia Mono, Consolas, 'Courier New', monospace", - "update.repo": "wood-desktop", - "update.owner": "npmrun", - "update.allowDowngrade": false, - "update.allowPrerelease": false, - }, + app_title: "zephyr", // 和风 + default_config: { + storagePath: "$storagePath$", + language: "zh", + "common.theme": "auto", + "desktop:wallpaper": "", + "editor.bg": "", + "editor.logoType": "logo", + "editor.fontFamily": "Cascadia Mono, Consolas, 'Courier New', monospace", + "update.repo": "wood-desktop", + "update.owner": "npmrun", + "update.allowDowngrade": false, + "update.allowPrerelease": false, + }, } as const satisfies IConfig diff --git a/electron-builder.yml b/electron-builder.yml index 0ccc997..1c7aff4 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -1,45 +1,45 @@ appId: com.zephyr.app productName: zephyr directories: - buildResources: build + buildResources: build files: - - "!**/.vscode/*" - - "!src/*" - - "!electron.vite.config.{js,ts,mjs,cjs}" - - "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}" - - "!{.env,.env.*,.npmrc,pnpm-lock.yaml}" - - "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}" + - "!**/.vscode/*" + - "!src/*" + - "!electron.vite.config.{js,ts,mjs,cjs}" + - "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}" + - "!{.env,.env.*,.npmrc,pnpm-lock.yaml}" + - "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}" asarUnpack: - - resources/** + - resources/** win: - executableName: zephyr + executableName: zephyr nsis: - artifactName: ${name}-${version}-setup.${ext} - shortcutName: ${productName} - uninstallDisplayName: ${productName} - createDesktopShortcut: always + artifactName: ${name}-${version}-setup.${ext} + shortcutName: ${productName} + uninstallDisplayName: ${productName} + createDesktopShortcut: always mac: - entitlementsInherit: build/entitlements.mac.plist - extendInfo: - - NSCameraUsageDescription: Application requests access to the device's camera. - - NSMicrophoneUsageDescription: Application requests access to the device's microphone. - - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. - - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. - notarize: false + entitlementsInherit: build/entitlements.mac.plist + extendInfo: + - NSCameraUsageDescription: Application requests access to the device's camera. + - NSMicrophoneUsageDescription: Application requests access to the device's microphone. + - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. + - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. + notarize: false dmg: - artifactName: ${name}-${version}.${ext} + artifactName: ${name}-${version}.${ext} linux: - target: - - AppImage - - snap - - deb - maintainer: electronjs.org - category: Utility + target: + - AppImage + - snap + - deb + maintainer: electronjs.org + category: Utility appImage: - artifactName: ${name}-${version}.${ext} + artifactName: ${name}-${version}.${ext} npmRebuild: false publish: - provider: generic - url: https://example.com/auto-updates + provider: generic + url: https://example.com/auto-updates electronDownload: - mirror: https://npmmirror.com/mirrors/electron/ + mirror: https://npmmirror.com/mirrors/electron/ diff --git a/electron.vite.config.ts b/electron.vite.config.ts index feaba98..319bacd 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -13,100 +13,100 @@ import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite" import monacoEditorPlugin from "vite-plugin-monaco-editor" export default defineConfig({ - main: { - resolve: { - alias: { - config: resolve("config"), - main: resolve("src/main"), - common: resolve("src/common"), - "@res": resolve("resources"), - }, - }, - plugins: [externalizeDepsPlugin()], + main: { + resolve: { + alias: { + config: resolve("config"), + main: resolve("src/main"), + common: resolve("src/common"), + "@res": resolve("resources"), + }, }, - preload: { - plugins: [externalizeDepsPlugin()], + plugins: [externalizeDepsPlugin()], + }, + preload: { + plugins: [externalizeDepsPlugin()], + }, + renderer: { + root: resolve(__dirname, "./src/renderer"), + resolve: { + alias: { + config: resolve("config"), + common: resolve("src/common"), + "@": resolve("src/renderer/src"), + "@res": resolve("resources"), + }, }, - renderer: { - root: resolve(__dirname, "./src/renderer"), - resolve: { - alias: { - config: resolve("config"), - common: resolve("src/common"), - "@": resolve("src/renderer/src"), - "@res": resolve("resources"), - }, + css: { + preprocessorOptions: { + scss: { + additionalData: `@use "@/assets/style/global" as *;\n`, + api: "modern-compiler", }, - css: { - preprocessorOptions: { - scss: { - additionalData: `@use "@/assets/style/global" as *;\n`, - api: "modern-compiler", - }, - }, + }, + }, + build: { + rollupOptions: { + input: { + main: resolve(__dirname, "./src/renderer/index.html"), + about: resolve(__dirname, "./src/renderer/about.html"), }, - build: { - rollupOptions: { - input: { - main: resolve(__dirname, "./src/renderer/index.html"), - about: resolve(__dirname, "./src/renderer/about.html"), - }, - }, + }, + }, + plugins: [ + UnoCSS(), + VueMacros({ + plugins: { + vue: vue(), + vueJsx: vueJsx(), + vueRouter: VueRouter({ + root: resolve(__dirname, "src/renderer"), + // https://github.com/posva/unplugin-vue-router + extensions: [".vue", ".setup.tsx"], + exclude: ["**/_ui"], + }), }, - plugins: [ - UnoCSS(), - VueMacros({ - plugins: { - vue: vue(), - vueJsx: vueJsx(), - vueRouter: VueRouter({ - root: resolve(__dirname, "src/renderer"), - // https://github.com/posva/unplugin-vue-router - extensions: [".vue", ".setup.tsx"], - exclude: ["**/_ui"], - }), - }, - }), - VueI18nPlugin({ - compositionOnly: false, - include: resolve(__dirname, "packages/locales/languages/**"), - }), - Layouts({ - layoutsDirs: "src/layouts", - pagesDirs: "src/pages", - defaultLayout: "default", - extensions: ["vue", "setup.tsx"], - exclude: ["**/_ui"], - }), - // https://github.com/antfu/unplugin-auto-import - AutoImport({ - imports: [ - "vue", - "@vueuse/core", - VueRouterAutoImports, - { - // add any other imports you were relying on - "vue-router/auto": ["useLink"], - }, - "vue-i18n", - ], - dts: true, - dirs: ["src/composables"], - vueTemplate: true, - }), - // https://github.com/antfu/vite-plugin-components - Components({ - dts: true, - dirs: ["src/components"], - }), - // https://wf0.github.io/example/plugins/Formatter.html - // @ts-ignore ... - monacoEditorPlugin.default({ - publicPath: "monacoeditorwork", - customDistPath() { - return resolve(__dirname, "out/renderer/monacoeditorwork") - }, - }), + }), + VueI18nPlugin({ + compositionOnly: false, + include: resolve(__dirname, "packages/locales/languages/**"), + }), + Layouts({ + layoutsDirs: "src/layouts", + pagesDirs: "src/pages", + defaultLayout: "default", + extensions: ["vue", "setup.tsx"], + exclude: ["**/_ui"], + }), + // https://github.com/antfu/unplugin-auto-import + AutoImport({ + imports: [ + "vue", + "@vueuse/core", + VueRouterAutoImports, + { + // add any other imports you were relying on + "vue-router/auto": ["useLink"], + }, + "vue-i18n", ], - }, + dts: true, + dirs: ["src/composables"], + vueTemplate: true, + }), + // https://github.com/antfu/vite-plugin-components + Components({ + dts: true, + dirs: ["src/components"], + }), + // https://wf0.github.io/example/plugins/Formatter.html + // @ts-ignore ... + monacoEditorPlugin.default({ + publicPath: "monacoeditorwork", + customDistPath() { + return resolve(__dirname, "out/renderer/monacoeditorwork") + }, + }), + ], + }, }) diff --git a/package.json b/package.json index d1548d0..aaa00a7 100644 --- a/package.json +++ b/package.json @@ -1,78 +1,78 @@ { - "name": "zephyr", - "type": "module", - "private": true, - "version": "0.0.1", - "description": "An Electron application with Vue and TypeScript", - "main": "./out/main/index.js", - "author": "example.com", - "homepage": "https://electron-vite.org", - "scripts": { - "runInstall": "node node_modules/electron/install.js", - "format": "prettier --write .", - "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix", - "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", - "typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false", - "typecheck": "npm run typecheck:node && npm run typecheck:web", - "start": "electron-vite preview", - "dev": "chcp 65001 && set DEBUG=app:*&& electron-vite dev", - "dev:watch": "chcp 65001 & set DEBUG=app:*& electron-vite dev --watch", - "build": "npm run typecheck && electron-vite build", - "postinstall": "electron-builder install-app-deps", - "build:unpack": "npm run build && electron-builder --dir", - "build:win": "npm run build && electron-builder --win", - "build:mac": "npm run build && electron-builder --mac", - "build:linux": "npm run build && electron-builder --linux" - }, - "dependencies": { - "@electron-toolkit/preload": "^3.0.1", - "@electron-toolkit/utils": "^3.0.0", - "electron-updater": "^6.3.9", - "inversify": "^6.2.2", - "lowdb": "^7.0.1", - "reflect-metadata": "^0.2.2" - }, - "devDependencies": { - "@electron-toolkit/eslint-config": "^1.0.2", - "@electron-toolkit/eslint-config-ts": "^2.0.0", - "@electron-toolkit/tsconfig": "^1.0.1", - "@intlify/unplugin-vue-i18n": "^6.0.3", - "@rushstack/eslint-patch": "^1.10.5", - "@types/debug": "^4.1.12", - "@types/node": "^20.17.19", - "@unocss/preset-rem-to-px": "^0.64.1", - "@unocss/reset": "^0.64.1", - "@vitejs/plugin-vue": "^5.2.1", - "@vitejs/plugin-vue-jsx": "^4.1.1", - "@vue/eslint-config-prettier": "^9.0.0", - "@vue/eslint-config-typescript": "^13.0.0", - "@vueuse/core": "^12.7.0", - "debug": "^4.4.0", - "electron": "^31.7.7", - "electron-builder": "^24.13.3", - "electron-vite": "^2.3.0", - "eslint": "^8.57.1", - "eslint-plugin-vue": "^9.32.0", - "extract-zip": "^2.0.1", - "locales": "workspace:*", - "lodash-es": "^4.17.21", - "monaco-editor": "^0.52.2", - "prettier": "^3.5.1", - "rotating-file-stream": "^3.2.6", - "sass": "^1.85.0", - "simplebar-vue": "^2.4.0", - "typescript": "^5.7.3", - "unocss": "^0.64.1", - "unplugin-auto-import": "^19.1.0", - "unplugin-vue-components": "^28.4.0", - "unplugin-vue-macros": "^2.14.2", - "unplugin-vue-router": "^0.11.2", - "vite": "^5.4.14", - "vite-plugin-monaco-editor": "^1.1.0", - "vite-plugin-vue-layouts": "^0.11.0", - "vue": "^3.5.13", - "vue-i18n": "^11.1.1", - "vue-router": "^4.5.0", - "vue-tsc": "^2.1.10" - } + "name": "zephyr", + "type": "module", + "private": true, + "version": "0.0.1", + "description": "An Electron application with Vue and TypeScript", + "main": "./out/main/index.js", + "author": "example.com", + "homepage": "https://electron-vite.org", + "scripts": { + "runInstall": "node node_modules/electron/install.js", + "format": "prettier --write .", + "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix", + "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", + "typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false", + "typecheck": "npm run typecheck:node && npm run typecheck:web", + "start": "electron-vite preview", + "dev": "chcp 65001 && set DEBUG=app:*&& electron-vite dev", + "dev:watch": "chcp 65001 & set DEBUG=app:*& electron-vite dev --watch", + "build": "npm run typecheck && electron-vite build", + "postinstall": "electron-builder install-app-deps", + "build:unpack": "npm run build && electron-builder --dir", + "build:win": "npm run build && electron-builder --win", + "build:mac": "npm run build && electron-builder --mac", + "build:linux": "npm run build && electron-builder --linux" + }, + "dependencies": { + "@electron-toolkit/preload": "^3.0.1", + "@electron-toolkit/utils": "^3.0.0", + "electron-updater": "^6.3.9", + "inversify": "^6.2.2", + "lowdb": "^7.0.1", + "reflect-metadata": "^0.2.2" + }, + "devDependencies": { + "@electron-toolkit/eslint-config": "^1.0.2", + "@electron-toolkit/eslint-config-ts": "^2.0.0", + "@electron-toolkit/tsconfig": "^1.0.1", + "@intlify/unplugin-vue-i18n": "^6.0.3", + "@rushstack/eslint-patch": "^1.10.5", + "@types/debug": "^4.1.12", + "@types/node": "^20.17.19", + "@unocss/preset-rem-to-px": "^0.64.1", + "@unocss/reset": "^0.64.1", + "@vitejs/plugin-vue": "^5.2.1", + "@vitejs/plugin-vue-jsx": "^4.1.1", + "@vue/eslint-config-prettier": "^9.0.0", + "@vue/eslint-config-typescript": "^13.0.0", + "@vueuse/core": "^12.7.0", + "debug": "^4.4.0", + "electron": "^31.7.7", + "electron-builder": "^24.13.3", + "electron-vite": "^2.3.0", + "eslint": "^8.57.1", + "eslint-plugin-vue": "^9.32.0", + "extract-zip": "^2.0.1", + "locales": "workspace:*", + "lodash-es": "^4.17.21", + "monaco-editor": "^0.52.2", + "prettier": "^3.5.1", + "rotating-file-stream": "^3.2.6", + "sass": "^1.85.0", + "simplebar-vue": "^2.4.0", + "typescript": "^5.7.3", + "unocss": "^0.64.1", + "unplugin-auto-import": "^19.1.0", + "unplugin-vue-components": "^28.4.0", + "unplugin-vue-macros": "^2.14.2", + "unplugin-vue-router": "^0.11.2", + "vite": "^5.4.14", + "vite-plugin-monaco-editor": "^1.1.0", + "vite-plugin-vue-layouts": "^0.11.0", + "vue": "^3.5.13", + "vue-i18n": "^11.1.1", + "vue-router": "^4.5.0", + "vue-tsc": "^2.1.10" + } } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6de82ae..dee51e9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,2 @@ packages: - - "packages/*" + - "packages/*" diff --git a/resources/fuck.html b/resources/fuck.html index 561543f..a101415 100644 --- a/resources/fuck.html +++ b/resources/fuck.html @@ -1,11 +1,12 @@ - + - - - + + + Document - - - 前往 百度 - + + + 前往 + 百度 + diff --git a/src/common/_ioc.main.ts b/src/common/_ioc.main.ts index 5efa038..076a82e 100644 --- a/src/common/_ioc.main.ts +++ b/src/common/_ioc.main.ts @@ -4,13 +4,13 @@ import PlatFormCommand from "common/event/PlatForm/main/command" import TabsCommand from "common/event/Tabs/main/command" const modules = new ContainerModule(bind => { - bind("TabsCommand").to(TabsCommand).inSingletonScope() - bind("PlatFormCommand").to(PlatFormCommand).inSingletonScope() - bind("UpdateCommand").to(UpdateCommand).inSingletonScope() + bind("TabsCommand").to(TabsCommand).inSingletonScope() + bind("PlatFormCommand").to(PlatFormCommand).inSingletonScope() + bind("UpdateCommand").to(UpdateCommand).inSingletonScope() }) async function destroyAllCommand(ioc: Container) { - await ioc.unloadAsync(modules) + await ioc.unloadAsync(modules) } export { modules, destroyAllCommand } diff --git a/src/common/event/PlatForm/index.ts b/src/common/event/PlatForm/index.ts index 267076a..a94ba38 100644 --- a/src/common/event/PlatForm/index.ts +++ b/src/common/event/PlatForm/index.ts @@ -2,29 +2,29 @@ import { _Base } from "common/lib/_Base" import { ApiFactory } from "common/lib/abstract" class PlatForm extends _Base { - constructor() { - super() - } + constructor() { + super() + } - private get api() { - return ApiFactory.getApiClient() - } + private get api() { + return ApiFactory.getApiClient() + } - async showAbout() { - return this.api.call("BasicService.showAbout") - } + async showAbout() { + return this.api.call("BasicService.showAbout") + } - async isFullScreen() { - return this.api.call("PlatFormCommand.isFullscreen") - } + async isFullScreen() { + return this.api.call("PlatFormCommand.isFullscreen") + } - async toggleFullScreen() { - return this.api.call("PlatFormCommand.fullscreen") - } + async toggleFullScreen() { + return this.api.call("PlatFormCommand.fullscreen") + } - async toggleDevTools() { - return this.api.call("PlatFormCommand.toggleDevTools") - } + async toggleDevTools() { + return this.api.call("PlatFormCommand.toggleDevTools") + } } export { PlatForm } diff --git a/src/common/event/PlatForm/main/command.ts b/src/common/event/PlatForm/main/command.ts index 46f7670..1e0bfe5 100644 --- a/src/common/event/PlatForm/main/command.ts +++ b/src/common/event/PlatForm/main/command.ts @@ -4,78 +4,78 @@ import Tabs from "main/modules/tabs" import WindowManager from "main/modules/window-manager" export default class PlatFormCommand { - constructor( - @inject(WindowManager) private _WindowManager: WindowManager, - @inject(Tabs) private _Tabs: Tabs, - ) {} + constructor( + @inject(WindowManager) private _WindowManager: WindowManager, + @inject(Tabs) private _Tabs: Tabs, + ) {} - setTheme(theme: typeof nativeTheme.themeSource) { - nativeTheme.themeSource = theme - } + setTheme(theme: typeof nativeTheme.themeSource) { + nativeTheme.themeSource = theme + } - setTitlBar(options: TitleBarOverlayOptions) { - const mainWindow = this._WindowManager.getMainWindow() - if (mainWindow) { - mainWindow.setTitleBarOverlay(options) - } + setTitlBar(options: TitleBarOverlayOptions) { + const mainWindow = this._WindowManager.getMainWindow() + if (mainWindow) { + mainWindow.setTitleBarOverlay(options) } + } - showAbout() { - this._WindowManager.showWindow("about") - } + showAbout() { + this._WindowManager.showWindow("about") + } - toggleDevTools() { - const focusedWindow = this._WindowManager.getFocusWindow() - if (focusedWindow) { - // @ts-ignore ... - focusedWindow.toggleDevTools() - } + toggleDevTools() { + const focusedWindow = this._WindowManager.getFocusWindow() + if (focusedWindow) { + // @ts-ignore ... + focusedWindow.toggleDevTools() } - fullscreen() { - const focusedWindow = this._WindowManager.getFocusWindow() - if (focusedWindow) { - const isFullScreen = focusedWindow!.isFullScreen() - focusedWindow!.setFullScreen(!isFullScreen) - } + } + fullscreen() { + const focusedWindow = this._WindowManager.getFocusWindow() + if (focusedWindow) { + const isFullScreen = focusedWindow!.isFullScreen() + focusedWindow!.setFullScreen(!isFullScreen) } + } - isFullscreen() { - const focusedWindow = this._WindowManager.getFocusWindow() - if (focusedWindow) { - return focusedWindow!.isFullScreen() - } - return false + isFullscreen() { + const focusedWindow = this._WindowManager.getFocusWindow() + if (focusedWindow) { + return focusedWindow!.isFullScreen() } + return false + } - relunch() { - app.relaunch() - app.exit() - } + relunch() { + app.relaunch() + app.exit() + } - reload() { - const focusedWindow = this._WindowManager.getFocusWindow() - // 重载之后, 刷新并关闭所有的次要窗体 - if (this._WindowManager.length() > 1 && focusedWindow && focusedWindow.$$opts!.name === this._WindowManager.mainInfo.name) { - const choice = dialog.showMessageBoxSync(focusedWindow, { - type: "question", - buttons: ["取消", "是的,继续", "不,算了"], - title: "警告", - defaultId: 2, - cancelId: 0, - message: "警告", - detail: "重载主窗口将关闭所有子窗口,是否继续", - }) - if (choice == 1) { - this._WindowManager.getWndows().forEach(win => { - if (win.$$opts!.name !== this._WindowManager.mainInfo.name) { - win.close() - } - }) - } else { - return - } - } - this._Tabs.closeAll() - focusedWindow!.reload() + reload() { + const focusedWindow = this._WindowManager.getFocusWindow() + // 重载之后, 刷新并关闭所有的次要窗体 + if (this._WindowManager.length() > 1 && focusedWindow && focusedWindow.$$opts!.name === this._WindowManager.mainInfo.name) { + const choice = dialog.showMessageBoxSync(focusedWindow, { + type: "question", + buttons: ["取消", "是的,继续", "不,算了"], + title: "警告", + defaultId: 2, + cancelId: 0, + message: "警告", + detail: "重载主窗口将关闭所有子窗口,是否继续", + }) + if (choice == 1) { + this._WindowManager.getWndows().forEach(win => { + if (win.$$opts!.name !== this._WindowManager.mainInfo.name) { + win.close() + } + }) + } else { + return + } } + this._Tabs.closeAll() + focusedWindow!.reload() + } } diff --git a/src/common/event/Tabs/index.ts b/src/common/event/Tabs/index.ts index 5b8cd7c..f36ca59 100644 --- a/src/common/event/Tabs/index.ts +++ b/src/common/event/Tabs/index.ts @@ -1,54 +1,54 @@ import { _Base } from "../../lib/_Base" export class Tabs extends _Base { - constructor() { - super() - } - - private isListen: boolean = false - - private execUpdate = (...args) => { - this.#fnList.forEach(v => v(...args)) - } - - #fnList: ((...args) => void)[] = [] - listenUpdate(cb: (...args) => void) { - if (!this.isListen) { - api.on("main:TabsCommand.update", this.execUpdate) - this.isListen = true - } - this.#fnList.push(cb) - } - - unListenUpdate(fn: (...args) => void) { - this.#fnList = this.#fnList.filter(v => { - return v !== fn - }) - if (!this.#fnList.length) { - api.off("main:TabsCommand.update", this.execUpdate) - this.isListen = false - } - } - - bindPosition(data) { - api.call("TabsCommand.bindElement", data) - } - - closeAll() { - api.call("TabsCommand.closeAll") - } - - sync() { - api.call("TabsCommand.sync") - } - - unListenerAll() { - this.#fnList = [] - api.offAll("main:TabsCommand.update") - } - - async getAllTabs() { - const res = await api.call("TabsCommand.getAllTabs") - return res - } + constructor() { + super() + } + + private isListen: boolean = false + + private execUpdate = (...args) => { + this.#fnList.forEach(v => v(...args)) + } + + #fnList: ((...args) => void)[] = [] + listenUpdate(cb: (...args) => void) { + if (!this.isListen) { + api.on("main:TabsCommand.update", this.execUpdate) + this.isListen = true + } + this.#fnList.push(cb) + } + + unListenUpdate(fn: (...args) => void) { + this.#fnList = this.#fnList.filter(v => { + return v !== fn + }) + if (!this.#fnList.length) { + api.off("main:TabsCommand.update", this.execUpdate) + this.isListen = false + } + } + + bindPosition(data) { + api.call("TabsCommand.bindElement", data) + } + + closeAll() { + api.call("TabsCommand.closeAll") + } + + sync() { + api.call("TabsCommand.sync") + } + + unListenerAll() { + this.#fnList = [] + api.offAll("main:TabsCommand.update") + } + + async getAllTabs() { + const res = await api.call("TabsCommand.getAllTabs") + return res + } } diff --git a/src/common/event/Tabs/main/command.ts b/src/common/event/Tabs/main/command.ts index 0619e17..0962cca 100644 --- a/src/common/event/Tabs/main/command.ts +++ b/src/common/event/Tabs/main/command.ts @@ -4,61 +4,61 @@ import WindowManager from "main/modules/window-manager" import { broadcast } from "main/utils" class TabsCommand { - constructor( - @inject(Tabs) private _Tabs: Tabs, - @inject(WindowManager) private _WindowManager: WindowManager, - ) { - this._Tabs.events.on("update", this.listenerTabActive) - } + constructor( + @inject(Tabs) private _Tabs: Tabs, + @inject(WindowManager) private _WindowManager: WindowManager, + ) { + this._Tabs.events.on("update", this.listenerTabActive) + } - listenerTabActive = () => { - broadcast("main:TabsCommand.update", this.getAllTabs()) - } + listenerTabActive = () => { + broadcast("main:TabsCommand.update", this.getAllTabs()) + } - bindElement(rect) { - this._Tabs.updateRect(rect) - } + bindElement(rect) { + this._Tabs.updateRect(rect) + } - reload() { - this._WindowManager.getMainWindow()?.reload() - } + reload() { + this._WindowManager.getMainWindow()?.reload() + } - sync() { - this.listenerTabActive() - if (!this.getAllTabs().length) { - this.add("about:blank") - } + sync() { + this.listenerTabActive() + if (!this.getAllTabs().length) { + this.add("about:blank") } + } - add(url) { - this._Tabs.add(url, true, this._WindowManager.getMainWindow()!) - } + add(url) { + this._Tabs.add(url, true, this._WindowManager.getMainWindow()!) + } - nagivate(index: number, url: string) { - this._Tabs.navigate(+index, url) - } + nagivate(index: number, url: string) { + this._Tabs.navigate(+index, url) + } - closeAll() { - this._Tabs.closeAll() - } + closeAll() { + this._Tabs.closeAll() + } - setActive(index) { - this._Tabs.changeActive(index) - } + setActive(index) { + this._Tabs.changeActive(index) + } - closeTab(e) { - this._Tabs.remove(e.body.active) - } + closeTab(e) { + this._Tabs.remove(e.body.active) + } - getAllTabs() { - return this._Tabs._tabs.map(v => ({ - url: v.url, - showUrl: v.showUrl, - title: v.title, - favicons: v.favicons, - isActive: v.isActive, - })) - } + getAllTabs() { + return this._Tabs._tabs.map(v => ({ + url: v.url, + showUrl: v.showUrl, + title: v.title, + favicons: v.favicons, + isActive: v.isActive, + })) + } } export { TabsCommand } diff --git a/src/common/event/update/index.ts b/src/common/event/update/index.ts index 20f0ad3..55676c4 100644 --- a/src/common/event/update/index.ts +++ b/src/common/event/update/index.ts @@ -6,9 +6,9 @@ const curProgress = ref(0) // }) function useUpdate() { - return { - curProgress, - } + return { + curProgress, + } } export { useUpdate } diff --git a/src/common/event/update/main/command.ts b/src/common/event/update/main/command.ts index 5407dc3..069ece6 100644 --- a/src/common/event/update/main/command.ts +++ b/src/common/event/update/main/command.ts @@ -2,9 +2,9 @@ import { inject } from "inversify" import Updater from "main/modules/updater" export default class PlatFormCommand { - constructor(@inject(Updater) private _Updater: Updater) {} + constructor(@inject(Updater) private _Updater: Updater) {} - async triggerHotUpdate() { - await this._Updater.triggerHotUpdate() - } + async triggerHotUpdate() { + await this._Updater.triggerHotUpdate() + } } diff --git a/src/common/event/update/main/index.ts b/src/common/event/update/main/index.ts index aa3132d..9aa935d 100644 --- a/src/common/event/update/main/index.ts +++ b/src/common/event/update/main/index.ts @@ -2,7 +2,7 @@ import { broadcast } from "main/utils" import { AllKeys } from "common/event/common" function emitHotUpdateReady(...argu) { - broadcast("hot-update-ready", ...argu) + broadcast("hot-update-ready", ...argu) } export { emitHotUpdateReady } diff --git a/src/common/lib/_Base.ts b/src/common/lib/_Base.ts index 91d8f4b..1638ca1 100644 --- a/src/common/lib/_Base.ts +++ b/src/common/lib/_Base.ts @@ -1,12 +1,12 @@ export abstract class _Base { - static instance + static instance - static getInstance(): T { - if (!this.instance) { - // 如果实例不存在,则创建一个新的实例 - // @ts-ignore ... - this.instance = new this() - } - return this.instance + static getInstance(): T { + if (!this.instance) { + // 如果实例不存在,则创建一个新的实例 + // @ts-ignore ... + this.instance = new this() } + return this.instance + } } diff --git a/src/common/lib/abstract.ts b/src/common/lib/abstract.ts index 9712b8a..0a025d9 100644 --- a/src/common/lib/abstract.ts +++ b/src/common/lib/abstract.ts @@ -3,51 +3,51 @@ import { BrowserApiClient } from "common/lib/browser" // 定义抽象 API 接口 export interface IApiClient { - call(command: string, ...args: any[]): Promise - on(channel: K, callback: (...args: any[]) => void): void - off(channel: K, callback: (...args: any[]) => void): void - offAll(channel: K): void + call(command: string, ...args: any[]): Promise + on(channel: K, callback: (...args: any[]) => void): void + off(channel: K, callback: (...args: any[]) => void): void + offAll(channel: K): void } class NullApiClient implements IApiClient { - async call(command: string, ...args: any[]): Promise { - args - console.warn(`API call to ${command} failed: API client not initialized`) - return undefined as any - } - - on(channel: K, callback: (...args: any[]) => void): void { - callback - console.warn(`Failed to register listener for ${channel}: API client not initialized`) - } - - off(channel: K, callback: (...args: any[]) => void): void { - callback - console.warn(`Failed to unregister listener for ${channel}: API client not initialized`) - } - - offAll(channel: K): void { - console.warn(`Failed to unregister all listeners for ${channel}: API client not initialized`) - } + async call(command: string, ...args: any[]): Promise { + args + console.warn(`API call to ${command} failed: API client not initialized`) + return undefined as any + } + + on(channel: K, callback: (...args: any[]) => void): void { + callback + console.warn(`Failed to register listener for ${channel}: API client not initialized`) + } + + off(channel: K, callback: (...args: any[]) => void): void { + callback + console.warn(`Failed to unregister listener for ${channel}: API client not initialized`) + } + + offAll(channel: K): void { + console.warn(`Failed to unregister all listeners for ${channel}: API client not initialized`) + } } // 创建 API 工厂 export class ApiFactory { - private static instance: IApiClient = new NullApiClient() // 默认使用空实现 - - static setApiClient(client: IApiClient) { - this.instance = client - } - - static getApiClient(): IApiClient { - if (this.instance instanceof NullApiClient) { - // 根据环境选择合适的 API 客户端 - if (window.api && window.electron) { - this.instance = new ElectronApiClient() - } else { - this.instance = new BrowserApiClient() - } - } - return this.instance + private static instance: IApiClient = new NullApiClient() // 默认使用空实现 + + static setApiClient(client: IApiClient) { + this.instance = client + } + + static getApiClient(): IApiClient { + if (this.instance instanceof NullApiClient) { + // 根据环境选择合适的 API 客户端 + if (window.api && window.electron) { + this.instance = new ElectronApiClient() + } else { + this.instance = new BrowserApiClient() + } } + return this.instance + } } diff --git a/src/common/lib/browser.ts b/src/common/lib/browser.ts index 100e60d..e80a478 100644 --- a/src/common/lib/browser.ts +++ b/src/common/lib/browser.ts @@ -1,29 +1,29 @@ import { IApiClient } from "./abstract" export class BrowserApiClient implements IApiClient { - call(command: string, ...args: any[]): Promise { - // 浏览器特定实现,可能使用 fetch 或其他方式 - const [service, method] = command.split(".") - return fetch(`/api/${service}/${method}`, { - method: "POST", - body: JSON.stringify(args), - headers: { "Content-Type": "application/json" }, - }).then(res => res.json()) - } + call(command: string, ...args: any[]): Promise { + // 浏览器特定实现,可能使用 fetch 或其他方式 + const [service, method] = command.split(".") + return fetch(`/api/${service}/${method}`, { + method: "POST", + body: JSON.stringify(args), + headers: { "Content-Type": "application/json" }, + }).then(res => res.json()) + } - // 实现其他方法... - on(channel: K, callback: (...args: any[]) => void): void { - // 浏览器中可能使用 WebSocket 或其他方式 - console.log("不支持 on 方法", channel, callback) - } + // 实现其他方法... + on(channel: K, callback: (...args: any[]) => void): void { + // 浏览器中可能使用 WebSocket 或其他方式 + console.log("不支持 on 方法", channel, callback) + } - off(channel: K, callback: (...args: any[]) => void): void { - // 相应的解绑实现 - console.log("不支持 on 方法", channel, callback) - } + off(channel: K, callback: (...args: any[]) => void): void { + // 相应的解绑实现 + console.log("不支持 on 方法", channel, callback) + } - offAll(channel: K): void { - // 相应的全部解绑实现 - console.log("不支持 on 方法", channel) - } + offAll(channel: K): void { + // 相应的全部解绑实现 + console.log("不支持 on 方法", channel) + } } diff --git a/src/common/lib/electron.ts b/src/common/lib/electron.ts index 27908a5..43da684 100644 --- a/src/common/lib/electron.ts +++ b/src/common/lib/electron.ts @@ -1,20 +1,20 @@ import { IApiClient } from "./abstract" export class ElectronApiClient implements IApiClient { - call(command: string, ...args: any[]): Promise { - // Electron 特定实现 - return window.api.call(command, ...args) - } + call(command: string, ...args: any[]): Promise { + // Electron 特定实现 + return window.api.call(command, ...args) + } - on(channel: K, callback: (...args: any[]) => void): void { - window.api.on(channel, callback) - } + on(channel: K, callback: (...args: any[]) => void): void { + window.api.on(channel, callback) + } - off(channel: K, callback: (...args: any[]) => void): void { - window.api.off(channel, callback) - } + off(channel: K, callback: (...args: any[]) => void): void { + window.api.off(channel, callback) + } - offAll(channel: K): void { - window.api.offAll(channel) - } + offAll(channel: K): void { + window.api.offAll(channel) + } } diff --git a/src/common/usePlatform.ts b/src/common/usePlatform.ts index 1316845..85f70ea 100644 --- a/src/common/usePlatform.ts +++ b/src/common/usePlatform.ts @@ -2,8 +2,8 @@ import { PlatForm } from "./event/PlatForm" import { Tabs } from "./event/Tabs" export function usePlatForm() { - return { - Tabs: Tabs.getInstance(), - PlatForm: PlatForm.getInstance(), - } + return { + Tabs: Tabs.getInstance(), + PlatForm: PlatForm.getInstance(), + } } diff --git a/src/main/App copy.ts b/src/main/App copy.ts index 9d0584c..220ca22 100644 --- a/src/main/App copy.ts +++ b/src/main/App copy.ts @@ -10,155 +10,155 @@ import { getFileUrl } from "./utils" import BaseClass from "./base/base" protocol.registerSchemesAsPrivileged([ - // { - // scheme: "http", - // privileges: { standard: true, bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true }, - // }, - // { - // scheme: "https", - // privileges: { standard: true, bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true }, - // }, - // { scheme: "mailto", privileges: { standard: true } }, - { - scheme: "api", - privileges: { - standard: true, - secure: true, - supportFetchAPI: true, - }, + // { + // scheme: "http", + // privileges: { standard: true, bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true }, + // }, + // { + // scheme: "https", + // privileges: { standard: true, bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true }, + // }, + // { scheme: "mailto", privileges: { standard: true } }, + { + scheme: "api", + privileges: { + standard: true, + secure: true, + supportFetchAPI: true, }, + }, ]) @injectable() class App extends BaseClass { - destroy() { - // destroyAll() - // 这里是应用正常退出 - } - // private _setting: Setting - // private _db: DB - private _Api: Api - private _windowManager: WindowManager - // private _tabs: Tabs + destroy() { + // destroyAll() + // 这里是应用正常退出 + } + // private _setting: Setting + // private _db: DB + private _Api: Api + private _windowManager: WindowManager + // private _tabs: Tabs - constructor( - // @inject(Setting) setting: Setting, - // @inject(DB) db: DB, - @inject(Api) Api: Api, - @inject(WindowManager) windowManager: WindowManager, - // @inject(Tabs) tabs: Tabs, - ) { - super() - // this._setting = setting - // this._db = db - this._Api = Api - this._windowManager = windowManager - // this._tabs = tabs - } + constructor( + // @inject(Setting) setting: Setting, + // @inject(DB) db: DB, + @inject(Api) Api: Api, + @inject(WindowManager) windowManager: WindowManager, + // @inject(Tabs) tabs: Tabs, + ) { + super() + // this._setting = setting + // this._db = db + this._Api = Api + this._windowManager = windowManager + // this._tabs = tabs + } - async init() { - this._windowManager.init() - app.whenReady().then(() => { - electronApp.setAppUserModelId("top.xieyaxin") - this.create() - this._Api.init() - }) - app.on("window-all-closed", () => { - if (process.platform !== "darwin") { - app.quit() - } - }) - app.on("will-quit", () => { - this.destroy() - }) - } + async init() { + this._windowManager.init() + app.whenReady().then(() => { + electronApp.setAppUserModelId("top.xieyaxin") + this.create() + this._Api.init() + }) + app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit() + } + }) + app.on("will-quit", () => { + this.destroy() + }) + } - create() { - this._windowManager.showMainWindow() - const mainWindow = this._windowManager.getMainWindow() - if (mainWindow) { - nativeTheme.themeSource = "light" - mainWindow.setTitleBarOverlay({ - height: 29, // the smallest size of the title bar on windows accounting for the border on windows 11 - color: "#F8F8F8", - symbolColor: "#000000", - }) - this._windowManager.showWindow("main-top") - const mainTopWindow = this._windowManager.get("main-top") - setTimeout(() => { - // console.log(mainWindow.getParentWindow()); - setTimeout(() => { - mainWindow.contentView.children.length = 0 - const view = new WebContentsView() - view.addChildView(mainTopWindow!.contentView) - view.webContents.loadURL(getFileUrl("about.html")) - // mainTopWindow!.contentView.setBounds({ x: 0, y: 0, width: 100, height: 30 }) - // view.setBounds({ x: 0, y: 0, width: 100, height: 30 }) - mainWindow.contentView.addChildView(view) - // mainWindow.contentView.children.sort() - console.log(mainWindow.contentView.children) - }, 5000) - // mainWindow.webContents = mainTopWindow!.webContents - mainWindow.reload() - console.log(mainWindow.webContents.getURL()) + create() { + this._windowManager.showMainWindow() + const mainWindow = this._windowManager.getMainWindow() + if (mainWindow) { + nativeTheme.themeSource = "light" + mainWindow.setTitleBarOverlay({ + height: 29, // the smallest size of the title bar on windows accounting for the border on windows 11 + color: "#F8F8F8", + symbolColor: "#000000", + }) + this._windowManager.showWindow("main-top") + const mainTopWindow = this._windowManager.get("main-top") + setTimeout(() => { + // console.log(mainWindow.getParentWindow()); + setTimeout(() => { + mainWindow.contentView.children.length = 0 + const view = new WebContentsView() + view.addChildView(mainTopWindow!.contentView) + view.webContents.loadURL(getFileUrl("about.html")) + // mainTopWindow!.contentView.setBounds({ x: 0, y: 0, width: 100, height: 30 }) + // view.setBounds({ x: 0, y: 0, width: 100, height: 30 }) + mainWindow.contentView.addChildView(view) + // mainWindow.contentView.children.sort() + console.log(mainWindow.contentView.children) + }, 5000) + // mainWindow.webContents = mainTopWindow!.webContents + mainWindow.reload() + console.log(mainWindow.webContents.getURL()) - // mainTopWindow?.destroy() - // mainWindow.contentView.addChildView(mainWindow.contentView) - console.log(`child count: `, mainWindow.contentView.children.length) - }, 2000) - // if (mainTopWindow) { - // mainTopWindow.setParentWindow(mainWindow) - // mainTopWindow.setIgnoreMouseEvents(true, { forward: false }) - // const listenMove = () => { - // if (mainWindow && mainTopWindow) { - // const pos = mainWindow.getPosition() - // mainTopWindow.setPosition(pos[0], pos[1]) - // } - // } - // mainWindow?.on("move", listenMove) - // const listenResize = () => { - // if (mainWindow && mainTopWindow) { - // const size = mainWindow.getSize() - // console.log(size) - // mainTopWindow.setSize(size[0], size[1]) - // const pos = mainWindow.getPosition() - // mainTopWindow.setPosition(pos[0], pos[1]) - // } - // } - // listenResize() - // mainWindow?.on("resize", listenResize) - // } - } - // 考虑双browserwindow模式 - /** - * 因为browserwindow可以设置穿透,考虑将tab放在底层window上,其他组件放在上层window上。 - */ - // const webContentsView = new WebContentsView({ - // webPreferences: { - // preload: join(__dirname, "../preload/index.mjs"), - // transparent: true, - // nodeIntegration: true, - // spellcheck: false, - // contextIsolation: true, - // }, - // }) - // // mainWindow!.contentView = webContentsView - // // setTimeout(() => { - // mainWindow!.contentView.addChildView(webContentsView) - // // mainWindow?.setIgnoreMouseEvents(true, { forward: true }) - // // }, 2000); - // webContentsView.webContents.loadURL(getFileUrl("index.html")) - // const listenResize = () => { - // const size = mainWindow!.getSize() - // webContentsView.setBounds({ x: 0, y: 0, width: size[0], height: size[1] }) - // } - // listenResize() - // mainWindow!.addListener("resize", listenResize) - - // this._tabs.add("https://baidu.com", true) - // this._tabs.add("https://zhihu.com") - return mainWindow + // mainTopWindow?.destroy() + // mainWindow.contentView.addChildView(mainWindow.contentView) + console.log(`child count: `, mainWindow.contentView.children.length) + }, 2000) + // if (mainTopWindow) { + // mainTopWindow.setParentWindow(mainWindow) + // mainTopWindow.setIgnoreMouseEvents(true, { forward: false }) + // const listenMove = () => { + // if (mainWindow && mainTopWindow) { + // const pos = mainWindow.getPosition() + // mainTopWindow.setPosition(pos[0], pos[1]) + // } + // } + // mainWindow?.on("move", listenMove) + // const listenResize = () => { + // if (mainWindow && mainTopWindow) { + // const size = mainWindow.getSize() + // console.log(size) + // mainTopWindow.setSize(size[0], size[1]) + // const pos = mainWindow.getPosition() + // mainTopWindow.setPosition(pos[0], pos[1]) + // } + // } + // listenResize() + // mainWindow?.on("resize", listenResize) + // } } + // 考虑双browserwindow模式 + /** + * 因为browserwindow可以设置穿透,考虑将tab放在底层window上,其他组件放在上层window上。 + */ + // const webContentsView = new WebContentsView({ + // webPreferences: { + // preload: join(__dirname, "../preload/index.mjs"), + // transparent: true, + // nodeIntegration: true, + // spellcheck: false, + // contextIsolation: true, + // }, + // }) + // // mainWindow!.contentView = webContentsView + // // setTimeout(() => { + // mainWindow!.contentView.addChildView(webContentsView) + // // mainWindow?.setIgnoreMouseEvents(true, { forward: true }) + // // }, 2000); + // webContentsView.webContents.loadURL(getFileUrl("index.html")) + // const listenResize = () => { + // const size = mainWindow!.getSize() + // webContentsView.setBounds({ x: 0, y: 0, width: size[0], height: size[1] }) + // } + // listenResize() + // mainWindow!.addListener("resize", listenResize) + + // this._tabs.add("https://baidu.com", true) + // this._tabs.add("https://zhihu.com") + return mainWindow + } } export default App diff --git a/src/main/App.ts b/src/main/App.ts index b440f86..e87ba17 100644 --- a/src/main/App.ts +++ b/src/main/App.ts @@ -13,74 +13,74 @@ import Zephyr from "./modules/zephyr" import Updater from "./modules/updater" protocol.registerSchemesAsPrivileged([ - // { - // scheme: "http", - // privileges: { standard: true, bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true }, - // }, - // { - // scheme: "https", - // privileges: { standard: true, bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true }, - // }, - // { scheme: "mailto", privileges: { standard: true } }, - { - scheme: "api", - privileges: { - standard: true, - secure: true, - supportFetchAPI: true, - }, + // { + // scheme: "http", + // privileges: { standard: true, bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true }, + // }, + // { + // scheme: "https", + // privileges: { standard: true, bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true }, + // }, + // { scheme: "mailto", privileges: { standard: true } }, + { + scheme: "api", + privileges: { + standard: true, + secure: true, + supportFetchAPI: true, }, - { - scheme: "zephyr", - privileges: { - standard: true, - secure: true, - supportFetchAPI: true, - }, + }, + { + scheme: "zephyr", + privileges: { + standard: true, + secure: true, + supportFetchAPI: true, }, + }, ]) @injectable() class App extends BaseClass { - destroy() { - this._IOC.destroy() - // 这里是应用正常退出, 可以检测应用是不是非正常退出,比如应用启动时记录一个启动时间并删除上一次结束时间和开始时间,结束时记录一个结束时间, - // 如果存在结束时间或者不存在开始时间则为正常启动 - } + destroy() { + this._IOC.destroy() + // 这里是应用正常退出, 可以检测应用是不是非正常退出,比如应用启动时记录一个启动时间并删除上一次结束时间和开始时间,结束时记录一个结束时间, + // 如果存在结束时间或者不存在开始时间则为正常启动 + } - constructor( - @inject(IOC) private _IOC: IOC, - @inject(Api) private _Api: Api, - @inject(Command) private _Command: Command, - @inject(DB) private _DB: DB, - @inject(WindowManager) private _WindowManager: WindowManager, - @inject(Zephyr) private _Zephyr: Zephyr, - @inject(Updater) private _Updater: Updater, - ) { - super() - } + constructor( + @inject(IOC) private _IOC: IOC, + @inject(Api) private _Api: Api, + @inject(Command) private _Command: Command, + @inject(DB) private _DB: DB, + @inject(WindowManager) private _WindowManager: WindowManager, + @inject(Zephyr) private _Zephyr: Zephyr, + @inject(Updater) private _Updater: Updater, + ) { + super() + } - async init() { - this._Updater.init() - this._DB.init() - this._Command.init() - this._WindowManager.init() - app.whenReady().then(() => { - this._Api.init() - this._Zephyr.init() - electronApp.setAppUserModelId("top.xieyaxin") - this._WindowManager.showMainWindow() - this._Command.invoke("PlatFormCommand.setTheme", "light") - this._Command.invoke("PlatFormCommand.setTitlBar", { - height: 29, - color: "#F8F8F8", - symbolColor: "#000000", - }) - }) - app.on("will-quit", () => { - this.destroy() - }) - } + async init() { + this._Updater.init() + this._DB.init() + this._Command.init() + this._WindowManager.init() + app.whenReady().then(() => { + this._Api.init() + this._Zephyr.init() + electronApp.setAppUserModelId("top.xieyaxin") + this._WindowManager.showMainWindow() + this._Command.invoke("PlatFormCommand.setTheme", "light") + this._Command.invoke("PlatFormCommand.setTitlBar", { + height: 29, + color: "#F8F8F8", + symbolColor: "#000000", + }) + }) + app.on("will-quit", () => { + this.destroy() + }) + } } export default App diff --git a/src/main/_ioc.ts b/src/main/_ioc.ts index 7613d44..fa0689e 100644 --- a/src/main/_ioc.ts +++ b/src/main/_ioc.ts @@ -6,9 +6,9 @@ import iocCommand, { destroyAllCommand } from "common/_ioc.main" import App from "./App" async function destroyAll() { - await destroyAllModules(_ioc) - await destroyAllController(_ioc) - await destroyAllCommand(_ioc) + await destroyAllModules(_ioc) + await destroyAllController(_ioc) + await destroyAllCommand(_ioc) } const _ioc = new Container() diff --git a/src/main/_iocClass.ts b/src/main/_iocClass.ts index 74d1bd9..9bec22a 100644 --- a/src/main/_iocClass.ts +++ b/src/main/_iocClass.ts @@ -3,21 +3,21 @@ import BaseClass from "./base/base" import { destroyAll, _ioc } from "./_ioc" class IOC extends BaseClass { - init() { - // TODO - } + init() { + // TODO + } - destroy() { - destroyAll() - } + destroy() { + destroyAll() + } - get(serviceIdentifier: interfaces.ServiceIdentifier) { - return _ioc.get(serviceIdentifier) - } + get(serviceIdentifier: interfaces.ServiceIdentifier) { + return _ioc.get(serviceIdentifier) + } - getAsync(serviceIdentifier: interfaces.ServiceIdentifier) { - return _ioc.getAsync(serviceIdentifier) - } + getAsync(serviceIdentifier: interfaces.ServiceIdentifier) { + return _ioc.getAsync(serviceIdentifier) + } } export { IOC } diff --git a/src/main/base/base.ts b/src/main/base/base.ts index 0976ae4..5305a52 100644 --- a/src/main/base/base.ts +++ b/src/main/base/base.ts @@ -1,9 +1,9 @@ import EventEmitter from "node:events" abstract class BaseClass { - public _events = new EventEmitter() - abstract init(...argus: any[]) - abstract destroy() + public _events = new EventEmitter() + abstract init(...argus: any[]) + abstract destroy() } export { BaseClass } diff --git a/src/main/controller/BasicService.ts b/src/main/controller/BasicService.ts index 6c4482c..3668256 100644 --- a/src/main/controller/BasicService.ts +++ b/src/main/controller/BasicService.ts @@ -5,24 +5,24 @@ import WindowManager from "main/modules/window-manager" @injectable() class BasicService extends BaseContainer { - constructor( - @inject(WindowManager) private _WindowManager: WindowManager, - @inject(Tabs) private _Tabs: Tabs, - ) { - super() - } + constructor( + @inject(WindowManager) private _WindowManager: WindowManager, + @inject(Tabs) private _Tabs: Tabs, + ) { + super() + } - showAbout() { - this._WindowManager.showWindow("about") - return { - a: "fuck", - } + showAbout() { + this._WindowManager.showWindow("about") + return { + a: "fuck", } + } - openTabDevtool() { - // this._Tabs.reload(0) - this._Tabs.openDevtool(0) - } + openTabDevtool() { + // this._Tabs.reload(0) + this._Tabs.openDevtool(0) + } } export { BasicService } diff --git a/src/main/controller/TabsService.ts b/src/main/controller/TabsService.ts index fb8b0a2..3d8538d 100644 --- a/src/main/controller/TabsService.ts +++ b/src/main/controller/TabsService.ts @@ -5,38 +5,38 @@ import WindowManager from "main/modules/window-manager" @injectable() class TabsService extends BaseContainer { - constructor( - @inject(Tabs) private _Tabs: Tabs, - @inject(WindowManager) private _WindowManager: WindowManager, - ) { - super() - } + constructor( + @inject(Tabs) private _Tabs: Tabs, + @inject(WindowManager) private _WindowManager: WindowManager, + ) { + super() + } - add(e) { - this._Tabs.add(e.body.url, true, this._WindowManager.getMainWindow()!) - } + add(e) { + this._Tabs.add(e.body.url, true, this._WindowManager.getMainWindow()!) + } - setActive(e) { - this._Tabs.changeActive(e.body.active) - } + setActive(e) { + this._Tabs.changeActive(e.body.active) + } - closeTab(e) { - this._Tabs.remove(e.body.active) - } + closeTab(e) { + this._Tabs.remove(e.body.active) + } - closeTabAll(e) { - this._Tabs.removeAll(e.body.active) - } + closeTabAll(e) { + this._Tabs.removeAll(e.body.active) + } - getAllTabs() { - return this._Tabs._tabs.map(v => ({ - url: v.url, - showUrl: v.showUrl, - title: v.title, - favicons: v.favicons, - isActive: v.isActive, - })) - } + getAllTabs() { + return this._Tabs._tabs.map(v => ({ + url: v.url, + showUrl: v.showUrl, + title: v.title, + favicons: v.favicons, + isActive: v.isActive, + })) + } } export { TabsService } diff --git a/src/main/controller/_ioc.ts b/src/main/controller/_ioc.ts index c1f6e51..f568ca2 100644 --- a/src/main/controller/_ioc.ts +++ b/src/main/controller/_ioc.ts @@ -3,12 +3,12 @@ import BasicService from "./BasicService" import TabsService from "./TabsService" const modules = new ContainerModule(bind => { - bind("BasicService").to(BasicService).inSingletonScope() - bind("TabsService").to(TabsService).inSingletonScope() + bind("BasicService").to(BasicService).inSingletonScope() + bind("TabsService").to(TabsService).inSingletonScope() }) async function destroyAllController(ioc: Container) { - await ioc.unloadAsync(modules) + await ioc.unloadAsync(modules) } export { modules, destroyAllController } diff --git a/src/main/index.ts b/src/main/index.ts index 8c09dd0..b61912c 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -19,56 +19,56 @@ const streams = new Map() // 转换命名空间为安全路径 function sanitizeNamespace(namespace) { - return namespace - .split(":") // 按层级分隔符拆分 - .map(part => part.replace(/[\\/:*?"<>|]/g, "_")) // 替换非法字符 - .join(path.sep) // 拼接为系统路径分隔符(如 / 或 \) + return namespace + .split(":") // 按层级分隔符拆分 + .map(part => part.replace(/[\\/:*?"<>|]/g, "_")) // 替换非法字符 + .join(path.sep) // 拼接为系统路径分隔符(如 / 或 \) } // 覆盖 debug.log 方法 const originalLog = debug.log debug.log = function (...args) { - // 保留原始控制台输出(可选) - originalLog.apply(this, args) - // 获取当前命名空间 - // @ts-ignore ... - const namespace = this.namespace - if (!namespace) { - // TODO 增加容错机制,如果没有命名空间就输出到一个默认文件中 - return - } + // 保留原始控制台输出(可选) + originalLog.apply(this, args) + // 获取当前命名空间 + // @ts-ignore ... + const namespace = this.namespace + if (!namespace) { + // TODO 增加容错机制,如果没有命名空间就输出到一个默认文件中 + return + } - // 生成日志文件路径(示例:logs/app/server.log) - const sanitizedPath = sanitizeNamespace(namespace) - // const logFilePath = path.join(LOG_ROOT, `${sanitizedPath}.log`) + // 生成日志文件路径(示例:logs/app/server.log) + const sanitizedPath = sanitizeNamespace(namespace) + // const logFilePath = path.join(LOG_ROOT, `${sanitizedPath}.log`) - const today = new Date().toISOString().split("T")[0] - const logFilePath = path.join(LOG_ROOT, sanitizedPath, `${today}.log`) + const today = new Date().toISOString().split("T")[0] + const logFilePath = path.join(LOG_ROOT, sanitizedPath, `${today}.log`) - // 确保目录存在 - const dir = path.dirname(logFilePath) - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }) // 自动创建多级目录 - } + // 确保目录存在 + const dir = path.dirname(logFilePath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) // 自动创建多级目录 + } - // 获取或创建文件流 - let stream = streams.get(logFilePath) - if (!stream) { - // stream = fs.createWriteStream(logFilePath, { flags: "a" }) // 追加模式 - stream = rfs.createStream(path.parse(logFilePath).base, { - path: dir, - size: "10M", // 单个文件最大 10MB - rotate: 5, // 保留最近 5 个文件 - }) - streams.set(logFilePath, stream) - } + // 获取或创建文件流 + let stream = streams.get(logFilePath) + if (!stream) { + // stream = fs.createWriteStream(logFilePath, { flags: "a" }) // 追加模式 + stream = rfs.createStream(path.parse(logFilePath).base, { + path: dir, + size: "10M", // 单个文件最大 10MB + rotate: 5, // 保留最近 5 个文件 + }) + streams.set(logFilePath, stream) + } - // 写入日志(添加时间戳) - const message = args.join(" ") - stream.write(`${message}\n`) + // 写入日志(添加时间戳) + const message = args.join(" ") + stream.write(`${message}\n`) - // const timestamp = new Date().toISOString() - // stream.write(`[${timestamp}] ${message}\n`) + // const timestamp = new Date().toISOString() + // stream.write(`[${timestamp}] ${message}\n`) } const curApp = _ioc.get(App) @@ -76,10 +76,10 @@ curApp.init() const _debug = debug("app:app") app.on("before-quit", () => { - _debug("应用关闭") - streams.forEach(stream => { - stream.end() - stream.destroy() - }) - streams.clear() + _debug("应用关闭") + streams.forEach(stream => { + stream.end() + stream.destroy() + }) + streams.clear() }) diff --git a/src/main/modules/_ioc.ts b/src/main/modules/_ioc.ts index 7196178..c9ade3e 100644 --- a/src/main/modules/_ioc.ts +++ b/src/main/modules/_ioc.ts @@ -9,28 +9,28 @@ import Zephyr from "./zephyr" import Updater from "./updater" const modules = new ContainerModule(bind => { - bind(Setting).toConstantValue(new Setting()) - bind(Zephyr).toSelf().inSingletonScope() - bind(Updater).toSelf().inSingletonScope() - bind(Api).toSelf().inSingletonScope() - bind(WindowManager).toSelf().inSingletonScope() - bind(Commands).toSelf().inSingletonScope() - bind(Tabs).toSelf().inSingletonScope() - bind(DB).toSelf().inSingletonScope() + bind(Setting).toConstantValue(new Setting()) + bind(Zephyr).toSelf().inSingletonScope() + bind(Updater).toSelf().inSingletonScope() + bind(Api).toSelf().inSingletonScope() + bind(WindowManager).toSelf().inSingletonScope() + bind(Commands).toSelf().inSingletonScope() + bind(Tabs).toSelf().inSingletonScope() + bind(DB).toSelf().inSingletonScope() }) async function destroyAllModules(ioc: Container) { - await Promise.all([ - ioc.get(Setting).destroy(), - ioc.get(WindowManager).destroy(), - ioc.get(Commands).destroy(), - ioc.get(Updater).destroy(), - ioc.get(Zephyr).destroy(), - ioc.get(Tabs).destroy(), - ioc.get(Api).destroy(), - ioc.get(DB).destroy(), - ]) - ioc.unloadAsync(modules) + await Promise.all([ + ioc.get(Setting).destroy(), + ioc.get(WindowManager).destroy(), + ioc.get(Commands).destroy(), + ioc.get(Updater).destroy(), + ioc.get(Zephyr).destroy(), + ioc.get(Tabs).destroy(), + ioc.get(Api).destroy(), + ioc.get(DB).destroy(), + ]) + ioc.unloadAsync(modules) } export default modules diff --git a/src/main/modules/api/index.ts b/src/main/modules/api/index.ts index 6abe99e..a047f12 100644 --- a/src/main/modules/api/index.ts +++ b/src/main/modules/api/index.ts @@ -5,85 +5,85 @@ import BaseClass from "main/base/base" @injectable() class Api extends BaseClass { - constructor(@inject(IOC) private _IOC: IOC) { - super() - this.interceptHandler = this.interceptHandler.bind(this) - } + constructor(@inject(IOC) private _IOC: IOC) { + super() + this.interceptHandler = this.interceptHandler.bind(this) + } - destroy() { - // TODO - } - init(partition?: string) { - // const ses = partition ? session.fromPartition(partition) : session.defaultSession - const ses = partition ? session.fromPartition(partition) : session.defaultSession - ses.protocol.handle("api", this.interceptHandler) - } - async interceptHandler(request: Request) { - if (request.url.startsWith("api://fuck/")) { - let curUrl = request.url - const isScriteText = curUrl.endsWith("?script") - if (isScriteText) { - curUrl = curUrl.replace("?script", "") - } - const isPost = request.method.toLowerCase() === "post" - const file = curUrl.replace("api://fuck/", "") - const array = file.split("/") - const routePath = array.slice(0, -1).join("/") - const fnName = array[array.length - 1] - // https://vitejs.cn/vite5-cn/guide/features.html#dynamic-import - const module = await this._IOC.getAsync(routePath) - // const module = await import(`main/controller/${routePath}.ts`) - const opts = { body: {}, query: {} } - if (isPost) { - opts.body = await request.json() - } - const headers: HeadersInit = {} - if (isScriteText) { - headers["content-type"] = "text/javascript" - } - if (isPost) { - headers["content-type"] = "application/json" - } - if (module && module[fnName]) { - if (typeof module[fnName] === "string") { - const result = module[fnName] - return new Response(result, { - status: 200, - headers: Object.keys(headers).length ? headers : undefined, - }) - } - if (typeof module[fnName] === "function") { - let result = await module[fnName](opts) - if (typeof result === "object") { - result = JSON.stringify(result) - } - return new Response(result, { - status: 200, - headers: Object.keys(headers).length ? headers : undefined, - }) - } - if (typeof module[fnName] === "object") { - let result = module[fnName] - if (typeof result === "object") { - result = JSON.stringify(result) - } - return new Response(result, { - status: 200, - headers: Object.keys(headers).length ? headers : undefined, - }) - } - } - return new Response("", { - status: 500, - headers: Object.keys(headers).length ? headers : undefined, - }) - } else if (request.url.startsWith("api://")) { - return new Response("error", { - status: 500, - }) + destroy() { + // TODO + } + init(partition?: string) { + // const ses = partition ? session.fromPartition(partition) : session.defaultSession + const ses = partition ? session.fromPartition(partition) : session.defaultSession + ses.protocol.handle("api", this.interceptHandler) + } + async interceptHandler(request: Request) { + if (request.url.startsWith("api://fuck/")) { + let curUrl = request.url + const isScriteText = curUrl.endsWith("?script") + if (isScriteText) { + curUrl = curUrl.replace("?script", "") + } + const isPost = request.method.toLowerCase() === "post" + const file = curUrl.replace("api://fuck/", "") + const array = file.split("/") + const routePath = array.slice(0, -1).join("/") + const fnName = array[array.length - 1] + // https://vitejs.cn/vite5-cn/guide/features.html#dynamic-import + const module = await this._IOC.getAsync(routePath) + // const module = await import(`main/controller/${routePath}.ts`) + const opts = { body: {}, query: {} } + if (isPost) { + opts.body = await request.json() + } + const headers: HeadersInit = {} + if (isScriteText) { + headers["content-type"] = "text/javascript" + } + if (isPost) { + headers["content-type"] = "application/json" + } + if (module && module[fnName]) { + if (typeof module[fnName] === "string") { + const result = module[fnName] + return new Response(result, { + status: 200, + headers: Object.keys(headers).length ? headers : undefined, + }) + } + if (typeof module[fnName] === "function") { + let result = await module[fnName](opts) + if (typeof result === "object") { + result = JSON.stringify(result) + } + return new Response(result, { + status: 200, + headers: Object.keys(headers).length ? headers : undefined, + }) + } + if (typeof module[fnName] === "object") { + let result = module[fnName] + if (typeof result === "object") { + result = JSON.stringify(result) + } + return new Response(result, { + status: 200, + headers: Object.keys(headers).length ? headers : undefined, + }) } - return net.fetch(request.url, request) + } + return new Response("", { + status: 500, + headers: Object.keys(headers).length ? headers : undefined, + }) + } else if (request.url.startsWith("api://")) { + return new Response("error", { + status: 500, + }) } + return net.fetch(request.url, request) + } } export default Api diff --git a/src/main/modules/api/readme.md b/src/main/modules/api/readme.md index 2fac3e0..fa6a5b7 100644 --- a/src/main/modules/api/readme.md +++ b/src/main/modules/api/readme.md @@ -1,4 +1,3 @@ - ## 资源 - https://juejin.cn/post/7311619723317657611#heading-6 diff --git a/src/main/modules/api/test.ts b/src/main/modules/api/test.ts index 4ce835e..3ab696f 100644 --- a/src/main/modules/api/test.ts +++ b/src/main/modules/api/test.ts @@ -69,4 +69,3 @@ // }; // ses.protocol.interceptBufferProtocol("https", interceptHandler); - diff --git a/src/main/modules/commands/index.ts b/src/main/modules/commands/index.ts index 410934f..45474a7 100644 --- a/src/main/modules/commands/index.ts +++ b/src/main/modules/commands/index.ts @@ -7,99 +7,99 @@ import { isPromise } from "main/utils" import WindowManager from "../window-manager" export default class Commands extends BaseClass { - destroy() { - // TODO - } + destroy() { + // TODO + } - constructor( - @inject(IOC) private _IOC: IOC, - @inject(WindowManager) private _WindowManager: WindowManager, - ) { - super() - } + constructor( + @inject(IOC) private _IOC: IOC, + @inject(WindowManager) private _WindowManager: WindowManager, + ) { + super() + } - private async handleCommand(command: string, ...argus) { - const splitClass = command.split(".") - const run = await this._IOC.getAsync(splitClass[0]) - if (run) { - const result: Promise | any = run[splitClass[1]](...argus) - return [true, result] - } - return [false] + private async handleCommand(command: string, ...argus) { + const splitClass = command.split(".") + const run = await this._IOC.getAsync(splitClass[0]) + if (run) { + const result: Promise | any = run[splitClass[1]](...argus) + return [true, result] } + return [false] + } - public async invoke(command, ...argus) { - const result = await this.handleCommand(command, ...argus) - return result - } + public async invoke(command, ...argus) { + const result = await this.handleCommand(command, ...argus) + return result + } - init() { - ipcMain.addListener("command", async (event, key, command: string, ...argus) => { - // console.log(event.sender); - try { - const [isExist, result] = await this.handleCommand(command, ...argus) - if (isExist) { - if (isPromise(result)) { - result - .then((res: any) => { - event.reply(key, null, res ?? null) - event.returnValue = res ?? null - }) - .catch((err: Error) => { - event.reply(key, err) - event.returnValue = null - }) - } else { - event.reply(key, null, result ?? null) - event.returnValue = result ?? null - } - } else { - event.reply(key, new Error(`不存在该命令:${command}`)) - event.returnValue = null - } - } catch (error) { - event.reply(key, error) + init() { + ipcMain.addListener("command", async (event, key, command: string, ...argus) => { + // console.log(event.sender); + try { + const [isExist, result] = await this.handleCommand(command, ...argus) + if (isExist) { + if (isPromise(result)) { + result + .then((res: any) => { + event.reply(key, null, res ?? null) + event.returnValue = res ?? null + }) + .catch((err: Error) => { + event.reply(key, err) event.returnValue = null - } - }) - - ipcMain.on("x_popup_menu", (_, name: string, options: IPopupMenuOption) => { - const menu = new Menu() - const readMenu = (items: IMenuItemOption[]) => { - return items.map(opt => { - if (typeof opt._click_evt === "string") { - const evt: string = opt._click_evt - opt.click = () => { - // broadcast(evt) - this.sendMessage(name, evt) - } - } - if (opt.submenu && Array.isArray(opt.submenu)) { - opt.submenu = readMenu(opt.submenu) - } + }) + } else { + event.reply(key, null, result ?? null) + event.returnValue = result ?? null + } + } else { + event.reply(key, new Error(`不存在该命令:${command}`)) + event.returnValue = null + } + } catch (error) { + event.reply(key, error) + event.returnValue = null + } + }) - return opt - }) + ipcMain.on("x_popup_menu", (_, name: string, options: IPopupMenuOption) => { + const menu = new Menu() + const readMenu = (items: IMenuItemOption[]) => { + return items.map(opt => { + if (typeof opt._click_evt === "string") { + const evt: string = opt._click_evt + opt.click = () => { + // broadcast(evt) + this.sendMessage(name, evt) } - const arrays = readMenu(options.items) - - arrays.forEach(v => { - const item = new MenuItem(v) - menu.append(item) - }) + } + if (opt.submenu && Array.isArray(opt.submenu)) { + opt.submenu = readMenu(opt.submenu) + } - menu.on("menu-will-close", () => { - this.sendMessage(name, `popup_menu_close:${options.menu_id}`) - // broadcast(`popup_menu_close:${options.menu_id}`) - }) - menu.popup(options.popupOptions) + return opt }) - } + } + const arrays = readMenu(options.items) - sendMessage(name: string, evt: string, ...argu: any[]) { - const win = this._WindowManager.get(name) - if (win) { - win.webContents.send(evt, ...argu) - } + arrays.forEach(v => { + const item = new MenuItem(v) + menu.append(item) + }) + + menu.on("menu-will-close", () => { + this.sendMessage(name, `popup_menu_close:${options.menu_id}`) + // broadcast(`popup_menu_close:${options.menu_id}`) + }) + menu.popup(options.popupOptions) + }) + } + + sendMessage(name: string, evt: string, ...argu: any[]) { + const win = this._WindowManager.get(name) + if (win) { + win.webContents.send(evt, ...argu) } + } } diff --git a/src/main/modules/db/custom.ts b/src/main/modules/db/custom.ts index 14c5f23..a932712 100644 --- a/src/main/modules/db/custom.ts +++ b/src/main/modules/db/custom.ts @@ -3,31 +3,31 @@ import { Low } from "lowdb" import fs from "fs-extra" export class CustomAdapter extends JSONFile { - constructor(filepath: string) { - super(filepath) - this.filepath = filepath + constructor(filepath: string) { + super(filepath) + this.filepath = filepath + } + filepath: string = "" + async read() { + if (!fs.existsSync(this.filepath)) { + return null } - filepath: string = "" - async read() { - if (!fs.existsSync(this.filepath)) { - return null - } - const data = fs.readJSONSync(this.filepath, { throws: false }) - if (!data) { - return null - } - return data + const data = fs.readJSONSync(this.filepath, { throws: false }) + if (!data) { + return null } + return data + } - async write(data: T) { - fs.ensureFileSync(this.filepath) - await super.write(data) - } + async write(data: T) { + fs.ensureFileSync(this.filepath) + await super.write(data) + } } export class CustomLow extends Low { - constructor(adapter: CustomAdapter, defaultData: T) { - super(adapter, defaultData) - this.filepath = adapter.filepath - } - filepath: string = "" + constructor(adapter: CustomAdapter, defaultData: T) { + super(adapter, defaultData) + this.filepath = adapter.filepath + } + filepath: string = "" } diff --git a/src/main/modules/db/index.ts b/src/main/modules/db/index.ts index 1f13419..78fd619 100644 --- a/src/main/modules/db/index.ts +++ b/src/main/modules/db/index.ts @@ -9,73 +9,73 @@ const debug = _debug("app:db") @injectable() class DB extends BaseClass { - destroy() { - debug(`DB destroy`) - } - Modules: Record> = {} + destroy() { + debug(`DB destroy`) + } + Modules: Record> = {} - constructor(@inject(Setting) private _setting: Setting) { - super() - } + constructor(@inject(Setting) private _setting: Setting) { + super() + } - init() { - console.log("DB Init") - } + init() { + console.log("DB Init") + } - create(filepath) { - const adapter = new CustomAdapter(filepath) - const db = new CustomLow(adapter, {}) - db.filepath = filepath - return db - } + create(filepath) { + const adapter = new CustomAdapter(filepath) + const db = new CustomLow(adapter, {}) + db.filepath = filepath + return db + } - getDB(dbName: string) { - if (this.Modules[dbName] === undefined) { - const filepath = path.resolve(this._setting.values("storagePath"), "./db/" + dbName + ".json") - this.Modules[dbName] = this.create(filepath) - return this.Modules[dbName] - } else { - const cur = this.Modules[dbName] - const filepath = path.resolve(this._setting.values("storagePath"), "./db/" + dbName + ".json") - if (cur.filepath != filepath) { - this.Modules[dbName] = this.create(filepath) - } - return this.Modules[dbName] - } + getDB(dbName: string) { + if (this.Modules[dbName] === undefined) { + const filepath = path.resolve(this._setting.values("storagePath"), "./db/" + dbName + ".json") + this.Modules[dbName] = this.create(filepath) + return this.Modules[dbName] + } else { + const cur = this.Modules[dbName] + const filepath = path.resolve(this._setting.values("storagePath"), "./db/" + dbName + ".json") + if (cur.filepath != filepath) { + this.Modules[dbName] = this.create(filepath) + } + return this.Modules[dbName] } + } - async saveData(data: any): Promise - async saveData(dbName: string, data: any): Promise - async saveData(dbName: string, data?: any): Promise { - let db, rData - if (arguments.length === 2) { - db = this.getDB(dbName) - rData = data - } else { - db = this.getDB("db") - rData = dbName - } - if (db) { - db.data = rData - await db.write() - return db.data - } - return null + async saveData(data: any): Promise + async saveData(dbName: string, data: any): Promise + async saveData(dbName: string, data?: any): Promise { + let db, rData + if (arguments.length === 2) { + db = this.getDB(dbName) + rData = data + } else { + db = this.getDB("db") + rData = dbName + } + if (db) { + db.data = rData + await db.write() + return db.data } + return null + } - async getData(dbName?: string) { - let db - if (dbName) { - db = this.getDB(dbName) - } else { - db = this.getDB("db") - } - if (db) { - await db.read() - return db.data - } - return null + async getData(dbName?: string) { + let db + if (dbName) { + db = this.getDB(dbName) + } else { + db = this.getDB("db") + } + if (db) { + await db.read() + return db.data } + return null + } } export default DB diff --git a/src/main/modules/setting/index.ts b/src/main/modules/setting/index.ts index 0ba1dee..e3f11ff 100644 --- a/src/main/modules/setting/index.ts +++ b/src/main/modules/setting/index.ts @@ -18,217 +18,217 @@ let storagePath = path.join(app.getPath("documents"), Config.app_title) const storagePathDev = path.join(app.getPath("documents"), Config.app_title + "-dev") if (process.env.NODE_ENV === "development") { - storagePath = storagePathDev + storagePath = storagePathDev } const _tempConfig = cloneDeep(Config.default_config as IConfig) Object.keys(_tempConfig).forEach(key => { - if (typeof _tempConfig[key] === "string" && _tempConfig[key].includes("$storagePath$")) { - _tempConfig[key] = _tempConfig[key].replace(/\$storagePath\$/g, storagePath) - if (_tempConfig[key] && path.isAbsolute(_tempConfig[key])) { - _tempConfig[key] = path.normalize(_tempConfig[key]) - } + if (typeof _tempConfig[key] === "string" && _tempConfig[key].includes("$storagePath$")) { + _tempConfig[key] = _tempConfig[key].replace(/\$storagePath\$/g, storagePath) + if (_tempConfig[key] && path.isAbsolute(_tempConfig[key])) { + _tempConfig[key] = path.normalize(_tempConfig[key]) } + } }) function isPath(str) { - // 使用正则表达式检查字符串是否以斜杠或盘符开头 - return /^(?:\/|[a-zA-Z]:\\)/.test(str) + // 使用正则表达式检查字符串是否以斜杠或盘符开头 + return /^(?:\/|[a-zA-Z]:\\)/.test(str) } function init(config: IConfig) { - // 在配置初始化后执行 - Object.keys(config).forEach(key => { - if (config[key] && isPath(config[key]) && path.isAbsolute(config[key])) { - fs.ensureDirSync(config[key]) - } - }) - // 在配置初始化后执行 - // fs.ensureDirSync(config["snippet.storagePath"]) - // fs.ensureDirSync(config["bookmark.storagePath"]) + // 在配置初始化后执行 + Object.keys(config).forEach(key => { + if (config[key] && isPath(config[key]) && path.isAbsolute(config[key])) { + fs.ensureDirSync(config[key]) + } + }) + // 在配置初始化后执行 + // fs.ensureDirSync(config["snippet.storagePath"]) + // fs.ensureDirSync(config["bookmark.storagePath"]) } // 判断是否是空文件夹 function isEmptyDir(fPath: string) { - const pa = fs.readdirSync(fPath) - if (pa.length === 0) { - return true - } else { - return false - } + const pa = fs.readdirSync(fPath) + if (pa.length === 0) { + return true + } else { + return false + } } @injectable() class Setting extends BaseClass { - constructor() { - super() - debug(`Setting inited`) - this.init() - } - - destroy() { - // TODO - } - - #cb: [IT, IOnFunc][] = [] - - onChange(fn: IOnFunc, that?: any) - onChange(key: IT, fn: IOnFunc, that?: any) - onChange(fnOrType: IT | IOnFunc, fnOrThat: IOnFunc | any = null, that: any = null) { - if (typeof fnOrType === "function") { - this.#cb.push(["_", fnOrType.bind(fnOrThat)]) - } else { - this.#cb.push([fnOrType, fnOrThat.bind(that)]) - } + constructor() { + super() + debug(`Setting inited`) + this.init() + } + + destroy() { + // TODO + } + + #cb: [IT, IOnFunc][] = [] + + onChange(fn: IOnFunc, that?: any) + onChange(key: IT, fn: IOnFunc, that?: any) + onChange(fnOrType: IT | IOnFunc, fnOrThat: IOnFunc | any = null, that: any = null) { + if (typeof fnOrType === "function") { + this.#cb.push(["_", fnOrType.bind(fnOrThat)]) + } else { + this.#cb.push([fnOrType, fnOrThat.bind(that)]) } - - #runCB(n: IConfig, c: IConfig, keys: (keyof IConfig)[]) { - for (let i = 0; i < this.#cb.length; i++) { - const temp = this.#cb[i] - const k = temp[0] - const fn = temp[1] - if (k === "_") { - fn(n, c, keys) - } - if (typeof k === "string" && keys.includes(k as keyof IConfig)) { - fn(n, c) - } - if (Array.isArray(k) && k.filter(v => keys.indexOf(v) !== -1).length) { - fn(n, c) - } - } + } + + #runCB(n: IConfig, c: IConfig, keys: (keyof IConfig)[]) { + for (let i = 0; i < this.#cb.length; i++) { + const temp = this.#cb[i] + const k = temp[0] + const fn = temp[1] + if (k === "_") { + fn(n, c, keys) + } + if (typeof k === "string" && keys.includes(k as keyof IConfig)) { + fn(n, c) + } + if (Array.isArray(k) && k.filter(v => keys.indexOf(v) !== -1).length) { + fn(n, c) + } } - - #pathFile: string = - process.env.NODE_ENV === "development" - ? path.resolve(app.getPath("userData"), "./config_path-dev") - : path.resolve(app.getPath("userData"), "./config_path") - #config: IConfig = cloneDeep(_tempConfig) - #configPath(storagePath?: string): string { - return path.join(storagePath || this.#config.storagePath, "./config.json") + } + + #pathFile: string = + process.env.NODE_ENV === "development" + ? path.resolve(app.getPath("userData"), "./config_path-dev") + : path.resolve(app.getPath("userData"), "./config_path") + #config: IConfig = cloneDeep(_tempConfig) + #configPath(storagePath?: string): string { + return path.join(storagePath || this.#config.storagePath, "./config.json") + } + /** + * 读取配置文件变量同步 + * @param confingPath 配置文件路径 + */ + #syncVar(confingPath?: string) { + const configFile = this.#configPath(confingPath) + if (!fs.pathExistsSync(configFile)) { + fs.ensureFileSync(configFile) + fs.writeJSONSync(configFile, {}) } - /** - * 读取配置文件变量同步 - * @param confingPath 配置文件路径 - */ - #syncVar(confingPath?: string) { - const configFile = this.#configPath(confingPath) - if (!fs.pathExistsSync(configFile)) { - fs.ensureFileSync(configFile) - fs.writeJSONSync(configFile, {}) - } - const config = fs.readJSONSync(configFile) as IConfig - confingPath && (config.storagePath = confingPath) - // 优先取本地的值 - for (const key in config) { - // if (Object.prototype.hasOwnProperty.call(this.#config, key)) { - // this.#config[key] = config[key] || this.#config[key] - // } - // 删除配置时本地的配置不会改变,想一下哪种方式更好 - this.#config[key] = config[key] || this.#config[key] - } + const config = fs.readJSONSync(configFile) as IConfig + confingPath && (config.storagePath = confingPath) + // 优先取本地的值 + for (const key in config) { + // if (Object.prototype.hasOwnProperty.call(this.#config, key)) { + // this.#config[key] = config[key] || this.#config[key] + // } + // 删除配置时本地的配置不会改变,想一下哪种方式更好 + this.#config[key] = config[key] || this.#config[key] } - init() { - debug(`位置:${this.#pathFile}`) - - if (fs.pathExistsSync(this.#pathFile)) { - const confingPath = fs.readFileSync(this.#pathFile, { encoding: "utf8" }) - if (confingPath && fs.pathExistsSync(this.#configPath(confingPath))) { - this.#syncVar(confingPath) - // 防止增加了配置本地却没变的情况 - this.#sync(confingPath) - } else { - this.#syncVar(confingPath) - this.#sync(confingPath) - } - } else { - this.#syncVar() - this.#sync() - } - init.call(this, this.#config) + } + init() { + debug(`位置:${this.#pathFile}`) + + if (fs.pathExistsSync(this.#pathFile)) { + const confingPath = fs.readFileSync(this.#pathFile, { encoding: "utf8" }) + if (confingPath && fs.pathExistsSync(this.#configPath(confingPath))) { + this.#syncVar(confingPath) + // 防止增加了配置本地却没变的情况 + this.#sync(confingPath) + } else { + this.#syncVar(confingPath) + this.#sync(confingPath) + } + } else { + this.#syncVar() + this.#sync() } - config() { - return this.#config + init.call(this, this.#config) + } + config() { + return this.#config + } + #sync(c?: string) { + const config = cloneDeep(this.#config) + delete config.storagePath + const p = this.#configPath(c) + fs.ensureFileSync(p) + fs.writeJSONSync(this.#configPath(c), config) + } + #change(p: string) { + const storagePath = this.#config.storagePath + if (fs.existsSync(storagePath) && !fs.existsSync(p)) { + fs.moveSync(storagePath, p) } - #sync(c?: string) { - const config = cloneDeep(this.#config) - delete config.storagePath - const p = this.#configPath(c) - fs.ensureFileSync(p) - fs.writeJSONSync(this.#configPath(c), config) + if (fs.existsSync(p) && fs.existsSync(storagePath) && isEmptyDir(p)) { + fs.moveSync(storagePath, p, { overwrite: true }) } - #change(p: string) { - const storagePath = this.#config.storagePath - if (fs.existsSync(storagePath) && !fs.existsSync(p)) { - fs.moveSync(storagePath, p) - } - if (fs.existsSync(p) && fs.existsSync(storagePath) && isEmptyDir(p)) { - fs.moveSync(storagePath, p, { overwrite: true }) + fs.writeFileSync(this.#pathFile, p, { encoding: "utf8" }) + } + reset(key: keyof IConfig) { + this.set(key, cloneDeep(_tempConfig[key])) + } + set(key: keyof IConfig | Partial, value?: any) { + const oldMainConfig = Object.assign({}, this.#config) + let isChange = false + const changeKeys: (keyof IConfig)[] = [] + const canChangeStorage = (targetPath: string) => { + if (fs.existsSync(oldMainConfig.storagePath) && fs.existsSync(targetPath) && !isEmptyDir(targetPath)) { + if (fs.existsSync(path.join(targetPath, "./config.json"))) { + return true } - fs.writeFileSync(this.#pathFile, p, { encoding: "utf8" }) - } - reset(key: keyof IConfig) { - this.set(key, cloneDeep(_tempConfig[key])) + return false + } + return true } - set(key: keyof IConfig | Partial, value?: any) { - const oldMainConfig = Object.assign({}, this.#config) - let isChange = false - const changeKeys: (keyof IConfig)[] = [] - const canChangeStorage = (targetPath: string) => { - if (fs.existsSync(oldMainConfig.storagePath) && fs.existsSync(targetPath) && !isEmptyDir(targetPath)) { - if (fs.existsSync(path.join(targetPath, "./config.json"))) { - return true - } - return false - } - return true - } - if (typeof key === "string") { - if (value != undefined && value !== this.#config[key]) { - if (key === "storagePath") { - if (!canChangeStorage(value)) { - throw "无法改变存储地址" - return - } - this.#change(value) - changeKeys.push("storagePath") - this.#config["storagePath"] = value - } else { - changeKeys.push(key) - this.#config[key as string] = value - } - isChange = true - } + if (typeof key === "string") { + if (value != undefined && value !== this.#config[key]) { + if (key === "storagePath") { + if (!canChangeStorage(value)) { + throw "无法改变存储地址" + return + } + this.#change(value) + changeKeys.push("storagePath") + this.#config["storagePath"] = value } else { - if (key["storagePath"] !== undefined && key["storagePath"] !== this.#config["storagePath"]) { - if (!canChangeStorage(key["storagePath"])) { - throw "无法改变存储地址" - return - } - this.#change(key["storagePath"]) - this.#config["storagePath"] = key["storagePath"] - changeKeys.push("storagePath") - isChange = true - } - for (const _ in key) { - if (Object.prototype.hasOwnProperty.call(key, _)) { - const v = key[_] - if (v != undefined && _ !== "storagePath" && v !== this.#config[_]) { - this.#config[_] = v - changeKeys.push(_ as keyof IConfig) - isChange = true - } - } - } + changeKeys.push(key) + this.#config[key as string] = value + } + isChange = true + } + } else { + if (key["storagePath"] !== undefined && key["storagePath"] !== this.#config["storagePath"]) { + if (!canChangeStorage(key["storagePath"])) { + throw "无法改变存储地址" + return } - if (isChange) { - this.#sync() - this.#runCB(this.#config, oldMainConfig, changeKeys) + this.#change(key["storagePath"]) + this.#config["storagePath"] = key["storagePath"] + changeKeys.push("storagePath") + isChange = true + } + for (const _ in key) { + if (Object.prototype.hasOwnProperty.call(key, _)) { + const v = key[_] + if (v != undefined && _ !== "storagePath" && v !== this.#config[_]) { + this.#config[_] = v + changeKeys.push(_ as keyof IConfig) + isChange = true + } } + } } - values(key: T): IConfig[T] { - return this.#config[key] + if (isChange) { + this.#sync() + this.#runCB(this.#config, oldMainConfig, changeKeys) } + } + values(key: T): IConfig[T] { + return this.#config[key] + } } export default Setting diff --git a/src/main/modules/tabs/Constant.ts b/src/main/modules/tabs/Constant.ts index 07b51f9..130260d 100644 --- a/src/main/modules/tabs/Constant.ts +++ b/src/main/modules/tabs/Constant.ts @@ -1,6 +1,6 @@ export function Layout(width, height) { - // Tab布局位置 - const NavbarHeight = 30 - const OffsetHeight = NavbarHeight + 100 - return { x: 0, y: OffsetHeight, width: width, height: height - OffsetHeight } + // Tab布局位置 + const NavbarHeight = 30 + const OffsetHeight = NavbarHeight + 100 + return { x: 0, y: OffsetHeight, width: width, height: height - OffsetHeight } } diff --git a/src/main/modules/tabs/Tab.ts b/src/main/modules/tabs/Tab.ts index c9207db..9f7b1ab 100644 --- a/src/main/modules/tabs/Tab.ts +++ b/src/main/modules/tabs/Tab.ts @@ -9,282 +9,282 @@ import { fileURLToPath, pathToFileURL } from "node:url" const debug = _debug("app:tab") interface IOption { - url: string - active: boolean + url: string + active: boolean } interface IRect { - x: number - y: number - width: number - height: number + x: number + y: number + width: number + height: number } class Tab extends BaseClass { - init() { - // TODO - } - public url: string = "" - public showUrl: string = "" - public title: string = "" - public favicons: string[] = [] - public active: boolean = false - public alive: boolean = false - public isDestory: boolean = false - public playing: boolean = false - public visible: boolean = false - private webContentsView: WebContentsView | null = null - private curWindow: BrowserWindow | null = null - private curRect: - | { - x: number - y: number - width: number - height: number - } - | undefined = undefined + init() { + // TODO + } + public url: string = "" + public showUrl: string = "" + public title: string = "" + public favicons: string[] = [] + public active: boolean = false + public alive: boolean = false + public isDestory: boolean = false + public playing: boolean = false + public visible: boolean = false + private webContentsView: WebContentsView | null = null + private curWindow: BrowserWindow | null = null + private curRect: + | { + x: number + y: number + width: number + height: number + } + | undefined = undefined - private defaultOptions: IOption = { - url: "", - active: false, - } + private defaultOptions: IOption = { + url: "", + active: false, + } - private options: IOption + private options: IOption - get isActive() { - return this.active - } + get isActive() { + return this.active + } - get events() { - return this._events - } + get events() { + return this._events + } - constructor(options = {}, window: BrowserWindow, curRect?: IRect) { - super() - this.listenResize = this.listenResize.bind(this) - this.options = { - ...this.defaultOptions, - ...options, - } - this.url = this.getUrl(this.options.url) - this.showUrl = this.options.url - this.curWindow = window - this.curRect = curRect - this.setActive(this.options.active) + constructor(options = {}, window: BrowserWindow, curRect?: IRect) { + super() + this.listenResize = this.listenResize.bind(this) + this.options = { + ...this.defaultOptions, + ...options, } - destroyTimer: NodeJS.Timeout | null = null - stopDestroyTimer() { - if (this.destroyTimer !== null) { - clearTimeout(this.destroyTimer) - this.destroyTimer = null - } - } - startDestroyTimer() { - this.stopDestroyTimer() - if (this.visible) return - if (this.playing) return - this.destroyTimer = setTimeout(() => { - if (this.webContentsView && !this.webContentsView.webContents.isDestroyed()) { - this.curWindow?.contentView.removeChildView(this.webContentsView!) - // @ts-ignore 超过8s没有激活的tab就销毁 - this.webContentsView.webContents.destroy() - this.webContentsView = null - this.alive = false - this.events.emit("update") - } - }, 8000) + this.url = this.getUrl(this.options.url) + this.showUrl = this.options.url + this.curWindow = window + this.curRect = curRect + this.setActive(this.options.active) + } + destroyTimer: NodeJS.Timeout | null = null + stopDestroyTimer() { + if (this.destroyTimer !== null) { + clearTimeout(this.destroyTimer) + this.destroyTimer = null } + } + startDestroyTimer() { + this.stopDestroyTimer() + if (this.visible) return + if (this.playing) return + this.destroyTimer = setTimeout(() => { + if (this.webContentsView && !this.webContentsView.webContents.isDestroyed()) { + this.curWindow?.contentView.removeChildView(this.webContentsView!) + // @ts-ignore 超过8s没有激活的tab就销毁 + this.webContentsView.webContents.destroy() + this.webContentsView = null + this.alive = false + this.events.emit("update") + } + }, 8000) + } - setActive(active: boolean) { - if (!active) { - if (!this.webContentsView) return - this.curWindow!.removeListener("resize", this.listenResize) - this.webContentsView.setVisible(false) - this.visible = false - this.startDestroyTimer() - } else { - this.stopDestroyTimer() - this.visible = true - if (!this.webContentsView) { - this.create() - // , this.curWindow!.contentView.children.length - 1 - this.curWindow!.contentView.addChildView(this.webContentsView!) - this.alive = true - this.events.emit("update") - } - this.listenResize() - this.curWindow!.addListener("resize", this.listenResize) - this.webContentsView!.setVisible(true) - } - this.active = active + setActive(active: boolean) { + if (!active) { + if (!this.webContentsView) return + this.curWindow!.removeListener("resize", this.listenResize) + this.webContentsView.setVisible(false) + this.visible = false + this.startDestroyTimer() + } else { + this.stopDestroyTimer() + this.visible = true + if (!this.webContentsView) { + this.create() + // , this.curWindow!.contentView.children.length - 1 + this.curWindow!.contentView.addChildView(this.webContentsView!) + this.alive = true + this.events.emit("update") + } + this.listenResize() + this.curWindow!.addListener("resize", this.listenResize) + this.webContentsView!.setVisible(true) } + this.active = active + } - openDevtool() { - if (!this.webContentsView) return - this.webContentsView.webContents.openDevTools({ - mode: "right", - }) - } + openDevtool() { + if (!this.webContentsView) return + this.webContentsView.webContents.openDevTools({ + mode: "right", + }) + } - reload() { - if (!this.webContentsView) return - this.webContentsView.webContents.reload() + reload() { + if (!this.webContentsView) return + this.webContentsView.webContents.reload() + } + + create() { + let securityAttr: Partial = {} + if (this.url.startsWith("file:")) { + // 预加载脚本 + securityAttr = { + preload: join(__dirname, "../preload/index.mjs"), + sandbox: false, + } } + this.webContentsView = new WebContentsView({ + webPreferences: { + sandbox: true, + webSecurity: true, + allowRunningInsecureContent: false, + nodeIntegration: false, + spellcheck: false, + contextIsolation: true, + ...securityAttr, + }, + }) + const webContents = this.webContentsView.webContents + this.webContentsView.webContents.loadURL(this.url) + // this.webContentsView.webContents.executeJavaScript(` + // const click = (x,y)=>{ + // const ev = new MouseEvent("click", { + // view: window, + // bubbles: true, + // cancelable: true, + // screenX: x, + // screenY: y + // }) - create() { - let securityAttr: Partial = {} - if (this.url.startsWith("file:")) { - // 预加载脚本 - securityAttr = { - preload: join(__dirname, "../preload/index.mjs"), - sandbox: false, - } - } - this.webContentsView = new WebContentsView({ - webPreferences: { - sandbox: true, - webSecurity: true, - allowRunningInsecureContent: false, - nodeIntegration: false, - spellcheck: false, - contextIsolation: true, - ...securityAttr, - }, - }) - const webContents = this.webContentsView.webContents - this.webContentsView.webContents.loadURL(this.url) - // this.webContentsView.webContents.executeJavaScript(` - // const click = (x,y)=>{ - // const ev = new MouseEvent("click", { - // view: window, - // bubbles: true, - // cancelable: true, - // screenX: x, - // screenY: y - // }) + // const el = document.elementFromPoint(x,y); + // console.log(el) + // el.dispatchEvent(ev); + // } + // console.log("点击初始化完成") + // `) + this.webContentsView.webContents.setWindowOpenHandler(ev => { + debug(ev) + this.events.emit("window-open", ev) + return { action: "deny" } + }) + webContents.addListener("media-paused", () => { + this.playing = false + this.startDestroyTimer() + }) + webContents.addListener("media-started-playing", () => { + this.playing = true + this.stopDestroyTimer() + }) + webContents.addListener("did-finish-load", () => { + this.url = webContents.getURL() + this.showUrl = this.getShowUrl(this.url) + this.events.emit("update") + }) + webContents.addListener("did-navigate-in-page", () => { + this.url = webContents.getURL() + this.showUrl = this.getShowUrl(this.url) + this.events.emit("update") + }) + webContents.addListener("page-title-updated", (_, title) => { + this.title = title + debug(`tab页更新:`, title) + this.events.emit("update") + }) + webContents.addListener("page-favicon-updated", (_, favicons) => { + this.favicons = favicons + debug(favicons) + this.events.emit("update") + }) + // 待机的销毁,但不去除实例 + webContents.addListener("destroyed", () => { + this.#destoryWebContentsView() + }) + } - // const el = document.elementFromPoint(x,y); - // console.log(el) - // el.dispatchEvent(ev); - // } - // console.log("点击初始化完成") - // `) - this.webContentsView.webContents.setWindowOpenHandler(ev => { - debug(ev) - this.events.emit("window-open", ev) - return { action: "deny" } - }) - webContents.addListener("media-paused", () => { - this.playing = false - this.startDestroyTimer() - }) - webContents.addListener("media-started-playing", () => { - this.playing = true - this.stopDestroyTimer() - }) - webContents.addListener("did-finish-load", () => { - this.url = webContents.getURL() - this.showUrl = this.getShowUrl(this.url) - this.events.emit("update") - }) - webContents.addListener("did-navigate-in-page", () => { - this.url = webContents.getURL() - this.showUrl = this.getShowUrl(this.url) - this.events.emit("update") - }) - webContents.addListener("page-title-updated", (_, title) => { - this.title = title - debug(`tab页更新:`, title) - this.events.emit("update") - }) - webContents.addListener("page-favicon-updated", (_, favicons) => { - this.favicons = favicons - debug(favicons) - this.events.emit("update") - }) - // 待机的销毁,但不去除实例 - webContents.addListener("destroyed", () => { - this.#destoryWebContentsView() - }) + print() { + return { + url: this.url, + showUrl: this.showUrl, } + } - print() { - return { - url: this.url, - showUrl: this.showUrl, - } + private getUrl(url) { + if (url === "about:blank") { + debug(FuckHTML) + return pathToFileURL(FuckHTML).href } + return url + } - private getUrl(url) { - if (url === "about:blank") { - debug(FuckHTML) - return pathToFileURL(FuckHTML).href - } - return url + private getShowUrl(url) { + try { + if (fileURLToPath(url) === FuckHTML) { + debug(url) + debug(FuckHTML) + return "about:blank" + } + } catch (error) { + // ignore } + return url + } - private getShowUrl(url) { - try { - if (fileURLToPath(url) === FuckHTML) { - debug(url) - debug(FuckHTML) - return "about:blank" - } - } catch (error) { - // ignore - } - return url + listenResize() { + if (!this.curWindow) { + return } - - listenResize() { - if (!this.curWindow) { - return - } - if (!this.webContentsView) { - return - } - if (!this.curRect) { - return - } - this.webContentsView.setBounds(this.curRect) - // const size = this.curWindow.getContentSize() - // this.webContentsView.setBounds(Layout(size[0], size[1])) + if (!this.webContentsView) { + return } - - updateRect(curRect: IRect) { - this.curRect = curRect - if (!this.webContentsView) { - return - } - this.webContentsView.setBounds(this.curRect) + if (!this.curRect) { + return } + this.webContentsView.setBounds(this.curRect) + // const size = this.curWindow.getContentSize() + // this.webContentsView.setBounds(Layout(size[0], size[1])) + } - navigate(url: string) { - if (!this.webContentsView) return - this.webContentsView.webContents.loadURL(this.getUrl(url)) + updateRect(curRect: IRect) { + this.curRect = curRect + if (!this.webContentsView) { + return } + this.webContentsView.setBounds(this.curRect) + } - #destoryWebContentsView() { - this.stopDestroyTimer() - if (this.webContentsView && this.curWindow && !this.curWindow.isDestroyed()) { - this.curWindow.contentView.removeChildView(this.webContentsView) - this.curWindow.removeListener("resize", this.listenResize) - } - if (this.webContentsView && !this.webContentsView.webContents.isDestroyed()) { - this.webContentsView.webContents.removeAllListeners() - this.webContentsView.removeAllListeners() - // @ts-ignore 超过8s没有激活的tab就销毁 - this.webContentsView.webContents.destroy() - } - this.webContentsView = null - } + navigate(url: string) { + if (!this.webContentsView) return + this.webContentsView.webContents.loadURL(this.getUrl(url)) + } - destroy() { - this.#destoryWebContentsView() - this.events.removeAllListeners() - this.isDestory = true - debug("Tab destroy") + #destoryWebContentsView() { + this.stopDestroyTimer() + if (this.webContentsView && this.curWindow && !this.curWindow.isDestroyed()) { + this.curWindow.contentView.removeChildView(this.webContentsView) + this.curWindow.removeListener("resize", this.listenResize) + } + if (this.webContentsView && !this.webContentsView.webContents.isDestroyed()) { + this.webContentsView.webContents.removeAllListeners() + this.webContentsView.removeAllListeners() + // @ts-ignore 超过8s没有激活的tab就销毁 + this.webContentsView.webContents.destroy() } + this.webContentsView = null + } + + destroy() { + this.#destoryWebContentsView() + this.events.removeAllListeners() + this.isDestory = true + debug("Tab destroy") + } } export { Tab } diff --git a/src/main/modules/tabs/index.ts b/src/main/modules/tabs/index.ts index 2e62f6d..c60643e 100644 --- a/src/main/modules/tabs/index.ts +++ b/src/main/modules/tabs/index.ts @@ -5,121 +5,121 @@ import { BrowserWindow } from "electron" import EventEmitter from "events" interface IRect { - x: number - y: number - width: number - height: number + x: number + y: number + width: number + height: number } const debug = _debug("app:tabs") class Tabs extends BaseClass { - destroy() { - this._tabs.forEach(v => v.destroy()) - this._tabs = [] + destroy() { + this._tabs.forEach(v => v.destroy()) + this._tabs = [] + } + + public events = new EventEmitter() + + private curRect: + | { + x: number + y: number + width: number + height: number + } + | undefined = undefined + + constructor() { + super() + } + + _tabs: Tab[] = [] + + init() { + // 初始化 + } + + updateRect(curRect: IRect) { + this.curRect = curRect + this._tabs.forEach(tab => { + tab.updateRect(curRect) + }) + } + + add(url: string, active: boolean, win: BrowserWindow) { + const tab = new Tab({ url }, win, this.curRect) + tab.events.on("window-open", ev => { + debug(ev) + this.add(ev.url, true, win) + this.events.emit("update") + // tab.navigate(ev.url) + }) + tab.events.on("update", () => { + this.events.emit("update") + }) + this._tabs.push(tab) + if (active) { + this.changeActive(this._tabs.length - 1) } - - public events = new EventEmitter() - - private curRect: - | { - x: number - y: number - width: number - height: number - } - | undefined = undefined - - constructor() { - super() - } - - _tabs: Tab[] = [] - - init() { - // 初始化 - } - - updateRect(curRect: IRect) { - this.curRect = curRect - this._tabs.forEach(tab => { - tab.updateRect(curRect) - }) - } - - add(url: string, active: boolean, win: BrowserWindow) { - const tab = new Tab({ url }, win, this.curRect) - tab.events.on("window-open", ev => { - debug(ev) - this.add(ev.url, true, win) - this.events.emit("update") - // tab.navigate(ev.url) - }) - tab.events.on("update", () => { - this.events.emit("update") - }) - this._tabs.push(tab) - if (active) { - this.changeActive(this._tabs.length - 1) - } - this.events.emit("update") - } - - changeActive(index: number) { - this._tabs.forEach((tab, i) => { - tab.setActive(i === index) - }) - this.events.emit("update", index) - } - - openDevtool(index: number) { - if (this._tabs[index]) { - this._tabs[index].openDevtool() - } - } - - reload(index: number) { - if (this._tabs[index]) { - this._tabs[index].reload() - } - } - - navigate(index: number, url: string) { - if (this._tabs[index]) { - this._tabs[index].navigate(url) - } + this.events.emit("update") + } + + changeActive(index: number) { + this._tabs.forEach((tab, i) => { + tab.setActive(i === index) + }) + this.events.emit("update", index) + } + + openDevtool(index: number) { + if (this._tabs[index]) { + this._tabs[index].openDevtool() } + } - remove(index: number) { - this._tabs[index].destroy() - if (this._tabs[index].isActive && index - 1 >= 0) { - this.changeActive(index - 1) - } - this._tabs.splice(index, 1) - this.events.emit("update") + reload(index: number) { + if (this._tabs[index]) { + this._tabs[index].reload() } + } - closeAll() { - this._tabs = this._tabs.filter(tab => { - tab.destroy() - }) - this._tabs = [] - this.events.emit("update") + navigate(index: number, url: string) { + if (this._tabs[index]) { + this._tabs[index].navigate(url) } + } - removeAll(index: number[]) { - index - .map(v => { - return this._tabs[+v] - }) - .forEach(tab => { - tab.destroy() - }) - this._tabs = this._tabs.filter(v => { - return !v.isDestory - }) - this.events.emit("update") + remove(index: number) { + this._tabs[index].destroy() + if (this._tabs[index].isActive && index - 1 >= 0) { + this.changeActive(index - 1) } + this._tabs.splice(index, 1) + this.events.emit("update") + } + + closeAll() { + this._tabs = this._tabs.filter(tab => { + tab.destroy() + }) + this._tabs = [] + this.events.emit("update") + } + + removeAll(index: number[]) { + index + .map(v => { + return this._tabs[+v] + }) + .forEach(tab => { + tab.destroy() + }) + this._tabs = this._tabs.filter(v => { + return !v.isDestory + }) + this.events.emit("update") + } } export { Tabs } diff --git a/src/main/modules/updater/hot/index.ts b/src/main/modules/updater/hot/index.ts index f43a7e1..7e77cf6 100644 --- a/src/main/modules/updater/hot/index.ts +++ b/src/main/modules/updater/hot/index.ts @@ -10,15 +10,15 @@ import _debug from "debug" const debug = _debug("app:hot-updater") function getUpdateScriptTemplate() { - return process.platform === "win32" - ? ` + return process.platform === "win32" + ? ` @echo off timeout /t 2 taskkill /IM "{{EXE_NAME}}" /F xcopy /Y /E "{{UPDATE_DIR}}\\*" "{{APP_PATH}}" start "" "{{EXE_PATH}}" ` - : ` + : ` #!/bin/bash sleep 2 pkill -f "{{EXE_NAME}}" @@ -28,15 +28,15 @@ function getUpdateScriptTemplate() { } function generateUpdateScript() { - const scriptContent = getUpdateScriptTemplate() - .replace(/{{APP_PATH}}/g, process.platform === "win32" ? "%APP_PATH%" : "$APP_PATH") - .replace(/{{UPDATE_DIR}}/g, process.platform === "win32" ? "%UPDATE_DIR%" : "$UPDATE_DIR") - .replace(/{{EXE_PATH}}/g, process.platform === "win32" ? "%EXE_PATH%" : "$EXE_PATH") - .replace(/{{EXE_NAME}}/g, process.platform === "win32" ? "%EXE_NAME%" : "$EXE_NAME") + const scriptContent = getUpdateScriptTemplate() + .replace(/{{APP_PATH}}/g, process.platform === "win32" ? "%APP_PATH%" : "$APP_PATH") + .replace(/{{UPDATE_DIR}}/g, process.platform === "win32" ? "%UPDATE_DIR%" : "$UPDATE_DIR") + .replace(/{{EXE_PATH}}/g, process.platform === "win32" ? "%EXE_PATH%" : "$EXE_PATH") + .replace(/{{EXE_NAME}}/g, process.platform === "win32" ? "%EXE_NAME%" : "$EXE_NAME") - const scriptPath = path.join(os.tmpdir(), `update.${process.platform === "win32" ? "bat" : "sh"}`) - fs.writeFileSync(scriptPath, scriptContent) - return scriptPath + const scriptPath = path.join(os.tmpdir(), `update.${process.platform === "win32" ? "bat" : "sh"}`) + fs.writeFileSync(scriptPath, scriptContent) + return scriptPath } // 标记是否需要热更新 let shouldPerformHotUpdate = false @@ -45,74 +45,74 @@ let isReadyUpdate = false // 使用应用名称和随机字符串创建唯一的临时目录 const updateTempDirPath = path.join(os.tmpdir(), `${app.getName()}-update-${Math.random().toString(36).substring(2, 15)}`) app.once("will-quit", event => { - if (!shouldPerformHotUpdate) return - event.preventDefault() - const appPath = app.getAppPath() - const appExePath = process.execPath - const exeName = path.basename(appExePath) - // 生成动态脚本 - const scriptPath = generateUpdateScript() + if (!shouldPerformHotUpdate) return + event.preventDefault() + const appPath = app.getAppPath() + const appExePath = process.execPath + const exeName = path.basename(appExePath) + // 生成动态脚本 + const scriptPath = generateUpdateScript() - fs.chmodSync(scriptPath, 0o755) + fs.chmodSync(scriptPath, 0o755) - // 执行脚本 - const child = spawn(scriptPath, [], { - detached: true, - shell: true, - env: { - APP_PATH: appPath, - UPDATE_DIR: updateTempDirPath, - EXE_PATH: appExePath, - EXE_NAME: exeName, - }, - }) - child.unref() - app.exit() + // 执行脚本 + const child = spawn(scriptPath, [], { + detached: true, + shell: true, + env: { + APP_PATH: appPath, + UPDATE_DIR: updateTempDirPath, + EXE_PATH: appExePath, + EXE_NAME: exeName, + }, + }) + child.unref() + app.exit() }) // 下载热更新包 export async function fetchHotUpdatePackage(updatePackageUrl: string = "https://example.com/updates/latest.zip") { - if (isReadyUpdate) return + if (isReadyUpdate) return - // 清除临时目录 - clearUpdateTempDir() - // 创建临时目录 - if (!fs.existsSync(updateTempDirPath)) { - fs.mkdirSync(updateTempDirPath, { recursive: true }) - } + // 清除临时目录 + clearUpdateTempDir() + // 创建临时目录 + if (!fs.existsSync(updateTempDirPath)) { + fs.mkdirSync(updateTempDirPath, { recursive: true }) + } - // 下载文件的本地保存路径 - const downloadPath = path.join(updateTempDirPath, "update.zip") + // 下载文件的本地保存路径 + const downloadPath = path.join(updateTempDirPath, "update.zip") - try { - // 使用 fetch 下载更新包 - const response = await fetch(updatePackageUrl) - if (!response.ok) { - throw new Error(`下载失败: ${response.status} ${response.statusText}`) - } + try { + // 使用 fetch 下载更新包 + const response = await fetch(updatePackageUrl) + if (!response.ok) { + throw new Error(`下载失败: ${response.status} ${response.statusText}`) + } - // 将下载内容写入文件 - const arrayBuffer = await response.arrayBuffer() - fs.writeFileSync(downloadPath, Buffer.from(arrayBuffer)) + // 将下载内容写入文件 + const arrayBuffer = await response.arrayBuffer() + fs.writeFileSync(downloadPath, Buffer.from(arrayBuffer)) - // 解压更新包 - await extract(downloadPath, { dir: updateTempDirPath }) + // 解压更新包 + await extract(downloadPath, { dir: updateTempDirPath }) - // 删除下载的zip文件 - fs.unlinkSync(downloadPath) - isReadyUpdate = true - emitHotUpdateReady() - } catch (error) { - debug("热更新包下载失败:", error) - throw error - } + // 删除下载的zip文件 + fs.unlinkSync(downloadPath) + isReadyUpdate = true + emitHotUpdateReady() + } catch (error) { + debug("热更新包下载失败:", error) + throw error + } } function clearUpdateTempDir() { - if (!fs.existsSync(updateTempDirPath)) return - fs.rmSync(updateTempDirPath, { recursive: true }) + if (!fs.existsSync(updateTempDirPath)) return + fs.rmSync(updateTempDirPath, { recursive: true }) } export function flagNeedUpdate() { - shouldPerformHotUpdate = true + shouldPerformHotUpdate = true } diff --git a/src/main/modules/updater/index.ts b/src/main/modules/updater/index.ts index 8f94b54..81d9924 100644 --- a/src/main/modules/updater/index.ts +++ b/src/main/modules/updater/index.ts @@ -13,121 +13,121 @@ const { autoUpdater } = pkg @injectable() export class Updater extends BaseClass { - public events = new EventEmitter() - private timer: ReturnType | null = null - // autoReplace = false - async triggerHotUpdate(autoReplace = false) { - await fetchHotUpdatePackage() - flagNeedUpdate() - if (!autoReplace) { - dialog.showMessageBox({ - title: Locales.t("update.ready.hot.title"), - message: Locales.t("update.ready.hot.desc", { version: app.getVersion() }), - }) - } else { - app.quit() - } + public events = new EventEmitter() + private timer: ReturnType | null = null + // autoReplace = false + async triggerHotUpdate(autoReplace = false) { + await fetchHotUpdatePackage() + flagNeedUpdate() + if (!autoReplace) { + dialog.showMessageBox({ + title: Locales.t("update.ready.hot.title"), + message: Locales.t("update.ready.hot.desc", { version: app.getVersion() }), + }) + } else { + app.quit() } - - constructor() { - super() - - // 配置自动更新 - autoUpdater.autoDownload = false - autoUpdater.autoInstallOnAppQuit = true - - // 检查更新错误 - autoUpdater.on("error", error => { - debug("Update error:", error) - }) - - // 检查更新 - autoUpdater.on("checking-for-update", () => { - debug("Checking for updates...") - }) - - // 有可用更新 - autoUpdater.on("update-available", info => { - debug("Update available:", info) - this.promptUserToUpdate() - }) - - // 没有可用更新 - autoUpdater.on("update-not-available", info => { - debug("Update not available:", info) - }) - - // 更新下载进度 - autoUpdater.on("download-progress", progressObj => { - debug( - `Download speed: ${progressObj.bytesPerSecond} - Downloaded ${progressObj.percent}% (${progressObj.transferred}/${progressObj.total})`, - ) - }) - - // 更新下载完成 - autoUpdater.on("update-downloaded", info => { - debug("Update downloaded:", info) - this.promptUserToInstall() - }) - } - - init() { - // 定期检查更新 + } + + constructor() { + super() + + // 配置自动更新 + autoUpdater.autoDownload = false + autoUpdater.autoInstallOnAppQuit = true + + // 检查更新错误 + autoUpdater.on("error", error => { + debug("Update error:", error) + }) + + // 检查更新 + autoUpdater.on("checking-for-update", () => { + debug("Checking for updates...") + }) + + // 有可用更新 + autoUpdater.on("update-available", info => { + debug("Update available:", info) + this.promptUserToUpdate() + }) + + // 没有可用更新 + autoUpdater.on("update-not-available", info => { + debug("Update not available:", info) + }) + + // 更新下载进度 + autoUpdater.on("download-progress", progressObj => { + debug( + `Download speed: ${progressObj.bytesPerSecond} - Downloaded ${progressObj.percent}% (${progressObj.transferred}/${progressObj.total})`, + ) + }) + + // 更新下载完成 + autoUpdater.on("update-downloaded", info => { + debug("Update downloaded:", info) + this.promptUserToInstall() + }) + } + + init() { + // 定期检查更新 + this.checkForUpdates() + this.timer && clearInterval(this.timer) + this.timer = setInterval( + () => { this.checkForUpdates() - this.timer && clearInterval(this.timer) - this.timer = setInterval( - () => { - this.checkForUpdates() - }, - 1000 * 60 * 60, - ) // 每小时检查一次 - } - - destroy() { - // 清理工作 - if (this.timer) { - clearInterval(this.timer) - this.timer = null - } + }, + 1000 * 60 * 60, + ) // 每小时检查一次 + } + + destroy() { + // 清理工作 + if (this.timer) { + clearInterval(this.timer) + this.timer = null } - - private async checkForUpdates() { - if (app.isPackaged) { - try { - await autoUpdater.checkForUpdates() - } catch (error) { - debug("Failed to check for updates:", error) - } - } + } + + private async checkForUpdates() { + if (app.isPackaged) { + try { + await autoUpdater.checkForUpdates() + } catch (error) { + debug("Failed to check for updates:", error) + } } - - private async promptUserToUpdate() { - const result = await dialog.showMessageBox({ - type: "info", - title: "发现新版本", - message: "是否下载新版本?", - buttons: ["下载", "暂不更新"], - defaultId: 0, - }) - - if (result.response === 0) { - autoUpdater.downloadUpdate() - } + } + + private async promptUserToUpdate() { + const result = await dialog.showMessageBox({ + type: "info", + title: "发现新版本", + message: "是否下载新版本?", + buttons: ["下载", "暂不更新"], + defaultId: 0, + }) + + if (result.response === 0) { + autoUpdater.downloadUpdate() } - - private async promptUserToInstall() { - const result = await dialog.showMessageBox({ - type: "info", - title: "更新已就绪", - message: "新版本已下载完成,是否立即安装?", - buttons: ["立即安装", "稍后安装"], - defaultId: 0, - }) - - if (result.response === 0) { - autoUpdater.quitAndInstall(false, true) - } + } + + private async promptUserToInstall() { + const result = await dialog.showMessageBox({ + type: "info", + title: "更新已就绪", + message: "新版本已下载完成,是否立即安装?", + buttons: ["立即安装", "稍后安装"], + defaultId: 0, + }) + + if (result.response === 0) { + autoUpdater.quitAndInstall(false, true) } + } } export default Updater diff --git a/src/main/modules/window-manager/index.ts b/src/main/modules/window-manager/index.ts index da9c350..e84c24a 100644 --- a/src/main/modules/window-manager/index.ts +++ b/src/main/modules/window-manager/index.ts @@ -8,353 +8,353 @@ import _debug from "debug" const debug = _debug("app:window-manager") declare module "electron" { - interface BrowserWindow { - $$forceClose?: boolean - $$lastChoice?: number - $$opts?: Param - } + interface BrowserWindow { + $$forceClose?: boolean + $$lastChoice?: number + $$opts?: Param + } } export { WindowManager } export default class WindowManager extends BaseClass { - constructor() { - super() - this.isMainShowReady = new Promise(resolve => { - this.isMainShowResolve = resolve - }) - } + constructor() { + super() + this.isMainShowReady = new Promise(resolve => { + this.isMainShowResolve = resolve + }) + } - destroy() { - // TODO + destroy() { + // TODO + } + globalChioce: number = -1 + #showWin(info: Param) { + if (this.#windows.length >= 6) { + dialog.showErrorBox("错误", "窗口数量超出限制") + return } - globalChioce: number = -1 - #showWin(info: Param) { - if (this.#windows.length >= 6) { - dialog.showErrorBox("错误", "窗口数量超出限制") - return - } - if (!info.name) { - dialog.showErrorBox("错误", "窗口未指定唯一key") - return - } - const index = this.findIndex(info.name) - if (index === -1) { - this.#windows.push(this.#add(info)) - } else { - if (this.#windows[index].isDestroyed()) { - this.#windows[index] = this.#add(info) - } else { - if (info.url && info.loadURLInSameWin) { - this.#windows[index].loadURL(info.url) - } - this.#windows[index].show() - } + if (!info.name) { + dialog.showErrorBox("错误", "窗口未指定唯一key") + return + } + const index = this.findIndex(info.name) + if (index === -1) { + this.#windows.push(this.#add(info)) + } else { + if (this.#windows[index].isDestroyed()) { + this.#windows[index] = this.#add(info) + } else { + if (info.url && info.loadURLInSameWin) { + this.#windows[index].loadURL(info.url) } - this.showCurrentWindow() + this.#windows[index].show() + } } + this.showCurrentWindow() + } - showMainWindow() { - this.#showWin(this.mainInfo) - this.isMainShowResolve() - } + showMainWindow() { + this.#showWin(this.mainInfo) + this.isMainShowResolve() + } - private isMainShowResolve - private isMainShowReady - async waitMainShowReady() { - await this.isMainShowReady - } + private isMainShowResolve + private isMainShowReady + async waitMainShowReady() { + await this.isMainShowReady + } - showWindow(name: string, opts?: Partial) { - let have = false - for (const key in this.#urlMap) { - const info = this.#urlMap[key] - if (new RegExp(key).test(name)) { - opts && merge(info, opts) - info.name = name - if (!info.ignoreEmptyUrl && !info.url) { - dialog.showErrorBox("错误", name + "窗口未提供url") - return - } - this.#showWin(info as Param) - have = true - } - } - if (!have) { - dialog.showErrorBox("错误", name + "窗口未创建成功") - return + showWindow(name: string, opts?: Partial) { + let have = false + for (const key in this.#urlMap) { + const info = this.#urlMap[key] + if (new RegExp(key).test(name)) { + opts && merge(info, opts) + info.name = name + if (!info.ignoreEmptyUrl && !info.url) { + dialog.showErrorBox("错误", name + "窗口未提供url") + return } + this.#showWin(info as Param) + have = true + } } - - init() { - /** - * 当应用被激活时触发 - */ - app.on("activate", () => { - this.showMainWindow() - }) - // Default open or close DevTools by F12 in development - // and ignore CommandOrControl + R in production. - // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils - app.on("browser-window-created", (_, window) => { - optimizer.watchWindowShortcuts(window) - }) - /** - * 应用程序开始关闭时回调,可以通过event.preventDefault()阻止,以下两点需要注意: - * 1. 如果是autoUpdater.quitAndInstall()关闭的,那么会所有窗口关闭,并且在close事件之后执行 - * 2. 关机,重启,用户退出时不会触发 - */ - app.on("before-quit", (event: Electron.Event) => { - const mainWin = this.get(this.mainInfo.name) - if (!mainWin || (mainWin && mainWin?.$$forceClose)) { - // app.exit() - } else { - event.preventDefault() - } - }) - - app.on("window-all-closed", () => { - if (process.platform !== "darwin") { - app.quit() - } - }) + if (!have) { + dialog.showErrorBox("错误", name + "窗口未创建成功") + return } + } - #urlMap = getWindowsMap() + init() { + /** + * 当应用被激活时触发 + */ + app.on("activate", () => { + this.showMainWindow() + }) + // Default open or close DevTools by F12 in development + // and ignore CommandOrControl + R in production. + // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils + app.on("browser-window-created", (_, window) => { + optimizer.watchWindowShortcuts(window) + }) + /** + * 应用程序开始关闭时回调,可以通过event.preventDefault()阻止,以下两点需要注意: + * 1. 如果是autoUpdater.quitAndInstall()关闭的,那么会所有窗口关闭,并且在close事件之后执行 + * 2. 关机,重启,用户退出时不会触发 + */ + app.on("before-quit", (event: Electron.Event) => { + const mainWin = this.get(this.mainInfo.name) + if (!mainWin || (mainWin && mainWin?.$$forceClose)) { + // app.exit() + } else { + event.preventDefault() + } + }) - getWndows() { - return this.#windows - } + app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit() + } + }) + } - length() { - return this.#windows.length - } + #urlMap = getWindowsMap() - public get mainInfo() { - return this.#urlMap["main"] as Param - } + getWndows() { + return this.#windows + } - #windows: BrowserWindow[] = [] + length() { + return this.#windows.length + } - #defaultConfig: IConfig = defaultConfig + public get mainInfo() { + return this.#urlMap["main"] as Param + } - #add(config: Param) { - const curConfig = cloneDeep(this.#defaultConfig ?? {}) - for (const key in config) { - if (Object.prototype.hasOwnProperty.call(config, key)) { - const value = config[key] - // if (Reflect.has(curConfig, key)) { - curConfig[key] = value - // } - } - } - const privateConfig = merge(curConfig.overideWindowOpts ? {} : cloneDeep(defaultWindowConfig), curConfig.windowOpts ?? {}) - let parentWindow - if (typeof privateConfig.parent === "string") { - parentWindow = this.get(privateConfig.parent) - } - if (parentWindow) { - privateConfig.parent = parentWindow - } - const browserWin = new BrowserWindow(privateConfig) - browserWin.webContents.setWindowOpenHandler(() => { - if (curConfig.denyWindowOpen) { - return { action: "deny" } - } - return { action: "allow" } - }) - // @ts-ignore 不需要解释为啥 - browserWin.webContents.$$senderName = curConfig.name - browserWin.$$forceClose = false - browserWin.$$lastChoice = -1 - browserWin.on("close", (event: any) => { - if (this.globalChioce === 1) { - this.#onClose(curConfig.name) - return - } - if (!curConfig.confrimWindowClose) { - this.#onClose(curConfig.name) - return - } - // eslint-disable-next-line @typescript-eslint/no-this-alias - const that = this - function justQuit() { - browserWin.$$lastChoice = 1 - // app.quit() - // 不要用quit();试了会弹两次 - browserWin.$$forceClose = true - if (curConfig.name === that.mainInfo.name) { - that.globalChioce = 1 - app.quit() // exit()直接关闭客户端,不会执行quit(); - } else { - that.delete(curConfig.name) - that.showCurrentWindow() - } - } - if (browserWin.$$forceClose) { - that.delete(curConfig.name) - app.quit() - } else { - let choice = -1 - if (browserWin && browserWin!.$$lastChoice !== undefined && browserWin.$$lastChoice >= 0) { - choice = browserWin.$$lastChoice - } else { - choice = dialog.showMessageBoxSync(browserWin, { - type: "info", - title: curConfig.confrimWindowCloseText.title, - defaultId: curConfig.confrimWindowCloseText.defaultId, - cancelId: curConfig.confrimWindowCloseText.cancelId, - message: curConfig.confrimWindowCloseText.message, - buttons: curConfig.confrimWindowCloseText.buttons, - }) - } - if (choice === 1) { - justQuit() - } else { - event && event.preventDefault() - } - } - }) - browserWin.$$opts = curConfig - // 在此注册窗口 - browserWin.webContents.addListener("did-finish-load", () => { - browserWin.webContents.executeJavaScript(`window._global=${JSON.stringify({ name: curConfig.name })};`) - browserWin.webContents.send("bind-window-manager", curConfig.name) - }) - // https://www.electronjs.org/zh/docs/latest/tutorial/security#12-%E5%88%9B%E5%BB%BAwebview%E5%89%8D%E7%A1%AE%E8%AE%A4%E5%85%B6%E9%80%89%E9%A1%B9 - // browserWin.webContents.on("will-attach-webview", (_event, webPreferences) => { - // if (webPreferences.preload !== path.resolve(app.getAppPath(), "webview.js")) { - // // 如果未使用,则删除预加载脚本或验证其位置是否合法 - // delete webPreferences.preload - // } + #windows: BrowserWindow[] = [] - // // 禁用 Node.js 集成 - // webPreferences.nodeIntegration = false + #defaultConfig: IConfig = defaultConfig - // // 验证正在加载的 URL - // // if (!params.src.startsWith('https://example.com/')) { - // // event.preventDefault() - // // } - // }) - if (curConfig.type === "info") { - // 隐藏菜单 - browserWin.setMenuBarVisibility(false) - } - if (curConfig.url) { - browserWin.loadURL(curConfig.url) - // logger.debug(`当前窗口网址:${curConfig.url}`) + #add(config: Param) { + const curConfig = cloneDeep(this.#defaultConfig ?? {}) + for (const key in config) { + if (Object.prototype.hasOwnProperty.call(config, key)) { + const value = config[key] + // if (Reflect.has(curConfig, key)) { + curConfig[key] = value + // } + } + } + const privateConfig = merge(curConfig.overideWindowOpts ? {} : cloneDeep(defaultWindowConfig), curConfig.windowOpts ?? {}) + let parentWindow + if (typeof privateConfig.parent === "string") { + parentWindow = this.get(privateConfig.parent) + } + if (parentWindow) { + privateConfig.parent = parentWindow + } + const browserWin = new BrowserWindow(privateConfig) + browserWin.webContents.setWindowOpenHandler(() => { + if (curConfig.denyWindowOpen) { + return { action: "deny" } + } + return { action: "allow" } + }) + // @ts-ignore 不需要解释为啥 + browserWin.webContents.$$senderName = curConfig.name + browserWin.$$forceClose = false + browserWin.$$lastChoice = -1 + browserWin.on("close", (event: any) => { + if (this.globalChioce === 1) { + this.#onClose(curConfig.name) + return + } + if (!curConfig.confrimWindowClose) { + this.#onClose(curConfig.name) + return + } + // eslint-disable-next-line @typescript-eslint/no-this-alias + const that = this + function justQuit() { + browserWin.$$lastChoice = 1 + // app.quit() + // 不要用quit();试了会弹两次 + browserWin.$$forceClose = true + if (curConfig.name === that.mainInfo.name) { + that.globalChioce = 1 + app.quit() // exit()直接关闭客户端,不会执行quit(); + } else { + that.delete(curConfig.name) + that.showCurrentWindow() } - if (curConfig.windowOpts?.show === false) { - if (curConfig.url) { - browserWin.once("ready-to-show", () => { - debug(`准备展示:`, curConfig.url) - browserWin?.show() - }) - } else { - browserWin?.show() - } + } + if (browserWin.$$forceClose) { + that.delete(curConfig.name) + app.quit() + } else { + let choice = -1 + if (browserWin && browserWin!.$$lastChoice !== undefined && browserWin.$$lastChoice >= 0) { + choice = browserWin.$$lastChoice + } else { + choice = dialog.showMessageBoxSync(browserWin, { + type: "info", + title: curConfig.confrimWindowCloseText.title, + defaultId: curConfig.confrimWindowCloseText.defaultId, + cancelId: curConfig.confrimWindowCloseText.cancelId, + message: curConfig.confrimWindowCloseText.message, + buttons: curConfig.confrimWindowCloseText.buttons, + }) } - return browserWin - } - - showCurrentWindow() { - if (this.#windows.length) { - debug(`current open window: ${this.#windows.map(v => v.$$opts!.name).join(",")}`) + if (choice === 1) { + justQuit() } else { - debug(`all closed`) + event && event.preventDefault() } - } + } + }) + browserWin.$$opts = curConfig + // 在此注册窗口 + browserWin.webContents.addListener("did-finish-load", () => { + browserWin.webContents.executeJavaScript(`window._global=${JSON.stringify({ name: curConfig.name })};`) + browserWin.webContents.send("bind-window-manager", curConfig.name) + }) + // https://www.electronjs.org/zh/docs/latest/tutorial/security#12-%E5%88%9B%E5%BB%BAwebview%E5%89%8D%E7%A1%AE%E8%AE%A4%E5%85%B6%E9%80%89%E9%A1%B9 + // browserWin.webContents.on("will-attach-webview", (_event, webPreferences) => { + // if (webPreferences.preload !== path.resolve(app.getAppPath(), "webview.js")) { + // // 如果未使用,则删除预加载脚本或验证其位置是否合法 + // delete webPreferences.preload + // } - #onClose(name: string) { - for (let i = this.#windows.length - 1; i >= 0; i--) { - const win = this.#windows[i] - if (name === win.$$opts!.name) { - win.destroy() - this.#windows.splice(i, 1) - } - } - this.showCurrentWindow() - } + // // 禁用 Node.js 集成 + // webPreferences.nodeIntegration = false - get(name: string) { - return this.#windows.find(v => { - return v.$$opts!.name === name + // // 验证正在加载的 URL + // // if (!params.src.startsWith('https://example.com/')) { + // // event.preventDefault() + // // } + // }) + if (curConfig.type === "info") { + // 隐藏菜单 + browserWin.setMenuBarVisibility(false) + } + if (curConfig.url) { + browserWin.loadURL(curConfig.url) + // logger.debug(`当前窗口网址:${curConfig.url}`) + } + if (curConfig.windowOpts?.show === false) { + if (curConfig.url) { + browserWin.once("ready-to-show", () => { + debug(`准备展示:`, curConfig.url) + browserWin?.show() }) + } else { + browserWin?.show() + } } + return browserWin + } - getFocusWindow() { - const mainWindow = this.getMainWindow() - if (mainWindow?.isFocused()) { - return mainWindow - } - for (let i = 0; i < this.#windows.length; i++) { - const win = this.#windows[i] - if (win.isFocused()) { - return win - } - } - return + showCurrentWindow() { + if (this.#windows.length) { + debug(`current open window: ${this.#windows.map(v => v.$$opts!.name).join(",")}`) + } else { + debug(`all closed`) } + } - getMainWindow() { - return this.#windows.find(v => { - return v.$$opts!.name === this.mainInfo.name - }) + #onClose(name: string) { + for (let i = this.#windows.length - 1; i >= 0; i--) { + const win = this.#windows[i] + if (name === win.$$opts!.name) { + win.destroy() + this.#windows.splice(i, 1) + } } + this.showCurrentWindow() + } - close(name: string | RegExp) { - const indexList = this.findAllIndex(name) - for (let i = indexList.length - 1; i >= 0; i--) { - const index = indexList[i] - const win = this.#windows[index] - win.close() - } + get(name: string) { + return this.#windows.find(v => { + return v.$$opts!.name === name + }) + } + + getFocusWindow() { + const mainWindow = this.getMainWindow() + if (mainWindow?.isFocused()) { + return mainWindow + } + for (let i = 0; i < this.#windows.length; i++) { + const win = this.#windows[i] + if (win.isFocused()) { + return win + } } + return + } - delete(name: string | RegExp) { - const indexList = this.findAllIndex(name) - for (let i = indexList.length - 1; i >= 0; i--) { - const index = indexList[i] - this.#windows.splice(index, 1) - } + getMainWindow() { + return this.#windows.find(v => { + return v.$$opts!.name === this.mainInfo.name + }) + } + + close(name: string | RegExp) { + const indexList = this.findAllIndex(name) + for (let i = indexList.length - 1; i >= 0; i--) { + const index = indexList[i] + const win = this.#windows[index] + win.close() } + } - findIndex(name: string | RegExp) { - const index = this.#windows.findIndex(v => { - if (typeof name === "string") { - return v.$$opts!.name === name - } else { - return name.test(v.$$opts!.name) - } - }) - return index + delete(name: string | RegExp) { + const indexList = this.findAllIndex(name) + for (let i = indexList.length - 1; i >= 0; i--) { + const index = indexList[i] + this.#windows.splice(index, 1) } + } - findAllIndex(name: string | RegExp) { - const result: number[] = [] - for (let i = 0; i < this.#windows.length; i++) { - const win = this.#windows[i] - if (typeof name === "string" && win.$$opts!.name === name) { - result.push(i) - } else if (typeof name !== "string" && name.test(win.$$opts!.name)) { - result.push(i) - } - } - return result + findIndex(name: string | RegExp) { + const index = this.#windows.findIndex(v => { + if (typeof name === "string") { + return v.$$opts!.name === name + } else { + return name.test(v.$$opts!.name) + } + }) + return index + } + + findAllIndex(name: string | RegExp) { + const result: number[] = [] + for (let i = 0; i < this.#windows.length; i++) { + const win = this.#windows[i] + if (typeof name === "string" && win.$$opts!.name === name) { + result.push(i) + } else if (typeof name !== "string" && name.test(win.$$opts!.name)) { + result.push(i) + } } + return result + } - // show(name: string | RegExp) { - // let indexList = this.findAllIndex(name) - // if (!!indexList.length) { - // for (let i = 0; i < indexList.length; i++) { - // const index = indexList[i]; - // const win = this.#windows[index] - // if (win.isDestroyed()) { - // this.#windows[index] = this.#add(win.$$opts) - // } else { - // win.show() - // } - // } - // } else { - // console.warn("该窗口不存在") - // } - // } + // show(name: string | RegExp) { + // let indexList = this.findAllIndex(name) + // if (!!indexList.length) { + // for (let i = 0; i < indexList.length; i++) { + // const index = indexList[i]; + // const win = this.#windows[index] + // if (win.isDestroyed()) { + // this.#windows[index] = this.#add(win.$$opts) + // } else { + // win.show() + // } + // } + // } else { + // console.warn("该窗口不存在") + // } + // } } diff --git a/src/main/modules/window-manager/windowsMap.ts b/src/main/modules/window-manager/windowsMap.ts index ed30553..182b5b2 100644 --- a/src/main/modules/window-manager/windowsMap.ts +++ b/src/main/modules/window-manager/windowsMap.ts @@ -7,124 +7,124 @@ import { join } from "path" export type Param = Partial & Required> export interface IConfig { - name?: string - url?: string - loadURLInSameWin?: boolean - type?: "info" - windowOpts?: BrowserWindowConstructorOptions - overideWindowOpts?: boolean - ignoreEmptyUrl?: boolean - denyWindowOpen?: boolean - confrimWindowClose?: boolean - confrimWindowCloseText?: { - title: string - message: string - buttons: string[] - defaultId: number - cancelId: number - } + name?: string + url?: string + loadURLInSameWin?: boolean + type?: "info" + windowOpts?: BrowserWindowConstructorOptions + overideWindowOpts?: boolean + ignoreEmptyUrl?: boolean + denyWindowOpen?: boolean + confrimWindowClose?: boolean + confrimWindowCloseText?: { + title: string + message: string + buttons: string[] + defaultId: number + cancelId: number + } } export const defaultConfig: IConfig = { - denyWindowOpen: true, + denyWindowOpen: true, } export const defaultWindowConfig = { - height: 600, - useContentSize: true, - width: 800, - show: true, - resizable: true, - minWidth: 900, - minHeight: 600, - frame: true, - transparent: false, - alwaysOnTop: false, - webPreferences: {}, + height: 600, + useContentSize: true, + width: 800, + show: true, + resizable: true, + minWidth: 900, + minHeight: 600, + frame: true, + transparent: false, + alwaysOnTop: false, + webPreferences: {}, } export function getWindowsMap(): Record { - return { - main: { - name: "main", - url: getFileUrl("index.html"), - confrimWindowClose: true, - confrimWindowCloseText: { - title: config.app_title, - defaultId: 0, - cancelId: 0, - message: "确定要关闭吗?", - buttons: ["没事", "直接退出"], - }, - windowOpts: { - show: false, - titleBarStyle: "hidden", - titleBarOverlay: true, - icon: icon, - ...(process.platform === "linux" ? { icon } : {}), - webPreferences: { - webviewTag: false, - preload: join(__dirname, "../preload/index.mjs"), - nodeIntegration: true, - contextIsolation: true, - }, - }, + return { + main: { + name: "main", + url: getFileUrl("index.html"), + confrimWindowClose: true, + confrimWindowCloseText: { + title: config.app_title, + defaultId: 0, + cancelId: 0, + message: "确定要关闭吗?", + buttons: ["没事", "直接退出"], + }, + windowOpts: { + show: false, + titleBarStyle: "hidden", + titleBarOverlay: true, + icon: icon, + ...(process.platform === "linux" ? { icon } : {}), + webPreferences: { + webviewTag: false, + preload: join(__dirname, "../preload/index.mjs"), + nodeIntegration: true, + contextIsolation: true, }, - _blank: { - overideWindowOpts: false, - confrimWindowClose: true, - confrimWindowCloseText: { - title: config.app_title, - defaultId: 0, - cancelId: 0, - message: "确定要关闭吗?", - buttons: ["没事", "直接退出"], - }, - type: "info", - windowOpts: { - height: 600, - useContentSize: true, - width: 800, - show: true, - resizable: true, - minWidth: 900, - minHeight: 600, - frame: true, - transparent: false, - alwaysOnTop: false, - icon: icon, - title: config.app_title, - webPreferences: { - devTools: false, - sandbox: true, - nodeIntegration: false, - contextIsolation: true, - webviewTag: false, - preload: undefined, - }, - }, + }, + }, + _blank: { + overideWindowOpts: false, + confrimWindowClose: true, + confrimWindowCloseText: { + title: config.app_title, + defaultId: 0, + cancelId: 0, + message: "确定要关闭吗?", + buttons: ["没事", "直接退出"], + }, + type: "info", + windowOpts: { + height: 600, + useContentSize: true, + width: 800, + show: true, + resizable: true, + minWidth: 900, + minHeight: 600, + frame: true, + transparent: false, + alwaysOnTop: false, + icon: icon, + title: config.app_title, + webPreferences: { + devTools: false, + sandbox: true, + nodeIntegration: false, + contextIsolation: true, + webviewTag: false, + preload: undefined, }, - "^about": { - url: getFileUrl("about.html"), - overideWindowOpts: true, - confrimWindowClose: false, - type: "info", - windowOpts: { - width: 600, - height: 200, - minimizable: false, - darkTheme: true, - modal: true, - show: false, - resizable: false, - icon: icon, - webPreferences: { - devTools: false, - sandbox: false, - nodeIntegration: false, - contextIsolation: true, - }, - }, + }, + }, + "^about": { + url: getFileUrl("about.html"), + overideWindowOpts: true, + confrimWindowClose: false, + type: "info", + windowOpts: { + width: 600, + height: 200, + minimizable: false, + darkTheme: true, + modal: true, + show: false, + resizable: false, + icon: icon, + webPreferences: { + devTools: false, + sandbox: false, + nodeIntegration: false, + contextIsolation: true, }, - } + }, + }, + } } diff --git a/src/main/modules/zephyr/index.ts b/src/main/modules/zephyr/index.ts index 510ce8a..b812250 100644 --- a/src/main/modules/zephyr/index.ts +++ b/src/main/modules/zephyr/index.ts @@ -70,407 +70,407 @@ fetch("zephyr://w/path/to/file.txt", { @injectable() class Zephyr extends BaseClass { - // private readonly ALLOWED_PATHS: string[] = [] // 可以在这里定义允许访问的路径白名单 - private readonly ALLOWED_EXTENSIONS: string[] = [".txt", ".json", ".md"] // 允许的文件类型 - private readonly MAX_FILE_SIZE = 50 * 1024 * 1024 // 50MB - // private readonly SAFE_PATH_PATTERN = /^[a-zA-Z0-9\s\-_\/\\:\.]+$/ - - // 定义操作类型 - private readonly OPERATIONS = { - READ: "r", - WRITE: "w", - READWRITE: "rw", - TEMP: "t", - } as const - - private readonly pathConfig: { - read: string[] - temp: string[] - write: string[] - } = { - read: [], - temp: [], - write: [], + // private readonly ALLOWED_PATHS: string[] = [] // 可以在这里定义允许访问的路径白名单 + private readonly ALLOWED_EXTENSIONS: string[] = [".txt", ".json", ".md"] // 允许的文件类型 + private readonly MAX_FILE_SIZE = 50 * 1024 * 1024 // 50MB + // private readonly SAFE_PATH_PATTERN = /^[a-zA-Z0-9\s\-_\/\\:\.]+$/ + + // 定义操作类型 + private readonly OPERATIONS = { + READ: "r", + WRITE: "w", + READWRITE: "rw", + TEMP: "t", + } as const + + private readonly pathConfig: { + read: string[] + temp: string[] + write: string[] + } = { + read: [], + temp: [], + write: [], + } + + // 文件锁定相关 + private readonly fileLocks = new Map() + + // 访问频率限制相关 + private readonly rateLimiter = new Map() + private readonly MAX_REQUESTS = 10 // 每个文件在时间窗口内的最大请求次数 + private readonly WINDOW_MS = 60000 // 时间窗口:1分钟 + + // 审计日志相关 + private readonly LOG_FILE = path.join(app.getPath("logs"), "zephyr-access.log") + + constructor() { + super() + this.interceptHandlerZephyr = this.interceptHandlerZephyr.bind(this) + this.initLogFile() + debug("zephyr init") + } + + private async initLogFile() { + const logDir = path.dirname(this.LOG_FILE) + await fs.promises.mkdir(logDir, { recursive: true }) + } + + // 文件锁定机制 + private async acquireFileLock(filePath: string): Promise { + if (this.fileLocks.get(filePath)) { + return false } - - // 文件锁定相关 - private readonly fileLocks = new Map() - - // 访问频率限制相关 - private readonly rateLimiter = new Map() - private readonly MAX_REQUESTS = 10 // 每个文件在时间窗口内的最大请求次数 - private readonly WINDOW_MS = 60000 // 时间窗口:1分钟 - - // 审计日志相关 - private readonly LOG_FILE = path.join(app.getPath("logs"), "zephyr-access.log") - - constructor() { - super() - this.interceptHandlerZephyr = this.interceptHandlerZephyr.bind(this) - this.initLogFile() - debug("zephyr init") + this.fileLocks.set(filePath, true) + return true + } + + private releaseFileLock(filePath: string): void { + this.fileLocks.delete(filePath) + } + + // 访问频率限制 + private isRateLimited(filePath: string): boolean { + // const now = Date.now() + const count = this.rateLimiter.get(filePath) || 0 + + if (count >= this.MAX_REQUESTS) { + debug("访问频率超限:", filePath) + return true } - private async initLogFile() { - const logDir = path.dirname(this.LOG_FILE) - await fs.promises.mkdir(logDir, { recursive: true }) + this.rateLimiter.set(filePath, count + 1) + setTimeout(() => { + const currentCount = this.rateLimiter.get(filePath) + if (currentCount && currentCount > 0) { + this.rateLimiter.set(filePath, currentCount - 1) + } + }, this.WINDOW_MS) + + return false + } + + // 审计日志 + private async logAccess(operation: string, filePath: string, success: boolean, details?: string) { + const timestamp = new Date().toISOString() + const logEntry = { + timestamp, + operation, + filePath, + success, + details, } - // 文件锁定机制 - private async acquireFileLock(filePath: string): Promise { - if (this.fileLocks.get(filePath)) { - return false - } - this.fileLocks.set(filePath, true) - return true + try { + await fs.promises.appendFile(this.LOG_FILE, JSON.stringify(logEntry) + "\n", "utf8") + } catch (error) { + debug("写入审计日志失败:", error) } - - private releaseFileLock(filePath: string): void { - this.fileLocks.delete(filePath) + } + + // 文件内容验证 + private async validateFileContent(filePath: string): Promise { + try { + const ext = path.extname(filePath).toLowerCase() + const content = await fs.promises.readFile(filePath, "utf8") + + switch (ext) { + case ".json": + JSON.parse(content) + return true + case ".md": + // 可以添加 Markdown 验证逻辑 + return content.length > 0 + case ".txt": + // 文本文件验证 + return content.length > 0 + default: + return false + } + } catch { + return false } + } + + destroy() { + const ses = session.defaultSession + ses.protocol.unhandle("zephyr") + this.fileLocks.clear() + this.rateLimiter.clear() + debug("zephyr destroyed") + } + + init(partition?: string) { + const ses = partition ? session.fromPartition(partition) : session.defaultSession + ses.protocol.handle("zephyr", this.interceptHandlerZephyr) + debug("zephyr initialized with partition:", partition) + } + + setAllowedPaths(config: Partial) { + Object.assign(this.pathConfig, config) + debug("Updated allowed paths:", this.pathConfig) + } + + private isValidPath(filePath: string): boolean { + try { + // 规范化路径 + const normalizedPath = path.normalize(filePath) + + // Windows 路径特殊处理 + const isWindowsPath = /^[a-z]:/i.test(normalizedPath) + + // 检查基本字符(排除特殊字符) + // 允许驱动器冒号,但排除其他特殊字符 + const basicCheck = isWindowsPath ? /^[a-z]:[^<>"|?*]+$/i.test(normalizedPath) : /^[^<>:"|?*]+$/i.test(normalizedPath) + + return basicCheck && (isWindowsPath || normalizedPath.startsWith("/")) + } catch { + return false + } + } - // 访问频率限制 - private isRateLimited(filePath: string): boolean { - // const now = Date.now() - const count = this.rateLimiter.get(filePath) || 0 + private async isPathSafe(filePath: string, operation: string): Promise { + try { + // 1. 基本路径检查 + if (!this.isValidPath(filePath)) { + debug("不安全的路径字符:", filePath) + return false + } - if (count >= this.MAX_REQUESTS) { - debug("访问频率超限:", filePath) - return true - } + // 2. 检查是否包含 .. 路径 + if (filePath.includes("..")) { + debug("检测到路径遍历尝试") + return false + } - this.rateLimiter.set(filePath, count + 1) - setTimeout(() => { - const currentCount = this.rateLimiter.get(filePath) - if (currentCount && currentCount > 0) { - this.rateLimiter.set(filePath, currentCount - 1) - } - }, this.WINDOW_MS) + // 3. 检查符号链接 + if (await this.isSymlink(filePath)) { + debug("不允许访问符号链接") + return false + } + // 4. 检查文件大小 + if (!(await this.checkFileSize(filePath))) { + debug("文件超出大小限制") return false - } + } - // 审计日志 - private async logAccess(operation: string, filePath: string, success: boolean, details?: string) { - const timestamp = new Date().toISOString() - const logEntry = { - timestamp, - operation, - filePath, - success, - details, - } + // 5. 文件类型检查 + const ext = path.extname(filePath).toLowerCase() + if (!this.ALLOWED_EXTENSIONS.includes(ext)) { + debug("不允许的文件类型:", ext) + return false + } - try { - await fs.promises.appendFile(this.LOG_FILE, JSON.stringify(logEntry) + "\n", "utf8") - } catch (error) { - debug("写入审计日志失败:", error) - } - } + // 6. 权限检查 + const allowedPaths = this.getPathsByOperation(operation) + if (!allowedPaths) return false - // 文件内容验证 - private async validateFileContent(filePath: string): Promise { - try { - const ext = path.extname(filePath).toLowerCase() - const content = await fs.promises.readFile(filePath, "utf8") - - switch (ext) { - case ".json": - JSON.parse(content) - return true - case ".md": - // 可以添加 Markdown 验证逻辑 - return content.length > 0 - case ".txt": - // 文本文件验证 - return content.length > 0 - default: - return false - } - } catch { - return false - } - } + // 7. 确保路径在允许范围内 + const isInAllowedPath = allowedPaths.some(allowedPath => { + const resolvedAllowed = path.resolve(allowedPath) + const resolvedTarget = path.resolve(filePath) + return resolvedTarget.startsWith(resolvedAllowed) + }) - destroy() { - const ses = session.defaultSession - ses.protocol.unhandle("zephyr") - this.fileLocks.clear() - this.rateLimiter.clear() - debug("zephyr destroyed") - } + if (!isInAllowedPath) { + debug("路径不在允许范围内") + return false + } - init(partition?: string) { - const ses = partition ? session.fromPartition(partition) : session.defaultSession - ses.protocol.handle("zephyr", this.interceptHandlerZephyr) - debug("zephyr initialized with partition:", partition) - } + // 添加频率限制检查 + if (this.isRateLimited(filePath)) { + await this.logAccess(operation, filePath, false, "访问频率超限") + return false + } - setAllowedPaths(config: Partial) { - Object.assign(this.pathConfig, config) - debug("Updated allowed paths:", this.pathConfig) + // 添加文件内容验证 + if (!(await this.validateFileContent(filePath))) { + await this.logAccess(operation, filePath, false, "文件内容验证失败") + return false + } + + await this.logAccess(operation, filePath, true) + return true + } catch (error: any) { + await this.logAccess(operation, filePath, false, error.message) + debug("路径安全检查错误:", error) + return false } - - private isValidPath(filePath: string): boolean { - try { - // 规范化路径 - const normalizedPath = path.normalize(filePath) - - // Windows 路径特殊处理 - const isWindowsPath = /^[a-z]:/i.test(normalizedPath) - - // 检查基本字符(排除特殊字符) - // 允许驱动器冒号,但排除其他特殊字符 - const basicCheck = isWindowsPath ? /^[a-z]:[^<>"|?*]+$/i.test(normalizedPath) : /^[^<>:"|?*]+$/i.test(normalizedPath) - - return basicCheck && (isWindowsPath || normalizedPath.startsWith("/")) - } catch { - return false - } + } + + async interceptHandlerZephyr(request: Request): Promise { + try { + if (!request.url.startsWith("zephyr://")) { + return net.fetch(request.url, request) + } + + const urlParts = request.url.replace(/^zephyr:\/\//, "").split("/") + const operation = urlParts[0] + const filePath = path.normalize(urlParts.slice(1).join("/")) + + if (!operation || !filePath) { + return new Response("Invalid URL format", { status: 400 }) + } + + if (!(await this.isPathSafe(filePath, operation))) { + debug("访问被拒绝:", filePath) + return new Response("Access Denied", { status: 403 }) + } + + // 处理不同的操作类型 + switch (operation) { + case this.OPERATIONS.READ: + return await this.handleReadOperation(filePath) + case this.OPERATIONS.WRITE: + return await this.handleWriteOperation(filePath, request) + default: + return new Response("Operation not supported", { status: 400 }) + } + } catch (error) { + debug("处理请求错误:", error) + return new Response("Internal Server Error", { status: 500 }) } - - private async isPathSafe(filePath: string, operation: string): Promise { - try { - // 1. 基本路径检查 - if (!this.isValidPath(filePath)) { - debug("不安全的路径字符:", filePath) - return false - } - - // 2. 检查是否包含 .. 路径 - if (filePath.includes("..")) { - debug("检测到路径遍历尝试") - return false - } - - // 3. 检查符号链接 - if (await this.isSymlink(filePath)) { - debug("不允许访问符号链接") - return false - } - - // 4. 检查文件大小 - if (!(await this.checkFileSize(filePath))) { - debug("文件超出大小限制") - return false - } - - // 5. 文件类型检查 - const ext = path.extname(filePath).toLowerCase() - if (!this.ALLOWED_EXTENSIONS.includes(ext)) { - debug("不允许的文件类型:", ext) - return false - } - - // 6. 权限检查 - const allowedPaths = this.getPathsByOperation(operation) - if (!allowedPaths) return false - - // 7. 确保路径在允许范围内 - const isInAllowedPath = allowedPaths.some(allowedPath => { - const resolvedAllowed = path.resolve(allowedPath) - const resolvedTarget = path.resolve(filePath) - return resolvedTarget.startsWith(resolvedAllowed) - }) - - if (!isInAllowedPath) { - debug("路径不在允许范围内") - return false - } - - // 添加频率限制检查 - if (this.isRateLimited(filePath)) { - await this.logAccess(operation, filePath, false, "访问频率超限") - return false - } - - // 添加文件内容验证 - if (!(await this.validateFileContent(filePath))) { - await this.logAccess(operation, filePath, false, "文件内容验证失败") - return false - } - - await this.logAccess(operation, filePath, true) - return true - } catch (error: any) { - await this.logAccess(operation, filePath, false, error.message) - debug("路径安全检查错误:", error) - return false - } + } + + private async handleReadOperation(filePath: string): Promise { + const cleanup = async (error?: Error): Promise => { + this.releaseFileLock(filePath) + if (error) { + await this.logAccess("READ", filePath, false, error.message) + return new Response("Internal Server Error", { status: 500 }) + } + return new Response("OK", { status: 200 }) } - async interceptHandlerZephyr(request: Request): Promise { - try { - if (!request.url.startsWith("zephyr://")) { - return net.fetch(request.url, request) - } - - const urlParts = request.url.replace(/^zephyr:\/\//, "").split("/") - const operation = urlParts[0] - const filePath = path.normalize(urlParts.slice(1).join("/")) - - if (!operation || !filePath) { - return new Response("Invalid URL format", { status: 400 }) - } - - if (!(await this.isPathSafe(filePath, operation))) { - debug("访问被拒绝:", filePath) - return new Response("Access Denied", { status: 403 }) - } - - // 处理不同的操作类型 - switch (operation) { - case this.OPERATIONS.READ: - return await this.handleReadOperation(filePath) - case this.OPERATIONS.WRITE: - return await this.handleWriteOperation(filePath, request) - default: - return new Response("Operation not supported", { status: 400 }) - } - } catch (error) { - debug("处理请求错误:", error) - return new Response("Internal Server Error", { status: 500 }) - } + try { + if (!(await this.acquireFileLock(filePath))) { + await this.logAccess("READ", filePath, false, "文件已锁定") + return new Response("File is locked", { status: 423 }) + } + + const stream = fs.createReadStream(filePath, { + flags: "r", + encoding: "utf8", + }) + + const timeout = setTimeout(() => { + stream.destroy() + cleanup(new Error("读取超时")) + }, 5000) + + const response = new Response(stream as any, { + status: 200, + headers: { + "content-type": this.getContentType(filePath), + "cache-control": "no-cache", + }, + }) + + stream.on("error", error => { + clearTimeout(timeout) + cleanup(error) + }) + + stream.on("end", () => { + clearTimeout(timeout) + cleanup() + }) + + return response + } catch (error) { + return await cleanup(error as Error) + } + } + + private getContentType(filePath: string): string { + const ext = path.extname(filePath).toLowerCase() + const contentTypes: Record = { + ".txt": "text/plain", + ".json": "application/json", + ".md": "text/markdown", + } + return contentTypes[ext] || "application/octet-stream" + } + + private async isSymlink(filePath: string): Promise { + try { + const stats = await fs.promises.lstat(filePath) + return stats.isSymbolicLink() + } catch { + return false + } + } + + private async checkFileSize(filePath: string): Promise { + try { + const stats = await fs.promises.stat(filePath) + return stats.size <= this.MAX_FILE_SIZE + } catch { + return false + } + } + + private getPathsByOperation(operation: string): string[] | null { + switch (operation) { + case this.OPERATIONS.READ: + return this.pathConfig.read + case this.OPERATIONS.TEMP: + return this.pathConfig.temp + case this.OPERATIONS.WRITE: + return this.pathConfig.write + default: + debug("未知的操作类型:", operation) + return null + } + } + + private async handleWriteOperation(filePath: string, request: Request): Promise { + const cleanup = async (error?: Error): Promise => { + this.releaseFileLock(filePath) + if (error) { + await this.logAccess("WRITE", filePath, false, error.message) + return new Response("Write failed: " + error.message, { status: 500 }) + } + return new Response("Write successful", { status: 200 }) } - private async handleReadOperation(filePath: string): Promise { - const cleanup = async (error?: Error): Promise => { - this.releaseFileLock(filePath) - if (error) { - await this.logAccess("READ", filePath, false, error.message) - return new Response("Internal Server Error", { status: 500 }) - } - return new Response("OK", { status: 200 }) - } + try { + // 1. 获取文件锁 + if (!(await this.acquireFileLock(filePath))) { + return new Response("File is locked", { status: 423 }) + } - try { - if (!(await this.acquireFileLock(filePath))) { - await this.logAccess("READ", filePath, false, "文件已锁定") - return new Response("File is locked", { status: 423 }) - } - - const stream = fs.createReadStream(filePath, { - flags: "r", - encoding: "utf8", - }) - - const timeout = setTimeout(() => { - stream.destroy() - cleanup(new Error("读取超时")) - }, 5000) - - const response = new Response(stream as any, { - status: 200, - headers: { - "content-type": this.getContentType(filePath), - "cache-control": "no-cache", - }, - }) - - stream.on("error", error => { - clearTimeout(timeout) - cleanup(error) - }) - - stream.on("end", () => { - clearTimeout(timeout) - cleanup() - }) - - return response - } catch (error) { - return await cleanup(error as Error) - } - } + // 2. 确保目标目录存在 + await fs.promises.mkdir(path.dirname(filePath), { recursive: true }) - private getContentType(filePath: string): string { - const ext = path.extname(filePath).toLowerCase() - const contentTypes: Record = { - ".txt": "text/plain", - ".json": "application/json", - ".md": "text/markdown", - } - return contentTypes[ext] || "application/octet-stream" - } + // 3. 获取请求内容 + const content = await request.text() - private async isSymlink(filePath: string): Promise { - try { - const stats = await fs.promises.lstat(filePath) - return stats.isSymbolicLink() - } catch { - return false - } - } + // 4. 验证内容大小 + if (content.length > this.MAX_FILE_SIZE) { + return cleanup(new Error("Content too large")) + } - private async checkFileSize(filePath: string): Promise { + // 5. 验证文件类型和内容 + const ext = path.extname(filePath).toLowerCase() + if (ext === ".json") { try { - const stats = await fs.promises.stat(filePath) - return stats.size <= this.MAX_FILE_SIZE + JSON.parse(content) } catch { - return false + return cleanup(new Error("Invalid JSON content")) } - } + } - private getPathsByOperation(operation: string): string[] | null { - switch (operation) { - case this.OPERATIONS.READ: - return this.pathConfig.read - case this.OPERATIONS.TEMP: - return this.pathConfig.temp - case this.OPERATIONS.WRITE: - return this.pathConfig.write - default: - debug("未知的操作类型:", operation) - return null - } - } + // 6. 写入文件 + await fs.promises.writeFile(filePath, content, "utf8") + await this.logAccess("WRITE", filePath, true) - private async handleWriteOperation(filePath: string, request: Request): Promise { - const cleanup = async (error?: Error): Promise => { - this.releaseFileLock(filePath) - if (error) { - await this.logAccess("WRITE", filePath, false, error.message) - return new Response("Write failed: " + error.message, { status: 500 }) - } - return new Response("Write successful", { status: 200 }) - } - - try { - // 1. 获取文件锁 - if (!(await this.acquireFileLock(filePath))) { - return new Response("File is locked", { status: 423 }) - } - - // 2. 确保目标目录存在 - await fs.promises.mkdir(path.dirname(filePath), { recursive: true }) - - // 3. 获取请求内容 - const content = await request.text() - - // 4. 验证内容大小 - if (content.length > this.MAX_FILE_SIZE) { - return cleanup(new Error("Content too large")) - } - - // 5. 验证文件类型和内容 - const ext = path.extname(filePath).toLowerCase() - if (ext === ".json") { - try { - JSON.parse(content) - } catch { - return cleanup(new Error("Invalid JSON content")) - } - } - - // 6. 写入文件 - await fs.promises.writeFile(filePath, content, "utf8") - await this.logAccess("WRITE", filePath, true) - - return cleanup() - } catch (error) { - return cleanup(error as Error) - } + return cleanup() + } catch (error) { + return cleanup(error as Error) } + } } export default Zephyr diff --git a/src/main/utils/index.ts b/src/main/utils/index.ts index 3d43098..a91119f 100644 --- a/src/main/utils/index.ts +++ b/src/main/utils/index.ts @@ -3,27 +3,27 @@ import { join } from "node:path" import { webContents } from "electron" export function getFileUrl(app: string) { - let winURL = "" - if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { - winURL = process.env["ELECTRON_RENDERER_URL"] + `/${app}#/` - } else { - winURL = join(__dirname, `../renderer/${app}#/`) - } - return slash(winURL) + let winURL = "" + if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { + winURL = process.env["ELECTRON_RENDERER_URL"] + `/${app}#/` + } else { + winURL = join(__dirname, `../renderer/${app}#/`) + } + return slash(winURL) } export function isPromise(value: () => any) { - return value && Object.prototype.toString.call(value) === "[object Promise]" + return value && Object.prototype.toString.call(value) === "[object Promise]" } export const broadcast = (event: T, ...args: any[]) => { - webContents.getAllWebContents().forEach(browser => browser.send(event, ...args)) + webContents.getAllWebContents().forEach(browser => browser.send(event, ...args)) } export function slash(path: string) { - const isExtendedLengthPath = path.startsWith("\\\\?\\") - if (isExtendedLengthPath) { - return path - } - return path.replace(/\\/g, "/") + const isExtendedLengthPath = path.startsWith("\\\\?\\") + if (isExtendedLengthPath) { + return path + } + return path.replace(/\\/g, "/") } diff --git a/src/preload/call.ts b/src/preload/call.ts index 35ed73f..fc72239 100644 --- a/src/preload/call.ts +++ b/src/preload/call.ts @@ -2,70 +2,70 @@ import { ipcRenderer } from "electron" let count = 0 export function call(command: string, ...args: any[]): Promise { - return new Promise((resolve, reject) => { - if (!command) { - console.warn("命令不能为空") - return - } - count++ - const timestamp = new Date().getTime() - const key = timestamp + "-" + count - let timeID: any = null - ipcRenderer.once(key, fn) + return new Promise((resolve, reject) => { + if (!command) { + console.warn("命令不能为空") + return + } + count++ + const timestamp = new Date().getTime() + const key = timestamp + "-" + count + let timeID: any = null + ipcRenderer.once(key, fn) - function fn(_, err: any, res: any) { - clearTimeout(timeID) - if (err) { - reject(err) - return - } - resolve(res) - } + function fn(_, err: any, res: any) { + clearTimeout(timeID) + if (err) { + reject(err) + return + } + resolve(res) + } - ipcRenderer.send("command", key, command, ...args) + ipcRenderer.send("command", key, command, ...args) - // 超过5s就取消监听 - timeID = setTimeout(() => { - reject(new Error(`超过5s未响应: ${command}`)) - ipcRenderer.removeListener(key, fn) - }, 5000) - }) + // 超过5s就取消监听 + timeID = setTimeout(() => { + reject(new Error(`超过5s未响应: ${command}`)) + ipcRenderer.removeListener(key, fn) + }, 5000) + }) } export function callLong(command: string, ...args: any[]): Promise { - return new Promise((resolve, reject) => { - if (!command) { - console.warn("命令不能为空") - return - } - count++ - const timestamp = new Date().getTime() - const key = timestamp + "-" + count - ipcRenderer.once(key, fn) - - function fn(_, err: any, res: any) { - if (err) { - reject(err) - return - } - resolve(res) - } - - ipcRenderer.send("command", key, command, ...args) - }) -} - -export function callSync(command: string, ...args: any[]) { + return new Promise((resolve, reject) => { if (!command) { - console.warn("命令不能为空") - return + console.warn("命令不能为空") + return } count++ const timestamp = new Date().getTime() const key = timestamp + "-" + count - const result = ipcRenderer.sendSync("command", key, command, ...args) - if (!result) { + ipcRenderer.once(key, fn) + + function fn(_, err: any, res: any) { + if (err) { + reject(err) return + } + resolve(res) } - return result + + ipcRenderer.send("command", key, command, ...args) + }) +} + +export function callSync(command: string, ...args: any[]) { + if (!command) { + console.warn("命令不能为空") + return + } + count++ + const timestamp = new Date().getTime() + const key = timestamp + "-" + count + const result = ipcRenderer.sendSync("command", key, command, ...args) + if (!result) { + return + } + return result } diff --git a/src/preload/index.ts b/src/preload/index.ts index e3a5855..6e816ed 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -3,8 +3,8 @@ import { electronAPI } from "@electron-toolkit/preload" import { call, callLong, callSync } from "./call" import { IPopupMenuOption } from "#/popup-menu" document.addEventListener("DOMContentLoaded", () => { - const initStyle = document.createElement("style") - initStyle.textContent = ` + const initStyle = document.createElement("style") + initStyle.textContent = ` *, *::before, *::after { @@ -19,59 +19,59 @@ body { // background: #F8F8F8; } ` - document.head.appendChild(initStyle) + document.head.appendChild(initStyle) }) // Custom APIs for renderer const api = { - call, - callLong, - callSync, - send(command: string, ...argu: any[]) { - if (!command) return - return ipcRenderer.send(command, ...argu) - }, - sendSync(command: string, ...argu: any[]) { - if (!command) return - return ipcRenderer.sendSync(command, ...argu) - }, - on(command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) { - ipcRenderer.on(command, cb) - return () => ipcRenderer.removeListener(command, cb) - }, - once(command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) { - ipcRenderer.once(command, cb) - return () => ipcRenderer.removeListener(command, cb) - }, - off(command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) { - return ipcRenderer.removeListener(command, cb) - }, - offAll(command: string) { - return ipcRenderer.removeAllListeners(command) - }, - popupMenu(options: IPopupMenuOption) { - ipcRenderer.send("x_popup_menu", curWebContentName, options) - }, + call, + callLong, + callSync, + send(command: string, ...argu: any[]) { + if (!command) return + return ipcRenderer.send(command, ...argu) + }, + sendSync(command: string, ...argu: any[]) { + if (!command) return + return ipcRenderer.sendSync(command, ...argu) + }, + on(command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) { + ipcRenderer.on(command, cb) + return () => ipcRenderer.removeListener(command, cb) + }, + once(command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) { + ipcRenderer.once(command, cb) + return () => ipcRenderer.removeListener(command, cb) + }, + off(command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) { + return ipcRenderer.removeListener(command, cb) + }, + offAll(command: string) { + return ipcRenderer.removeAllListeners(command) + }, + popupMenu(options: IPopupMenuOption) { + ipcRenderer.send("x_popup_menu", curWebContentName, options) + }, } let curWebContentName = "" ipcRenderer.once("bind-window-manager", (_, name: string) => { - curWebContentName = name + curWebContentName = name }) // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise // just add to the DOM global. if (process.contextIsolated) { - try { - contextBridge.exposeInMainWorld("electron", electronAPI) - contextBridge.exposeInMainWorld("api", api) - } catch (error) { - console.error(error) - } + try { + contextBridge.exposeInMainWorld("electron", electronAPI) + contextBridge.exposeInMainWorld("api", api) + } catch (error) { + console.error(error) + } } else { - // @ts-ignore (define in dts) - window.electron = electronAPI - // @ts-ignore (define in dts) - window.api = api + // @ts-ignore (define in dts) + window.electron = electronAPI + // @ts-ignore (define in dts) + window.api = api } diff --git a/src/renderer/about.html b/src/renderer/about.html index 6798e60..5f6002e 100644 --- a/src/renderer/about.html +++ b/src/renderer/about.html @@ -1,28 +1,28 @@ - - - - 关于我 - - + + + + 关于我 + + - -
-

您好,亲爱的冒险者!

-
- + +
+

您好,亲爱的冒险者!

+
+ diff --git a/src/renderer/auto-imports.d.ts b/src/renderer/auto-imports.d.ts index ce4d49e..8923a26 100644 --- a/src/renderer/auto-imports.d.ts +++ b/src/renderer/auto-imports.d.ts @@ -6,594 +6,609 @@ // biome-ignore lint: disable export {} declare global { - const EffectScope: typeof import('vue')['EffectScope'] - const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] - const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] - const computed: typeof import('vue')['computed'] - const computedAsync: typeof import('@vueuse/core')['computedAsync'] - const computedEager: typeof import('@vueuse/core')['computedEager'] - const computedInject: typeof import('@vueuse/core')['computedInject'] - const computedWithControl: typeof import('@vueuse/core')['computedWithControl'] - const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] - const controlledRef: typeof import('@vueuse/core')['controlledRef'] - const createApp: typeof import('vue')['createApp'] - const createEventHook: typeof import('@vueuse/core')['createEventHook'] - const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] - const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] - const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] - const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate'] - const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] - const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise'] - const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] - const customRef: typeof import('vue')['customRef'] - const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] - const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] - const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] - const defineComponent: typeof import('vue')['defineComponent'] - const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] - const effectScope: typeof import('vue')['effectScope'] - const extendRef: typeof import('@vueuse/core')['extendRef'] - const getCurrentInstance: typeof import('vue')['getCurrentInstance'] - const getCurrentScope: typeof import('vue')['getCurrentScope'] - const h: typeof import('vue')['h'] - const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] - const inject: typeof import('vue')['inject'] - const injectLocal: typeof import('@vueuse/core')['injectLocal'] - const isDefined: typeof import('@vueuse/core')['isDefined'] - const isProxy: typeof import('vue')['isProxy'] - const isReactive: typeof import('vue')['isReactive'] - const isReadonly: typeof import('vue')['isReadonly'] - const isRef: typeof import('vue')['isRef'] - const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] - const markRaw: typeof import('vue')['markRaw'] - const nextTick: typeof import('vue')['nextTick'] - const onActivated: typeof import('vue')['onActivated'] - const onBeforeMount: typeof import('vue')['onBeforeMount'] - const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] - const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] - const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] - const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] - const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] - const onDeactivated: typeof import('vue')['onDeactivated'] - const onElementRemoval: typeof import('@vueuse/core')['onElementRemoval'] - const onErrorCaptured: typeof import('vue')['onErrorCaptured'] - const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] - const onLongPress: typeof import('@vueuse/core')['onLongPress'] - const onMounted: typeof import('vue')['onMounted'] - const onRenderTracked: typeof import('vue')['onRenderTracked'] - const onRenderTriggered: typeof import('vue')['onRenderTriggered'] - const onScopeDispose: typeof import('vue')['onScopeDispose'] - const onServerPrefetch: typeof import('vue')['onServerPrefetch'] - const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] - const onUnmounted: typeof import('vue')['onUnmounted'] - const onUpdated: typeof import('vue')['onUpdated'] - const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] - const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] - const provide: typeof import('vue')['provide'] - const provideLocal: typeof import('@vueuse/core')['provideLocal'] - const reactify: typeof import('@vueuse/core')['reactify'] - const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] - const reactive: typeof import('vue')['reactive'] - const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed'] - const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit'] - const reactivePick: typeof import('@vueuse/core')['reactivePick'] - const readonly: typeof import('vue')['readonly'] - const ref: typeof import('vue')['ref'] - const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] - const refDebounced: typeof import('@vueuse/core')['refDebounced'] - const refDefault: typeof import('@vueuse/core')['refDefault'] - const refThrottled: typeof import('@vueuse/core')['refThrottled'] - const refWithControl: typeof import('@vueuse/core')['refWithControl'] - const resolveComponent: typeof import('vue')['resolveComponent'] - const resolveRef: typeof import('@vueuse/core')['resolveRef'] - const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] - const shallowReactive: typeof import('vue')['shallowReactive'] - const shallowReadonly: typeof import('vue')['shallowReadonly'] - const shallowRef: typeof import('vue')['shallowRef'] - const syncRef: typeof import('@vueuse/core')['syncRef'] - const syncRefs: typeof import('@vueuse/core')['syncRefs'] - const templateRef: typeof import('@vueuse/core')['templateRef'] - const throttledRef: typeof import('@vueuse/core')['throttledRef'] - const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] - const toRaw: typeof import('vue')['toRaw'] - const toReactive: typeof import('@vueuse/core')['toReactive'] - const toRef: typeof import('vue')['toRef'] - const toRefs: typeof import('vue')['toRefs'] - const toValue: typeof import('vue')['toValue'] - const triggerRef: typeof import('vue')['triggerRef'] - const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] - const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] - const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] - const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] - const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] - const unref: typeof import('vue')['unref'] - const unrefElement: typeof import('@vueuse/core')['unrefElement'] - const until: typeof import('@vueuse/core')['until'] - const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] - const useAnimate: typeof import('@vueuse/core')['useAnimate'] - const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference'] - const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery'] - const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] - const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] - const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] - const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] - const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes'] - const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] - const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] - const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] - const useArraySome: typeof import('@vueuse/core')['useArraySome'] - const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] - const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] - const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] - const useAttrs: typeof import('vue')['useAttrs'] - const useBase64: typeof import('@vueuse/core')['useBase64'] - const useBattery: typeof import('@vueuse/core')['useBattery'] - const useBluetooth: typeof import('@vueuse/core')['useBluetooth'] - const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints'] - const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] - const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] - const useCached: typeof import('@vueuse/core')['useCached'] - const useClipboard: typeof import('@vueuse/core')['useClipboard'] - const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems'] - const useCloned: typeof import('@vueuse/core')['useCloned'] - const useColorMode: typeof import('@vueuse/core')['useColorMode'] - const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] - const useCountdown: typeof import('@vueuse/core')['useCountdown'] - const useCounter: typeof import('@vueuse/core')['useCounter'] - const useCssModule: typeof import('vue')['useCssModule'] - const useCssVar: typeof import('@vueuse/core')['useCssVar'] - const useCssVars: typeof import('vue')['useCssVars'] - const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] - const useCycleList: typeof import('@vueuse/core')['useCycleList'] - const useDark: typeof import('@vueuse/core')['useDark'] - const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] - const useDebounce: typeof import('@vueuse/core')['useDebounce'] - const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] - const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] - const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] - const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] - const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] - const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] - const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] - const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] - const useDraggable: typeof import('@vueuse/core')['useDraggable'] - const useDropZone: typeof import('@vueuse/core')['useDropZone'] - const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] - const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] - const useElementHover: typeof import('@vueuse/core')['useElementHover'] - const useElementSize: typeof import('@vueuse/core')['useElementSize'] - const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] - const useEventBus: typeof import('@vueuse/core')['useEventBus'] - const useEventListener: typeof import('@vueuse/core')['useEventListener'] - const useEventSource: typeof import('@vueuse/core')['useEventSource'] - const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] - const useFavicon: typeof import('@vueuse/core')['useFavicon'] - const useFetch: typeof import('@vueuse/core')['useFetch'] - const useFileDialog: typeof import('@vueuse/core')['useFileDialog'] - const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] - const useFocus: typeof import('@vueuse/core')['useFocus'] - const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] - const useFps: typeof import('@vueuse/core')['useFps'] - const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] - const useGamepad: typeof import('@vueuse/core')['useGamepad'] - const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] - const useI18n: typeof import('vue-i18n')['useI18n'] - const useId: typeof import('vue')['useId'] - const useIdle: typeof import('@vueuse/core')['useIdle'] - const useImage: typeof import('@vueuse/core')['useImage'] - const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] - const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] - const useInterval: typeof import('@vueuse/core')['useInterval'] - const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] - const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] - const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] - const useLink: typeof import('vue-router/auto')['useLink'] - const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] - const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] - const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] - const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] - const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] - const useMemoize: typeof import('@vueuse/core')['useMemoize'] - const useMemory: typeof import('@vueuse/core')['useMemory'] - const useModel: typeof import('vue')['useModel'] - const useMounted: typeof import('@vueuse/core')['useMounted'] - const useMouse: typeof import('@vueuse/core')['useMouse'] - const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] - const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] - const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] - const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage'] - const useNetwork: typeof import('@vueuse/core')['useNetwork'] - const useNow: typeof import('@vueuse/core')['useNow'] - const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl'] - const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination'] - const useOnline: typeof import('@vueuse/core')['useOnline'] - const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] - const useParallax: typeof import('@vueuse/core')['useParallax'] - const useParentElement: typeof import('@vueuse/core')['useParentElement'] - const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver'] - const usePermission: typeof import('@vueuse/core')['usePermission'] - const usePointer: typeof import('@vueuse/core')['usePointer'] - const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] - const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] - const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] - const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] - const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] - const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] - const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] - const usePreferredReducedTransparency: typeof import('@vueuse/core')['usePreferredReducedTransparency'] - const usePrevious: typeof import('@vueuse/core')['usePrevious'] - const useRafFn: typeof import('@vueuse/core')['useRafFn'] - const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] - const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] - const useRoute: typeof import('vue-router')['useRoute'] - const useRouter: typeof import('vue-router')['useRouter'] - const useSSRWidth: typeof import('@vueuse/core')['useSSRWidth'] - const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] - const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] - const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] - const useScroll: typeof import('@vueuse/core')['useScroll'] - const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] - const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] - const useShare: typeof import('@vueuse/core')['useShare'] - const useSlots: typeof import('vue')['useSlots'] - const useSorted: typeof import('@vueuse/core')['useSorted'] - const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] - const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] - const useStepper: typeof import('@vueuse/core')['useStepper'] - const useStorage: typeof import('@vueuse/core')['useStorage'] - const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] - const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] - const useSupported: typeof import('@vueuse/core')['useSupported'] - const useSwipe: typeof import('@vueuse/core')['useSwipe'] - const useTemplateRef: typeof import('vue')['useTemplateRef'] - const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] - const useTest: typeof import('./src/composables/useTest')['useTest'] - const useTextDirection: typeof import('@vueuse/core')['useTextDirection'] - const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] - const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize'] - const useThrottle: typeof import('@vueuse/core')['useThrottle'] - const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] - const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] - const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] - const useTimeout: typeof import('@vueuse/core')['useTimeout'] - const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] - const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] - const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] - const useTitle: typeof import('@vueuse/core')['useTitle'] - const useToNumber: typeof import('@vueuse/core')['useToNumber'] - const useToString: typeof import('@vueuse/core')['useToString'] - const useToggle: typeof import('@vueuse/core')['useToggle'] - const useTransition: typeof import('@vueuse/core')['useTransition'] - const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] - const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] - const useVModel: typeof import('@vueuse/core')['useVModel'] - const useVModels: typeof import('@vueuse/core')['useVModels'] - const useVibrate: typeof import('@vueuse/core')['useVibrate'] - const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] - const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] - const useWebNotification: typeof import('@vueuse/core')['useWebNotification'] - const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] - const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] - const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] - const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] - const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] - const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] - const watch: typeof import('vue')['watch'] - const watchArray: typeof import('@vueuse/core')['watchArray'] - const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] - const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] - const watchDeep: typeof import('@vueuse/core')['watchDeep'] - const watchEffect: typeof import('vue')['watchEffect'] - const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] - const watchImmediate: typeof import('@vueuse/core')['watchImmediate'] - const watchOnce: typeof import('@vueuse/core')['watchOnce'] - const watchPausable: typeof import('@vueuse/core')['watchPausable'] - const watchPostEffect: typeof import('vue')['watchPostEffect'] - const watchSyncEffect: typeof import('vue')['watchSyncEffect'] - const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] - const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable'] - const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] - const whenever: typeof import('@vueuse/core')['whenever'] + const EffectScope: (typeof import("vue"))["EffectScope"] + const asyncComputed: (typeof import("@vueuse/core"))["asyncComputed"] + const autoResetRef: (typeof import("@vueuse/core"))["autoResetRef"] + const computed: (typeof import("vue"))["computed"] + const computedAsync: (typeof import("@vueuse/core"))["computedAsync"] + const computedEager: (typeof import("@vueuse/core"))["computedEager"] + const computedInject: (typeof import("@vueuse/core"))["computedInject"] + const computedWithControl: (typeof import("@vueuse/core"))["computedWithControl"] + const controlledComputed: (typeof import("@vueuse/core"))["controlledComputed"] + const controlledRef: (typeof import("@vueuse/core"))["controlledRef"] + const createApp: (typeof import("vue"))["createApp"] + const createEventHook: (typeof import("@vueuse/core"))["createEventHook"] + const createGlobalState: (typeof import("@vueuse/core"))["createGlobalState"] + const createInjectionState: (typeof import("@vueuse/core"))["createInjectionState"] + const createReactiveFn: (typeof import("@vueuse/core"))["createReactiveFn"] + const createReusableTemplate: (typeof import("@vueuse/core"))["createReusableTemplate"] + const createSharedComposable: (typeof import("@vueuse/core"))["createSharedComposable"] + const createTemplatePromise: (typeof import("@vueuse/core"))["createTemplatePromise"] + const createUnrefFn: (typeof import("@vueuse/core"))["createUnrefFn"] + const customRef: (typeof import("vue"))["customRef"] + const debouncedRef: (typeof import("@vueuse/core"))["debouncedRef"] + const debouncedWatch: (typeof import("@vueuse/core"))["debouncedWatch"] + const defineAsyncComponent: (typeof import("vue"))["defineAsyncComponent"] + const defineComponent: (typeof import("vue"))["defineComponent"] + const eagerComputed: (typeof import("@vueuse/core"))["eagerComputed"] + const effectScope: (typeof import("vue"))["effectScope"] + const extendRef: (typeof import("@vueuse/core"))["extendRef"] + const getCurrentInstance: (typeof import("vue"))["getCurrentInstance"] + const getCurrentScope: (typeof import("vue"))["getCurrentScope"] + const h: (typeof import("vue"))["h"] + const ignorableWatch: (typeof import("@vueuse/core"))["ignorableWatch"] + const inject: (typeof import("vue"))["inject"] + const injectLocal: (typeof import("@vueuse/core"))["injectLocal"] + const isDefined: (typeof import("@vueuse/core"))["isDefined"] + const isProxy: (typeof import("vue"))["isProxy"] + const isReactive: (typeof import("vue"))["isReactive"] + const isReadonly: (typeof import("vue"))["isReadonly"] + const isRef: (typeof import("vue"))["isRef"] + const makeDestructurable: (typeof import("@vueuse/core"))["makeDestructurable"] + const markRaw: (typeof import("vue"))["markRaw"] + const nextTick: (typeof import("vue"))["nextTick"] + const onActivated: (typeof import("vue"))["onActivated"] + const onBeforeMount: (typeof import("vue"))["onBeforeMount"] + const onBeforeRouteLeave: (typeof import("vue-router"))["onBeforeRouteLeave"] + const onBeforeRouteUpdate: (typeof import("vue-router"))["onBeforeRouteUpdate"] + const onBeforeUnmount: (typeof import("vue"))["onBeforeUnmount"] + const onBeforeUpdate: (typeof import("vue"))["onBeforeUpdate"] + const onClickOutside: (typeof import("@vueuse/core"))["onClickOutside"] + const onDeactivated: (typeof import("vue"))["onDeactivated"] + const onElementRemoval: (typeof import("@vueuse/core"))["onElementRemoval"] + const onErrorCaptured: (typeof import("vue"))["onErrorCaptured"] + const onKeyStroke: (typeof import("@vueuse/core"))["onKeyStroke"] + const onLongPress: (typeof import("@vueuse/core"))["onLongPress"] + const onMounted: (typeof import("vue"))["onMounted"] + const onRenderTracked: (typeof import("vue"))["onRenderTracked"] + const onRenderTriggered: (typeof import("vue"))["onRenderTriggered"] + const onScopeDispose: (typeof import("vue"))["onScopeDispose"] + const onServerPrefetch: (typeof import("vue"))["onServerPrefetch"] + const onStartTyping: (typeof import("@vueuse/core"))["onStartTyping"] + const onUnmounted: (typeof import("vue"))["onUnmounted"] + const onUpdated: (typeof import("vue"))["onUpdated"] + const onWatcherCleanup: (typeof import("vue"))["onWatcherCleanup"] + const pausableWatch: (typeof import("@vueuse/core"))["pausableWatch"] + const provide: (typeof import("vue"))["provide"] + const provideLocal: (typeof import("@vueuse/core"))["provideLocal"] + const reactify: (typeof import("@vueuse/core"))["reactify"] + const reactifyObject: (typeof import("@vueuse/core"))["reactifyObject"] + const reactive: (typeof import("vue"))["reactive"] + const reactiveComputed: (typeof import("@vueuse/core"))["reactiveComputed"] + const reactiveOmit: (typeof import("@vueuse/core"))["reactiveOmit"] + const reactivePick: (typeof import("@vueuse/core"))["reactivePick"] + const readonly: (typeof import("vue"))["readonly"] + const ref: (typeof import("vue"))["ref"] + const refAutoReset: (typeof import("@vueuse/core"))["refAutoReset"] + const refDebounced: (typeof import("@vueuse/core"))["refDebounced"] + const refDefault: (typeof import("@vueuse/core"))["refDefault"] + const refThrottled: (typeof import("@vueuse/core"))["refThrottled"] + const refWithControl: (typeof import("@vueuse/core"))["refWithControl"] + const resolveComponent: (typeof import("vue"))["resolveComponent"] + const resolveRef: (typeof import("@vueuse/core"))["resolveRef"] + const resolveUnref: (typeof import("@vueuse/core"))["resolveUnref"] + const shallowReactive: (typeof import("vue"))["shallowReactive"] + const shallowReadonly: (typeof import("vue"))["shallowReadonly"] + const shallowRef: (typeof import("vue"))["shallowRef"] + const syncRef: (typeof import("@vueuse/core"))["syncRef"] + const syncRefs: (typeof import("@vueuse/core"))["syncRefs"] + const templateRef: (typeof import("@vueuse/core"))["templateRef"] + const throttledRef: (typeof import("@vueuse/core"))["throttledRef"] + const throttledWatch: (typeof import("@vueuse/core"))["throttledWatch"] + const toRaw: (typeof import("vue"))["toRaw"] + const toReactive: (typeof import("@vueuse/core"))["toReactive"] + const toRef: (typeof import("vue"))["toRef"] + const toRefs: (typeof import("vue"))["toRefs"] + const toValue: (typeof import("vue"))["toValue"] + const triggerRef: (typeof import("vue"))["triggerRef"] + const tryOnBeforeMount: (typeof import("@vueuse/core"))["tryOnBeforeMount"] + const tryOnBeforeUnmount: (typeof import("@vueuse/core"))["tryOnBeforeUnmount"] + const tryOnMounted: (typeof import("@vueuse/core"))["tryOnMounted"] + const tryOnScopeDispose: (typeof import("@vueuse/core"))["tryOnScopeDispose"] + const tryOnUnmounted: (typeof import("@vueuse/core"))["tryOnUnmounted"] + const unref: (typeof import("vue"))["unref"] + const unrefElement: (typeof import("@vueuse/core"))["unrefElement"] + const until: (typeof import("@vueuse/core"))["until"] + const useActiveElement: (typeof import("@vueuse/core"))["useActiveElement"] + const useAnimate: (typeof import("@vueuse/core"))["useAnimate"] + const useArrayDifference: (typeof import("@vueuse/core"))["useArrayDifference"] + const useArrayEvery: (typeof import("@vueuse/core"))["useArrayEvery"] + const useArrayFilter: (typeof import("@vueuse/core"))["useArrayFilter"] + const useArrayFind: (typeof import("@vueuse/core"))["useArrayFind"] + const useArrayFindIndex: (typeof import("@vueuse/core"))["useArrayFindIndex"] + const useArrayFindLast: (typeof import("@vueuse/core"))["useArrayFindLast"] + const useArrayIncludes: (typeof import("@vueuse/core"))["useArrayIncludes"] + const useArrayJoin: (typeof import("@vueuse/core"))["useArrayJoin"] + const useArrayMap: (typeof import("@vueuse/core"))["useArrayMap"] + const useArrayReduce: (typeof import("@vueuse/core"))["useArrayReduce"] + const useArraySome: (typeof import("@vueuse/core"))["useArraySome"] + const useArrayUnique: (typeof import("@vueuse/core"))["useArrayUnique"] + const useAsyncQueue: (typeof import("@vueuse/core"))["useAsyncQueue"] + const useAsyncState: (typeof import("@vueuse/core"))["useAsyncState"] + const useAttrs: (typeof import("vue"))["useAttrs"] + const useBase64: (typeof import("@vueuse/core"))["useBase64"] + const useBattery: (typeof import("@vueuse/core"))["useBattery"] + const useBluetooth: (typeof import("@vueuse/core"))["useBluetooth"] + const useBreakpoints: (typeof import("@vueuse/core"))["useBreakpoints"] + const useBroadcastChannel: (typeof import("@vueuse/core"))["useBroadcastChannel"] + const useBrowserLocation: (typeof import("@vueuse/core"))["useBrowserLocation"] + const useCached: (typeof import("@vueuse/core"))["useCached"] + const useClipboard: (typeof import("@vueuse/core"))["useClipboard"] + const useClipboardItems: (typeof import("@vueuse/core"))["useClipboardItems"] + const useCloned: (typeof import("@vueuse/core"))["useCloned"] + const useColorMode: (typeof import("@vueuse/core"))["useColorMode"] + const useConfirmDialog: (typeof import("@vueuse/core"))["useConfirmDialog"] + const useCountdown: (typeof import("@vueuse/core"))["useCountdown"] + const useCounter: (typeof import("@vueuse/core"))["useCounter"] + const useCssModule: (typeof import("vue"))["useCssModule"] + const useCssVar: (typeof import("@vueuse/core"))["useCssVar"] + const useCssVars: (typeof import("vue"))["useCssVars"] + const useCurrentElement: (typeof import("@vueuse/core"))["useCurrentElement"] + const useCycleList: (typeof import("@vueuse/core"))["useCycleList"] + const useDark: (typeof import("@vueuse/core"))["useDark"] + const useDateFormat: (typeof import("@vueuse/core"))["useDateFormat"] + const useDebounce: (typeof import("@vueuse/core"))["useDebounce"] + const useDebounceFn: (typeof import("@vueuse/core"))["useDebounceFn"] + const useDebouncedRefHistory: (typeof import("@vueuse/core"))["useDebouncedRefHistory"] + const useDeviceMotion: (typeof import("@vueuse/core"))["useDeviceMotion"] + const useDeviceOrientation: (typeof import("@vueuse/core"))["useDeviceOrientation"] + const useDevicePixelRatio: (typeof import("@vueuse/core"))["useDevicePixelRatio"] + const useDevicesList: (typeof import("@vueuse/core"))["useDevicesList"] + const useDisplayMedia: (typeof import("@vueuse/core"))["useDisplayMedia"] + const useDocumentVisibility: (typeof import("@vueuse/core"))["useDocumentVisibility"] + const useDraggable: (typeof import("@vueuse/core"))["useDraggable"] + const useDropZone: (typeof import("@vueuse/core"))["useDropZone"] + const useElementBounding: (typeof import("@vueuse/core"))["useElementBounding"] + const useElementByPoint: (typeof import("@vueuse/core"))["useElementByPoint"] + const useElementHover: (typeof import("@vueuse/core"))["useElementHover"] + const useElementSize: (typeof import("@vueuse/core"))["useElementSize"] + const useElementVisibility: (typeof import("@vueuse/core"))["useElementVisibility"] + const useEventBus: (typeof import("@vueuse/core"))["useEventBus"] + const useEventListener: (typeof import("@vueuse/core"))["useEventListener"] + const useEventSource: (typeof import("@vueuse/core"))["useEventSource"] + const useEyeDropper: (typeof import("@vueuse/core"))["useEyeDropper"] + const useFavicon: (typeof import("@vueuse/core"))["useFavicon"] + const useFetch: (typeof import("@vueuse/core"))["useFetch"] + const useFileDialog: (typeof import("@vueuse/core"))["useFileDialog"] + const useFileSystemAccess: (typeof import("@vueuse/core"))["useFileSystemAccess"] + const useFocus: (typeof import("@vueuse/core"))["useFocus"] + const useFocusWithin: (typeof import("@vueuse/core"))["useFocusWithin"] + const useFps: (typeof import("@vueuse/core"))["useFps"] + const useFullscreen: (typeof import("@vueuse/core"))["useFullscreen"] + const useGamepad: (typeof import("@vueuse/core"))["useGamepad"] + const useGeolocation: (typeof import("@vueuse/core"))["useGeolocation"] + const useI18n: (typeof import("vue-i18n"))["useI18n"] + const useId: (typeof import("vue"))["useId"] + const useIdle: (typeof import("@vueuse/core"))["useIdle"] + const useImage: (typeof import("@vueuse/core"))["useImage"] + const useInfiniteScroll: (typeof import("@vueuse/core"))["useInfiniteScroll"] + const useIntersectionObserver: (typeof import("@vueuse/core"))["useIntersectionObserver"] + const useInterval: (typeof import("@vueuse/core"))["useInterval"] + const useIntervalFn: (typeof import("@vueuse/core"))["useIntervalFn"] + const useKeyModifier: (typeof import("@vueuse/core"))["useKeyModifier"] + const useLastChanged: (typeof import("@vueuse/core"))["useLastChanged"] + const useLink: (typeof import("vue-router/auto"))["useLink"] + const useLocalStorage: (typeof import("@vueuse/core"))["useLocalStorage"] + const useMagicKeys: (typeof import("@vueuse/core"))["useMagicKeys"] + const useManualRefHistory: (typeof import("@vueuse/core"))["useManualRefHistory"] + const useMediaControls: (typeof import("@vueuse/core"))["useMediaControls"] + const useMediaQuery: (typeof import("@vueuse/core"))["useMediaQuery"] + const useMemoize: (typeof import("@vueuse/core"))["useMemoize"] + const useMemory: (typeof import("@vueuse/core"))["useMemory"] + const useModel: (typeof import("vue"))["useModel"] + const useMounted: (typeof import("@vueuse/core"))["useMounted"] + const useMouse: (typeof import("@vueuse/core"))["useMouse"] + const useMouseInElement: (typeof import("@vueuse/core"))["useMouseInElement"] + const useMousePressed: (typeof import("@vueuse/core"))["useMousePressed"] + const useMutationObserver: (typeof import("@vueuse/core"))["useMutationObserver"] + const useNavigatorLanguage: (typeof import("@vueuse/core"))["useNavigatorLanguage"] + const useNetwork: (typeof import("@vueuse/core"))["useNetwork"] + const useNow: (typeof import("@vueuse/core"))["useNow"] + const useObjectUrl: (typeof import("@vueuse/core"))["useObjectUrl"] + const useOffsetPagination: (typeof import("@vueuse/core"))["useOffsetPagination"] + const useOnline: (typeof import("@vueuse/core"))["useOnline"] + const usePageLeave: (typeof import("@vueuse/core"))["usePageLeave"] + const useParallax: (typeof import("@vueuse/core"))["useParallax"] + const useParentElement: (typeof import("@vueuse/core"))["useParentElement"] + const usePerformanceObserver: (typeof import("@vueuse/core"))["usePerformanceObserver"] + const usePermission: (typeof import("@vueuse/core"))["usePermission"] + const usePointer: (typeof import("@vueuse/core"))["usePointer"] + const usePointerLock: (typeof import("@vueuse/core"))["usePointerLock"] + const usePointerSwipe: (typeof import("@vueuse/core"))["usePointerSwipe"] + const usePreferredColorScheme: (typeof import("@vueuse/core"))["usePreferredColorScheme"] + const usePreferredContrast: (typeof import("@vueuse/core"))["usePreferredContrast"] + const usePreferredDark: (typeof import("@vueuse/core"))["usePreferredDark"] + const usePreferredLanguages: (typeof import("@vueuse/core"))["usePreferredLanguages"] + const usePreferredReducedMotion: (typeof import("@vueuse/core"))["usePreferredReducedMotion"] + const usePreferredReducedTransparency: (typeof import("@vueuse/core"))["usePreferredReducedTransparency"] + const usePrevious: (typeof import("@vueuse/core"))["usePrevious"] + const useRafFn: (typeof import("@vueuse/core"))["useRafFn"] + const useRefHistory: (typeof import("@vueuse/core"))["useRefHistory"] + const useResizeObserver: (typeof import("@vueuse/core"))["useResizeObserver"] + const useRoute: (typeof import("vue-router"))["useRoute"] + const useRouter: (typeof import("vue-router"))["useRouter"] + const useSSRWidth: (typeof import("@vueuse/core"))["useSSRWidth"] + const useScreenOrientation: (typeof import("@vueuse/core"))["useScreenOrientation"] + const useScreenSafeArea: (typeof import("@vueuse/core"))["useScreenSafeArea"] + const useScriptTag: (typeof import("@vueuse/core"))["useScriptTag"] + const useScroll: (typeof import("@vueuse/core"))["useScroll"] + const useScrollLock: (typeof import("@vueuse/core"))["useScrollLock"] + const useSessionStorage: (typeof import("@vueuse/core"))["useSessionStorage"] + const useShare: (typeof import("@vueuse/core"))["useShare"] + const useSlots: (typeof import("vue"))["useSlots"] + const useSorted: (typeof import("@vueuse/core"))["useSorted"] + const useSpeechRecognition: (typeof import("@vueuse/core"))["useSpeechRecognition"] + const useSpeechSynthesis: (typeof import("@vueuse/core"))["useSpeechSynthesis"] + const useStepper: (typeof import("@vueuse/core"))["useStepper"] + const useStorage: (typeof import("@vueuse/core"))["useStorage"] + const useStorageAsync: (typeof import("@vueuse/core"))["useStorageAsync"] + const useStyleTag: (typeof import("@vueuse/core"))["useStyleTag"] + const useSupported: (typeof import("@vueuse/core"))["useSupported"] + const useSwipe: (typeof import("@vueuse/core"))["useSwipe"] + const useTemplateRef: (typeof import("vue"))["useTemplateRef"] + const useTemplateRefsList: (typeof import("@vueuse/core"))["useTemplateRefsList"] + const useTest: (typeof import("./src/composables/useTest"))["useTest"] + const useTextDirection: (typeof import("@vueuse/core"))["useTextDirection"] + const useTextSelection: (typeof import("@vueuse/core"))["useTextSelection"] + const useTextareaAutosize: (typeof import("@vueuse/core"))["useTextareaAutosize"] + const useThrottle: (typeof import("@vueuse/core"))["useThrottle"] + const useThrottleFn: (typeof import("@vueuse/core"))["useThrottleFn"] + const useThrottledRefHistory: (typeof import("@vueuse/core"))["useThrottledRefHistory"] + const useTimeAgo: (typeof import("@vueuse/core"))["useTimeAgo"] + const useTimeout: (typeof import("@vueuse/core"))["useTimeout"] + const useTimeoutFn: (typeof import("@vueuse/core"))["useTimeoutFn"] + const useTimeoutPoll: (typeof import("@vueuse/core"))["useTimeoutPoll"] + const useTimestamp: (typeof import("@vueuse/core"))["useTimestamp"] + const useTitle: (typeof import("@vueuse/core"))["useTitle"] + const useToNumber: (typeof import("@vueuse/core"))["useToNumber"] + const useToString: (typeof import("@vueuse/core"))["useToString"] + const useToggle: (typeof import("@vueuse/core"))["useToggle"] + const useTransition: (typeof import("@vueuse/core"))["useTransition"] + const useUrlSearchParams: (typeof import("@vueuse/core"))["useUrlSearchParams"] + const useUserMedia: (typeof import("@vueuse/core"))["useUserMedia"] + const useVModel: (typeof import("@vueuse/core"))["useVModel"] + const useVModels: (typeof import("@vueuse/core"))["useVModels"] + const useVibrate: (typeof import("@vueuse/core"))["useVibrate"] + const useVirtualList: (typeof import("@vueuse/core"))["useVirtualList"] + const useWakeLock: (typeof import("@vueuse/core"))["useWakeLock"] + const useWebNotification: (typeof import("@vueuse/core"))["useWebNotification"] + const useWebSocket: (typeof import("@vueuse/core"))["useWebSocket"] + const useWebWorker: (typeof import("@vueuse/core"))["useWebWorker"] + const useWebWorkerFn: (typeof import("@vueuse/core"))["useWebWorkerFn"] + const useWindowFocus: (typeof import("@vueuse/core"))["useWindowFocus"] + const useWindowScroll: (typeof import("@vueuse/core"))["useWindowScroll"] + const useWindowSize: (typeof import("@vueuse/core"))["useWindowSize"] + const watch: (typeof import("vue"))["watch"] + const watchArray: (typeof import("@vueuse/core"))["watchArray"] + const watchAtMost: (typeof import("@vueuse/core"))["watchAtMost"] + const watchDebounced: (typeof import("@vueuse/core"))["watchDebounced"] + const watchDeep: (typeof import("@vueuse/core"))["watchDeep"] + const watchEffect: (typeof import("vue"))["watchEffect"] + const watchIgnorable: (typeof import("@vueuse/core"))["watchIgnorable"] + const watchImmediate: (typeof import("@vueuse/core"))["watchImmediate"] + const watchOnce: (typeof import("@vueuse/core"))["watchOnce"] + const watchPausable: (typeof import("@vueuse/core"))["watchPausable"] + const watchPostEffect: (typeof import("vue"))["watchPostEffect"] + const watchSyncEffect: (typeof import("vue"))["watchSyncEffect"] + const watchThrottled: (typeof import("@vueuse/core"))["watchThrottled"] + const watchTriggerable: (typeof import("@vueuse/core"))["watchTriggerable"] + const watchWithFilter: (typeof import("@vueuse/core"))["watchWithFilter"] + const whenever: (typeof import("@vueuse/core"))["whenever"] } // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' - import('vue') + export type { + Component, + ComponentPublicInstance, + ComputedRef, + DirectiveBinding, + ExtractDefaultPropTypes, + ExtractPropTypes, + ExtractPublicPropTypes, + InjectionKey, + PropType, + Ref, + MaybeRef, + MaybeRefOrGetter, + VNode, + WritableComputedRef, + } from "vue" + import("vue") } // for vue template auto import -import { UnwrapRef } from 'vue' -declare module 'vue' { +import { UnwrapRef } from "vue" +declare module "vue" { interface GlobalComponents {} interface ComponentCustomProperties { - readonly EffectScope: UnwrapRef - readonly asyncComputed: UnwrapRef - readonly autoResetRef: UnwrapRef - readonly computed: UnwrapRef - readonly computedAsync: UnwrapRef - readonly computedEager: UnwrapRef - readonly computedInject: UnwrapRef - readonly computedWithControl: UnwrapRef - readonly controlledComputed: UnwrapRef - readonly controlledRef: UnwrapRef - readonly createApp: UnwrapRef - readonly createEventHook: UnwrapRef - readonly createGlobalState: UnwrapRef - readonly createInjectionState: UnwrapRef - readonly createReactiveFn: UnwrapRef - readonly createReusableTemplate: UnwrapRef - readonly createSharedComposable: UnwrapRef - readonly createTemplatePromise: UnwrapRef - readonly createUnrefFn: UnwrapRef - readonly customRef: UnwrapRef - readonly debouncedRef: UnwrapRef - readonly debouncedWatch: UnwrapRef - readonly defineAsyncComponent: UnwrapRef - readonly defineComponent: UnwrapRef - readonly eagerComputed: UnwrapRef - readonly effectScope: UnwrapRef - readonly extendRef: UnwrapRef - readonly getCurrentInstance: UnwrapRef - readonly getCurrentScope: UnwrapRef - readonly h: UnwrapRef - readonly ignorableWatch: UnwrapRef - readonly inject: UnwrapRef - readonly injectLocal: UnwrapRef - readonly isDefined: UnwrapRef - readonly isProxy: UnwrapRef - readonly isReactive: UnwrapRef - readonly isReadonly: UnwrapRef - readonly isRef: UnwrapRef - readonly makeDestructurable: UnwrapRef - readonly markRaw: UnwrapRef - readonly nextTick: UnwrapRef - readonly onActivated: UnwrapRef - readonly onBeforeMount: UnwrapRef - readonly onBeforeRouteLeave: UnwrapRef - readonly onBeforeRouteUpdate: UnwrapRef - readonly onBeforeUnmount: UnwrapRef - readonly onBeforeUpdate: UnwrapRef - readonly onClickOutside: UnwrapRef - readonly onDeactivated: UnwrapRef - readonly onElementRemoval: UnwrapRef - readonly onErrorCaptured: UnwrapRef - readonly onKeyStroke: UnwrapRef - readonly onLongPress: UnwrapRef - readonly onMounted: UnwrapRef - readonly onRenderTracked: UnwrapRef - readonly onRenderTriggered: UnwrapRef - readonly onScopeDispose: UnwrapRef - readonly onServerPrefetch: UnwrapRef - readonly onStartTyping: UnwrapRef - readonly onUnmounted: UnwrapRef - readonly onUpdated: UnwrapRef - readonly onWatcherCleanup: UnwrapRef - readonly pausableWatch: UnwrapRef - readonly provide: UnwrapRef - readonly provideLocal: UnwrapRef - readonly reactify: UnwrapRef - readonly reactifyObject: UnwrapRef - readonly reactive: UnwrapRef - readonly reactiveComputed: UnwrapRef - readonly reactiveOmit: UnwrapRef - readonly reactivePick: UnwrapRef - readonly readonly: UnwrapRef - readonly ref: UnwrapRef - readonly refAutoReset: UnwrapRef - readonly refDebounced: UnwrapRef - readonly refDefault: UnwrapRef - readonly refThrottled: UnwrapRef - readonly refWithControl: UnwrapRef - readonly resolveComponent: UnwrapRef - readonly resolveRef: UnwrapRef - readonly resolveUnref: UnwrapRef - readonly shallowReactive: UnwrapRef - readonly shallowReadonly: UnwrapRef - readonly shallowRef: UnwrapRef - readonly syncRef: UnwrapRef - readonly syncRefs: UnwrapRef - readonly templateRef: UnwrapRef - readonly throttledRef: UnwrapRef - readonly throttledWatch: UnwrapRef - readonly toRaw: UnwrapRef - readonly toReactive: UnwrapRef - readonly toRef: UnwrapRef - readonly toRefs: UnwrapRef - readonly toValue: UnwrapRef - readonly triggerRef: UnwrapRef - readonly tryOnBeforeMount: UnwrapRef - readonly tryOnBeforeUnmount: UnwrapRef - readonly tryOnMounted: UnwrapRef - readonly tryOnScopeDispose: UnwrapRef - readonly tryOnUnmounted: UnwrapRef - readonly unref: UnwrapRef - readonly unrefElement: UnwrapRef - readonly until: UnwrapRef - readonly useActiveElement: UnwrapRef - readonly useAnimate: UnwrapRef - readonly useArrayDifference: UnwrapRef - readonly useArrayEvery: UnwrapRef - readonly useArrayFilter: UnwrapRef - readonly useArrayFind: UnwrapRef - readonly useArrayFindIndex: UnwrapRef - readonly useArrayFindLast: UnwrapRef - readonly useArrayIncludes: UnwrapRef - readonly useArrayJoin: UnwrapRef - readonly useArrayMap: UnwrapRef - readonly useArrayReduce: UnwrapRef - readonly useArraySome: UnwrapRef - readonly useArrayUnique: UnwrapRef - readonly useAsyncQueue: UnwrapRef - readonly useAsyncState: UnwrapRef - readonly useAttrs: UnwrapRef - readonly useBase64: UnwrapRef - readonly useBattery: UnwrapRef - readonly useBluetooth: UnwrapRef - readonly useBreakpoints: UnwrapRef - readonly useBroadcastChannel: UnwrapRef - readonly useBrowserLocation: UnwrapRef - readonly useCached: UnwrapRef - readonly useClipboard: UnwrapRef - readonly useClipboardItems: UnwrapRef - readonly useCloned: UnwrapRef - readonly useColorMode: UnwrapRef - readonly useConfirmDialog: UnwrapRef - readonly useCountdown: UnwrapRef - readonly useCounter: UnwrapRef - readonly useCssModule: UnwrapRef - readonly useCssVar: UnwrapRef - readonly useCssVars: UnwrapRef - readonly useCurrentElement: UnwrapRef - readonly useCycleList: UnwrapRef - readonly useDark: UnwrapRef - readonly useDateFormat: UnwrapRef - readonly useDebounce: UnwrapRef - readonly useDebounceFn: UnwrapRef - readonly useDebouncedRefHistory: UnwrapRef - readonly useDeviceMotion: UnwrapRef - readonly useDeviceOrientation: UnwrapRef - readonly useDevicePixelRatio: UnwrapRef - readonly useDevicesList: UnwrapRef - readonly useDisplayMedia: UnwrapRef - readonly useDocumentVisibility: UnwrapRef - readonly useDraggable: UnwrapRef - readonly useDropZone: UnwrapRef - readonly useElementBounding: UnwrapRef - readonly useElementByPoint: UnwrapRef - readonly useElementHover: UnwrapRef - readonly useElementSize: UnwrapRef - readonly useElementVisibility: UnwrapRef - readonly useEventBus: UnwrapRef - readonly useEventListener: UnwrapRef - readonly useEventSource: UnwrapRef - readonly useEyeDropper: UnwrapRef - readonly useFavicon: UnwrapRef - readonly useFetch: UnwrapRef - readonly useFileDialog: UnwrapRef - readonly useFileSystemAccess: UnwrapRef - readonly useFocus: UnwrapRef - readonly useFocusWithin: UnwrapRef - readonly useFps: UnwrapRef - readonly useFullscreen: UnwrapRef - readonly useGamepad: UnwrapRef - readonly useGeolocation: UnwrapRef - readonly useI18n: UnwrapRef - readonly useId: UnwrapRef - readonly useIdle: UnwrapRef - readonly useImage: UnwrapRef - readonly useInfiniteScroll: UnwrapRef - readonly useIntersectionObserver: UnwrapRef - readonly useInterval: UnwrapRef - readonly useIntervalFn: UnwrapRef - readonly useKeyModifier: UnwrapRef - readonly useLastChanged: UnwrapRef - readonly useLink: UnwrapRef - readonly useLocalStorage: UnwrapRef - readonly useMagicKeys: UnwrapRef - readonly useManualRefHistory: UnwrapRef - readonly useMediaControls: UnwrapRef - readonly useMediaQuery: UnwrapRef - readonly useMemoize: UnwrapRef - readonly useMemory: UnwrapRef - readonly useModel: UnwrapRef - readonly useMounted: UnwrapRef - readonly useMouse: UnwrapRef - readonly useMouseInElement: UnwrapRef - readonly useMousePressed: UnwrapRef - readonly useMutationObserver: UnwrapRef - readonly useNavigatorLanguage: UnwrapRef - readonly useNetwork: UnwrapRef - readonly useNow: UnwrapRef - readonly useObjectUrl: UnwrapRef - readonly useOffsetPagination: UnwrapRef - readonly useOnline: UnwrapRef - readonly usePageLeave: UnwrapRef - readonly useParallax: UnwrapRef - readonly useParentElement: UnwrapRef - readonly usePerformanceObserver: UnwrapRef - readonly usePermission: UnwrapRef - readonly usePointer: UnwrapRef - readonly usePointerLock: UnwrapRef - readonly usePointerSwipe: UnwrapRef - readonly usePreferredColorScheme: UnwrapRef - readonly usePreferredContrast: UnwrapRef - readonly usePreferredDark: UnwrapRef - readonly usePreferredLanguages: UnwrapRef - readonly usePreferredReducedMotion: UnwrapRef - readonly usePreferredReducedTransparency: UnwrapRef - readonly usePrevious: UnwrapRef - readonly useRafFn: UnwrapRef - readonly useRefHistory: UnwrapRef - readonly useResizeObserver: UnwrapRef - readonly useRoute: UnwrapRef - readonly useRouter: UnwrapRef - readonly useSSRWidth: UnwrapRef - readonly useScreenOrientation: UnwrapRef - readonly useScreenSafeArea: UnwrapRef - readonly useScriptTag: UnwrapRef - readonly useScroll: UnwrapRef - readonly useScrollLock: UnwrapRef - readonly useSessionStorage: UnwrapRef - readonly useShare: UnwrapRef - readonly useSlots: UnwrapRef - readonly useSorted: UnwrapRef - readonly useSpeechRecognition: UnwrapRef - readonly useSpeechSynthesis: UnwrapRef - readonly useStepper: UnwrapRef - readonly useStorage: UnwrapRef - readonly useStorageAsync: UnwrapRef - readonly useStyleTag: UnwrapRef - readonly useSupported: UnwrapRef - readonly useSwipe: UnwrapRef - readonly useTemplateRef: UnwrapRef - readonly useTemplateRefsList: UnwrapRef - readonly useTest: UnwrapRef - readonly useTextDirection: UnwrapRef - readonly useTextSelection: UnwrapRef - readonly useTextareaAutosize: UnwrapRef - readonly useThrottle: UnwrapRef - readonly useThrottleFn: UnwrapRef - readonly useThrottledRefHistory: UnwrapRef - readonly useTimeAgo: UnwrapRef - readonly useTimeout: UnwrapRef - readonly useTimeoutFn: UnwrapRef - readonly useTimeoutPoll: UnwrapRef - readonly useTimestamp: UnwrapRef - readonly useTitle: UnwrapRef - readonly useToNumber: UnwrapRef - readonly useToString: UnwrapRef - readonly useToggle: UnwrapRef - readonly useTransition: UnwrapRef - readonly useUrlSearchParams: UnwrapRef - readonly useUserMedia: UnwrapRef - readonly useVModel: UnwrapRef - readonly useVModels: UnwrapRef - readonly useVibrate: UnwrapRef - readonly useVirtualList: UnwrapRef - readonly useWakeLock: UnwrapRef - readonly useWebNotification: UnwrapRef - readonly useWebSocket: UnwrapRef - readonly useWebWorker: UnwrapRef - readonly useWebWorkerFn: UnwrapRef - readonly useWindowFocus: UnwrapRef - readonly useWindowScroll: UnwrapRef - readonly useWindowSize: UnwrapRef - readonly watch: UnwrapRef - readonly watchArray: UnwrapRef - readonly watchAtMost: UnwrapRef - readonly watchDebounced: UnwrapRef - readonly watchDeep: UnwrapRef - readonly watchEffect: UnwrapRef - readonly watchIgnorable: UnwrapRef - readonly watchImmediate: UnwrapRef - readonly watchOnce: UnwrapRef - readonly watchPausable: UnwrapRef - readonly watchPostEffect: UnwrapRef - readonly watchSyncEffect: UnwrapRef - readonly watchThrottled: UnwrapRef - readonly watchTriggerable: UnwrapRef - readonly watchWithFilter: UnwrapRef - readonly whenever: UnwrapRef + readonly EffectScope: UnwrapRef<(typeof import("vue"))["EffectScope"]> + readonly asyncComputed: UnwrapRef<(typeof import("@vueuse/core"))["asyncComputed"]> + readonly autoResetRef: UnwrapRef<(typeof import("@vueuse/core"))["autoResetRef"]> + readonly computed: UnwrapRef<(typeof import("vue"))["computed"]> + readonly computedAsync: UnwrapRef<(typeof import("@vueuse/core"))["computedAsync"]> + readonly computedEager: UnwrapRef<(typeof import("@vueuse/core"))["computedEager"]> + readonly computedInject: UnwrapRef<(typeof import("@vueuse/core"))["computedInject"]> + readonly computedWithControl: UnwrapRef<(typeof import("@vueuse/core"))["computedWithControl"]> + readonly controlledComputed: UnwrapRef<(typeof import("@vueuse/core"))["controlledComputed"]> + readonly controlledRef: UnwrapRef<(typeof import("@vueuse/core"))["controlledRef"]> + readonly createApp: UnwrapRef<(typeof import("vue"))["createApp"]> + readonly createEventHook: UnwrapRef<(typeof import("@vueuse/core"))["createEventHook"]> + readonly createGlobalState: UnwrapRef<(typeof import("@vueuse/core"))["createGlobalState"]> + readonly createInjectionState: UnwrapRef<(typeof import("@vueuse/core"))["createInjectionState"]> + readonly createReactiveFn: UnwrapRef<(typeof import("@vueuse/core"))["createReactiveFn"]> + readonly createReusableTemplate: UnwrapRef<(typeof import("@vueuse/core"))["createReusableTemplate"]> + readonly createSharedComposable: UnwrapRef<(typeof import("@vueuse/core"))["createSharedComposable"]> + readonly createTemplatePromise: UnwrapRef<(typeof import("@vueuse/core"))["createTemplatePromise"]> + readonly createUnrefFn: UnwrapRef<(typeof import("@vueuse/core"))["createUnrefFn"]> + readonly customRef: UnwrapRef<(typeof import("vue"))["customRef"]> + readonly debouncedRef: UnwrapRef<(typeof import("@vueuse/core"))["debouncedRef"]> + readonly debouncedWatch: UnwrapRef<(typeof import("@vueuse/core"))["debouncedWatch"]> + readonly defineAsyncComponent: UnwrapRef<(typeof import("vue"))["defineAsyncComponent"]> + readonly defineComponent: UnwrapRef<(typeof import("vue"))["defineComponent"]> + readonly eagerComputed: UnwrapRef<(typeof import("@vueuse/core"))["eagerComputed"]> + readonly effectScope: UnwrapRef<(typeof import("vue"))["effectScope"]> + readonly extendRef: UnwrapRef<(typeof import("@vueuse/core"))["extendRef"]> + readonly getCurrentInstance: UnwrapRef<(typeof import("vue"))["getCurrentInstance"]> + readonly getCurrentScope: UnwrapRef<(typeof import("vue"))["getCurrentScope"]> + readonly h: UnwrapRef<(typeof import("vue"))["h"]> + readonly ignorableWatch: UnwrapRef<(typeof import("@vueuse/core"))["ignorableWatch"]> + readonly inject: UnwrapRef<(typeof import("vue"))["inject"]> + readonly injectLocal: UnwrapRef<(typeof import("@vueuse/core"))["injectLocal"]> + readonly isDefined: UnwrapRef<(typeof import("@vueuse/core"))["isDefined"]> + readonly isProxy: UnwrapRef<(typeof import("vue"))["isProxy"]> + readonly isReactive: UnwrapRef<(typeof import("vue"))["isReactive"]> + readonly isReadonly: UnwrapRef<(typeof import("vue"))["isReadonly"]> + readonly isRef: UnwrapRef<(typeof import("vue"))["isRef"]> + readonly makeDestructurable: UnwrapRef<(typeof import("@vueuse/core"))["makeDestructurable"]> + readonly markRaw: UnwrapRef<(typeof import("vue"))["markRaw"]> + readonly nextTick: UnwrapRef<(typeof import("vue"))["nextTick"]> + readonly onActivated: UnwrapRef<(typeof import("vue"))["onActivated"]> + readonly onBeforeMount: UnwrapRef<(typeof import("vue"))["onBeforeMount"]> + readonly onBeforeRouteLeave: UnwrapRef<(typeof import("vue-router"))["onBeforeRouteLeave"]> + readonly onBeforeRouteUpdate: UnwrapRef<(typeof import("vue-router"))["onBeforeRouteUpdate"]> + readonly onBeforeUnmount: UnwrapRef<(typeof import("vue"))["onBeforeUnmount"]> + readonly onBeforeUpdate: UnwrapRef<(typeof import("vue"))["onBeforeUpdate"]> + readonly onClickOutside: UnwrapRef<(typeof import("@vueuse/core"))["onClickOutside"]> + readonly onDeactivated: UnwrapRef<(typeof import("vue"))["onDeactivated"]> + readonly onElementRemoval: UnwrapRef<(typeof import("@vueuse/core"))["onElementRemoval"]> + readonly onErrorCaptured: UnwrapRef<(typeof import("vue"))["onErrorCaptured"]> + readonly onKeyStroke: UnwrapRef<(typeof import("@vueuse/core"))["onKeyStroke"]> + readonly onLongPress: UnwrapRef<(typeof import("@vueuse/core"))["onLongPress"]> + readonly onMounted: UnwrapRef<(typeof import("vue"))["onMounted"]> + readonly onRenderTracked: UnwrapRef<(typeof import("vue"))["onRenderTracked"]> + readonly onRenderTriggered: UnwrapRef<(typeof import("vue"))["onRenderTriggered"]> + readonly onScopeDispose: UnwrapRef<(typeof import("vue"))["onScopeDispose"]> + readonly onServerPrefetch: UnwrapRef<(typeof import("vue"))["onServerPrefetch"]> + readonly onStartTyping: UnwrapRef<(typeof import("@vueuse/core"))["onStartTyping"]> + readonly onUnmounted: UnwrapRef<(typeof import("vue"))["onUnmounted"]> + readonly onUpdated: UnwrapRef<(typeof import("vue"))["onUpdated"]> + readonly onWatcherCleanup: UnwrapRef<(typeof import("vue"))["onWatcherCleanup"]> + readonly pausableWatch: UnwrapRef<(typeof import("@vueuse/core"))["pausableWatch"]> + readonly provide: UnwrapRef<(typeof import("vue"))["provide"]> + readonly provideLocal: UnwrapRef<(typeof import("@vueuse/core"))["provideLocal"]> + readonly reactify: UnwrapRef<(typeof import("@vueuse/core"))["reactify"]> + readonly reactifyObject: UnwrapRef<(typeof import("@vueuse/core"))["reactifyObject"]> + readonly reactive: UnwrapRef<(typeof import("vue"))["reactive"]> + readonly reactiveComputed: UnwrapRef<(typeof import("@vueuse/core"))["reactiveComputed"]> + readonly reactiveOmit: UnwrapRef<(typeof import("@vueuse/core"))["reactiveOmit"]> + readonly reactivePick: UnwrapRef<(typeof import("@vueuse/core"))["reactivePick"]> + readonly readonly: UnwrapRef<(typeof import("vue"))["readonly"]> + readonly ref: UnwrapRef<(typeof import("vue"))["ref"]> + readonly refAutoReset: UnwrapRef<(typeof import("@vueuse/core"))["refAutoReset"]> + readonly refDebounced: UnwrapRef<(typeof import("@vueuse/core"))["refDebounced"]> + readonly refDefault: UnwrapRef<(typeof import("@vueuse/core"))["refDefault"]> + readonly refThrottled: UnwrapRef<(typeof import("@vueuse/core"))["refThrottled"]> + readonly refWithControl: UnwrapRef<(typeof import("@vueuse/core"))["refWithControl"]> + readonly resolveComponent: UnwrapRef<(typeof import("vue"))["resolveComponent"]> + readonly resolveRef: UnwrapRef<(typeof import("@vueuse/core"))["resolveRef"]> + readonly resolveUnref: UnwrapRef<(typeof import("@vueuse/core"))["resolveUnref"]> + readonly shallowReactive: UnwrapRef<(typeof import("vue"))["shallowReactive"]> + readonly shallowReadonly: UnwrapRef<(typeof import("vue"))["shallowReadonly"]> + readonly shallowRef: UnwrapRef<(typeof import("vue"))["shallowRef"]> + readonly syncRef: UnwrapRef<(typeof import("@vueuse/core"))["syncRef"]> + readonly syncRefs: UnwrapRef<(typeof import("@vueuse/core"))["syncRefs"]> + readonly templateRef: UnwrapRef<(typeof import("@vueuse/core"))["templateRef"]> + readonly throttledRef: UnwrapRef<(typeof import("@vueuse/core"))["throttledRef"]> + readonly throttledWatch: UnwrapRef<(typeof import("@vueuse/core"))["throttledWatch"]> + readonly toRaw: UnwrapRef<(typeof import("vue"))["toRaw"]> + readonly toReactive: UnwrapRef<(typeof import("@vueuse/core"))["toReactive"]> + readonly toRef: UnwrapRef<(typeof import("vue"))["toRef"]> + readonly toRefs: UnwrapRef<(typeof import("vue"))["toRefs"]> + readonly toValue: UnwrapRef<(typeof import("vue"))["toValue"]> + readonly triggerRef: UnwrapRef<(typeof import("vue"))["triggerRef"]> + readonly tryOnBeforeMount: UnwrapRef<(typeof import("@vueuse/core"))["tryOnBeforeMount"]> + readonly tryOnBeforeUnmount: UnwrapRef<(typeof import("@vueuse/core"))["tryOnBeforeUnmount"]> + readonly tryOnMounted: UnwrapRef<(typeof import("@vueuse/core"))["tryOnMounted"]> + readonly tryOnScopeDispose: UnwrapRef<(typeof import("@vueuse/core"))["tryOnScopeDispose"]> + readonly tryOnUnmounted: UnwrapRef<(typeof import("@vueuse/core"))["tryOnUnmounted"]> + readonly unref: UnwrapRef<(typeof import("vue"))["unref"]> + readonly unrefElement: UnwrapRef<(typeof import("@vueuse/core"))["unrefElement"]> + readonly until: UnwrapRef<(typeof import("@vueuse/core"))["until"]> + readonly useActiveElement: UnwrapRef<(typeof import("@vueuse/core"))["useActiveElement"]> + readonly useAnimate: UnwrapRef<(typeof import("@vueuse/core"))["useAnimate"]> + readonly useArrayDifference: UnwrapRef<(typeof import("@vueuse/core"))["useArrayDifference"]> + readonly useArrayEvery: UnwrapRef<(typeof import("@vueuse/core"))["useArrayEvery"]> + readonly useArrayFilter: UnwrapRef<(typeof import("@vueuse/core"))["useArrayFilter"]> + readonly useArrayFind: UnwrapRef<(typeof import("@vueuse/core"))["useArrayFind"]> + readonly useArrayFindIndex: UnwrapRef<(typeof import("@vueuse/core"))["useArrayFindIndex"]> + readonly useArrayFindLast: UnwrapRef<(typeof import("@vueuse/core"))["useArrayFindLast"]> + readonly useArrayIncludes: UnwrapRef<(typeof import("@vueuse/core"))["useArrayIncludes"]> + readonly useArrayJoin: UnwrapRef<(typeof import("@vueuse/core"))["useArrayJoin"]> + readonly useArrayMap: UnwrapRef<(typeof import("@vueuse/core"))["useArrayMap"]> + readonly useArrayReduce: UnwrapRef<(typeof import("@vueuse/core"))["useArrayReduce"]> + readonly useArraySome: UnwrapRef<(typeof import("@vueuse/core"))["useArraySome"]> + readonly useArrayUnique: UnwrapRef<(typeof import("@vueuse/core"))["useArrayUnique"]> + readonly useAsyncQueue: UnwrapRef<(typeof import("@vueuse/core"))["useAsyncQueue"]> + readonly useAsyncState: UnwrapRef<(typeof import("@vueuse/core"))["useAsyncState"]> + readonly useAttrs: UnwrapRef<(typeof import("vue"))["useAttrs"]> + readonly useBase64: UnwrapRef<(typeof import("@vueuse/core"))["useBase64"]> + readonly useBattery: UnwrapRef<(typeof import("@vueuse/core"))["useBattery"]> + readonly useBluetooth: UnwrapRef<(typeof import("@vueuse/core"))["useBluetooth"]> + readonly useBreakpoints: UnwrapRef<(typeof import("@vueuse/core"))["useBreakpoints"]> + readonly useBroadcastChannel: UnwrapRef<(typeof import("@vueuse/core"))["useBroadcastChannel"]> + readonly useBrowserLocation: UnwrapRef<(typeof import("@vueuse/core"))["useBrowserLocation"]> + readonly useCached: UnwrapRef<(typeof import("@vueuse/core"))["useCached"]> + readonly useClipboard: UnwrapRef<(typeof import("@vueuse/core"))["useClipboard"]> + readonly useClipboardItems: UnwrapRef<(typeof import("@vueuse/core"))["useClipboardItems"]> + readonly useCloned: UnwrapRef<(typeof import("@vueuse/core"))["useCloned"]> + readonly useColorMode: UnwrapRef<(typeof import("@vueuse/core"))["useColorMode"]> + readonly useConfirmDialog: UnwrapRef<(typeof import("@vueuse/core"))["useConfirmDialog"]> + readonly useCountdown: UnwrapRef<(typeof import("@vueuse/core"))["useCountdown"]> + readonly useCounter: UnwrapRef<(typeof import("@vueuse/core"))["useCounter"]> + readonly useCssModule: UnwrapRef<(typeof import("vue"))["useCssModule"]> + readonly useCssVar: UnwrapRef<(typeof import("@vueuse/core"))["useCssVar"]> + readonly useCssVars: UnwrapRef<(typeof import("vue"))["useCssVars"]> + readonly useCurrentElement: UnwrapRef<(typeof import("@vueuse/core"))["useCurrentElement"]> + readonly useCycleList: UnwrapRef<(typeof import("@vueuse/core"))["useCycleList"]> + readonly useDark: UnwrapRef<(typeof import("@vueuse/core"))["useDark"]> + readonly useDateFormat: UnwrapRef<(typeof import("@vueuse/core"))["useDateFormat"]> + readonly useDebounce: UnwrapRef<(typeof import("@vueuse/core"))["useDebounce"]> + readonly useDebounceFn: UnwrapRef<(typeof import("@vueuse/core"))["useDebounceFn"]> + readonly useDebouncedRefHistory: UnwrapRef<(typeof import("@vueuse/core"))["useDebouncedRefHistory"]> + readonly useDeviceMotion: UnwrapRef<(typeof import("@vueuse/core"))["useDeviceMotion"]> + readonly useDeviceOrientation: UnwrapRef<(typeof import("@vueuse/core"))["useDeviceOrientation"]> + readonly useDevicePixelRatio: UnwrapRef<(typeof import("@vueuse/core"))["useDevicePixelRatio"]> + readonly useDevicesList: UnwrapRef<(typeof import("@vueuse/core"))["useDevicesList"]> + readonly useDisplayMedia: UnwrapRef<(typeof import("@vueuse/core"))["useDisplayMedia"]> + readonly useDocumentVisibility: UnwrapRef<(typeof import("@vueuse/core"))["useDocumentVisibility"]> + readonly useDraggable: UnwrapRef<(typeof import("@vueuse/core"))["useDraggable"]> + readonly useDropZone: UnwrapRef<(typeof import("@vueuse/core"))["useDropZone"]> + readonly useElementBounding: UnwrapRef<(typeof import("@vueuse/core"))["useElementBounding"]> + readonly useElementByPoint: UnwrapRef<(typeof import("@vueuse/core"))["useElementByPoint"]> + readonly useElementHover: UnwrapRef<(typeof import("@vueuse/core"))["useElementHover"]> + readonly useElementSize: UnwrapRef<(typeof import("@vueuse/core"))["useElementSize"]> + readonly useElementVisibility: UnwrapRef<(typeof import("@vueuse/core"))["useElementVisibility"]> + readonly useEventBus: UnwrapRef<(typeof import("@vueuse/core"))["useEventBus"]> + readonly useEventListener: UnwrapRef<(typeof import("@vueuse/core"))["useEventListener"]> + readonly useEventSource: UnwrapRef<(typeof import("@vueuse/core"))["useEventSource"]> + readonly useEyeDropper: UnwrapRef<(typeof import("@vueuse/core"))["useEyeDropper"]> + readonly useFavicon: UnwrapRef<(typeof import("@vueuse/core"))["useFavicon"]> + readonly useFetch: UnwrapRef<(typeof import("@vueuse/core"))["useFetch"]> + readonly useFileDialog: UnwrapRef<(typeof import("@vueuse/core"))["useFileDialog"]> + readonly useFileSystemAccess: UnwrapRef<(typeof import("@vueuse/core"))["useFileSystemAccess"]> + readonly useFocus: UnwrapRef<(typeof import("@vueuse/core"))["useFocus"]> + readonly useFocusWithin: UnwrapRef<(typeof import("@vueuse/core"))["useFocusWithin"]> + readonly useFps: UnwrapRef<(typeof import("@vueuse/core"))["useFps"]> + readonly useFullscreen: UnwrapRef<(typeof import("@vueuse/core"))["useFullscreen"]> + readonly useGamepad: UnwrapRef<(typeof import("@vueuse/core"))["useGamepad"]> + readonly useGeolocation: UnwrapRef<(typeof import("@vueuse/core"))["useGeolocation"]> + readonly useI18n: UnwrapRef<(typeof import("vue-i18n"))["useI18n"]> + readonly useId: UnwrapRef<(typeof import("vue"))["useId"]> + readonly useIdle: UnwrapRef<(typeof import("@vueuse/core"))["useIdle"]> + readonly useImage: UnwrapRef<(typeof import("@vueuse/core"))["useImage"]> + readonly useInfiniteScroll: UnwrapRef<(typeof import("@vueuse/core"))["useInfiniteScroll"]> + readonly useIntersectionObserver: UnwrapRef<(typeof import("@vueuse/core"))["useIntersectionObserver"]> + readonly useInterval: UnwrapRef<(typeof import("@vueuse/core"))["useInterval"]> + readonly useIntervalFn: UnwrapRef<(typeof import("@vueuse/core"))["useIntervalFn"]> + readonly useKeyModifier: UnwrapRef<(typeof import("@vueuse/core"))["useKeyModifier"]> + readonly useLastChanged: UnwrapRef<(typeof import("@vueuse/core"))["useLastChanged"]> + readonly useLink: UnwrapRef<(typeof import("vue-router/auto"))["useLink"]> + readonly useLocalStorage: UnwrapRef<(typeof import("@vueuse/core"))["useLocalStorage"]> + readonly useMagicKeys: UnwrapRef<(typeof import("@vueuse/core"))["useMagicKeys"]> + readonly useManualRefHistory: UnwrapRef<(typeof import("@vueuse/core"))["useManualRefHistory"]> + readonly useMediaControls: UnwrapRef<(typeof import("@vueuse/core"))["useMediaControls"]> + readonly useMediaQuery: UnwrapRef<(typeof import("@vueuse/core"))["useMediaQuery"]> + readonly useMemoize: UnwrapRef<(typeof import("@vueuse/core"))["useMemoize"]> + readonly useMemory: UnwrapRef<(typeof import("@vueuse/core"))["useMemory"]> + readonly useModel: UnwrapRef<(typeof import("vue"))["useModel"]> + readonly useMounted: UnwrapRef<(typeof import("@vueuse/core"))["useMounted"]> + readonly useMouse: UnwrapRef<(typeof import("@vueuse/core"))["useMouse"]> + readonly useMouseInElement: UnwrapRef<(typeof import("@vueuse/core"))["useMouseInElement"]> + readonly useMousePressed: UnwrapRef<(typeof import("@vueuse/core"))["useMousePressed"]> + readonly useMutationObserver: UnwrapRef<(typeof import("@vueuse/core"))["useMutationObserver"]> + readonly useNavigatorLanguage: UnwrapRef<(typeof import("@vueuse/core"))["useNavigatorLanguage"]> + readonly useNetwork: UnwrapRef<(typeof import("@vueuse/core"))["useNetwork"]> + readonly useNow: UnwrapRef<(typeof import("@vueuse/core"))["useNow"]> + readonly useObjectUrl: UnwrapRef<(typeof import("@vueuse/core"))["useObjectUrl"]> + readonly useOffsetPagination: UnwrapRef<(typeof import("@vueuse/core"))["useOffsetPagination"]> + readonly useOnline: UnwrapRef<(typeof import("@vueuse/core"))["useOnline"]> + readonly usePageLeave: UnwrapRef<(typeof import("@vueuse/core"))["usePageLeave"]> + readonly useParallax: UnwrapRef<(typeof import("@vueuse/core"))["useParallax"]> + readonly useParentElement: UnwrapRef<(typeof import("@vueuse/core"))["useParentElement"]> + readonly usePerformanceObserver: UnwrapRef<(typeof import("@vueuse/core"))["usePerformanceObserver"]> + readonly usePermission: UnwrapRef<(typeof import("@vueuse/core"))["usePermission"]> + readonly usePointer: UnwrapRef<(typeof import("@vueuse/core"))["usePointer"]> + readonly usePointerLock: UnwrapRef<(typeof import("@vueuse/core"))["usePointerLock"]> + readonly usePointerSwipe: UnwrapRef<(typeof import("@vueuse/core"))["usePointerSwipe"]> + readonly usePreferredColorScheme: UnwrapRef<(typeof import("@vueuse/core"))["usePreferredColorScheme"]> + readonly usePreferredContrast: UnwrapRef<(typeof import("@vueuse/core"))["usePreferredContrast"]> + readonly usePreferredDark: UnwrapRef<(typeof import("@vueuse/core"))["usePreferredDark"]> + readonly usePreferredLanguages: UnwrapRef<(typeof import("@vueuse/core"))["usePreferredLanguages"]> + readonly usePreferredReducedMotion: UnwrapRef<(typeof import("@vueuse/core"))["usePreferredReducedMotion"]> + readonly usePreferredReducedTransparency: UnwrapRef<(typeof import("@vueuse/core"))["usePreferredReducedTransparency"]> + readonly usePrevious: UnwrapRef<(typeof import("@vueuse/core"))["usePrevious"]> + readonly useRafFn: UnwrapRef<(typeof import("@vueuse/core"))["useRafFn"]> + readonly useRefHistory: UnwrapRef<(typeof import("@vueuse/core"))["useRefHistory"]> + readonly useResizeObserver: UnwrapRef<(typeof import("@vueuse/core"))["useResizeObserver"]> + readonly useRoute: UnwrapRef<(typeof import("vue-router"))["useRoute"]> + readonly useRouter: UnwrapRef<(typeof import("vue-router"))["useRouter"]> + readonly useSSRWidth: UnwrapRef<(typeof import("@vueuse/core"))["useSSRWidth"]> + readonly useScreenOrientation: UnwrapRef<(typeof import("@vueuse/core"))["useScreenOrientation"]> + readonly useScreenSafeArea: UnwrapRef<(typeof import("@vueuse/core"))["useScreenSafeArea"]> + readonly useScriptTag: UnwrapRef<(typeof import("@vueuse/core"))["useScriptTag"]> + readonly useScroll: UnwrapRef<(typeof import("@vueuse/core"))["useScroll"]> + readonly useScrollLock: UnwrapRef<(typeof import("@vueuse/core"))["useScrollLock"]> + readonly useSessionStorage: UnwrapRef<(typeof import("@vueuse/core"))["useSessionStorage"]> + readonly useShare: UnwrapRef<(typeof import("@vueuse/core"))["useShare"]> + readonly useSlots: UnwrapRef<(typeof import("vue"))["useSlots"]> + readonly useSorted: UnwrapRef<(typeof import("@vueuse/core"))["useSorted"]> + readonly useSpeechRecognition: UnwrapRef<(typeof import("@vueuse/core"))["useSpeechRecognition"]> + readonly useSpeechSynthesis: UnwrapRef<(typeof import("@vueuse/core"))["useSpeechSynthesis"]> + readonly useStepper: UnwrapRef<(typeof import("@vueuse/core"))["useStepper"]> + readonly useStorage: UnwrapRef<(typeof import("@vueuse/core"))["useStorage"]> + readonly useStorageAsync: UnwrapRef<(typeof import("@vueuse/core"))["useStorageAsync"]> + readonly useStyleTag: UnwrapRef<(typeof import("@vueuse/core"))["useStyleTag"]> + readonly useSupported: UnwrapRef<(typeof import("@vueuse/core"))["useSupported"]> + readonly useSwipe: UnwrapRef<(typeof import("@vueuse/core"))["useSwipe"]> + readonly useTemplateRef: UnwrapRef<(typeof import("vue"))["useTemplateRef"]> + readonly useTemplateRefsList: UnwrapRef<(typeof import("@vueuse/core"))["useTemplateRefsList"]> + readonly useTest: UnwrapRef<(typeof import("./src/composables/useTest"))["useTest"]> + readonly useTextDirection: UnwrapRef<(typeof import("@vueuse/core"))["useTextDirection"]> + readonly useTextSelection: UnwrapRef<(typeof import("@vueuse/core"))["useTextSelection"]> + readonly useTextareaAutosize: UnwrapRef<(typeof import("@vueuse/core"))["useTextareaAutosize"]> + readonly useThrottle: UnwrapRef<(typeof import("@vueuse/core"))["useThrottle"]> + readonly useThrottleFn: UnwrapRef<(typeof import("@vueuse/core"))["useThrottleFn"]> + readonly useThrottledRefHistory: UnwrapRef<(typeof import("@vueuse/core"))["useThrottledRefHistory"]> + readonly useTimeAgo: UnwrapRef<(typeof import("@vueuse/core"))["useTimeAgo"]> + readonly useTimeout: UnwrapRef<(typeof import("@vueuse/core"))["useTimeout"]> + readonly useTimeoutFn: UnwrapRef<(typeof import("@vueuse/core"))["useTimeoutFn"]> + readonly useTimeoutPoll: UnwrapRef<(typeof import("@vueuse/core"))["useTimeoutPoll"]> + readonly useTimestamp: UnwrapRef<(typeof import("@vueuse/core"))["useTimestamp"]> + readonly useTitle: UnwrapRef<(typeof import("@vueuse/core"))["useTitle"]> + readonly useToNumber: UnwrapRef<(typeof import("@vueuse/core"))["useToNumber"]> + readonly useToString: UnwrapRef<(typeof import("@vueuse/core"))["useToString"]> + readonly useToggle: UnwrapRef<(typeof import("@vueuse/core"))["useToggle"]> + readonly useTransition: UnwrapRef<(typeof import("@vueuse/core"))["useTransition"]> + readonly useUrlSearchParams: UnwrapRef<(typeof import("@vueuse/core"))["useUrlSearchParams"]> + readonly useUserMedia: UnwrapRef<(typeof import("@vueuse/core"))["useUserMedia"]> + readonly useVModel: UnwrapRef<(typeof import("@vueuse/core"))["useVModel"]> + readonly useVModels: UnwrapRef<(typeof import("@vueuse/core"))["useVModels"]> + readonly useVibrate: UnwrapRef<(typeof import("@vueuse/core"))["useVibrate"]> + readonly useVirtualList: UnwrapRef<(typeof import("@vueuse/core"))["useVirtualList"]> + readonly useWakeLock: UnwrapRef<(typeof import("@vueuse/core"))["useWakeLock"]> + readonly useWebNotification: UnwrapRef<(typeof import("@vueuse/core"))["useWebNotification"]> + readonly useWebSocket: UnwrapRef<(typeof import("@vueuse/core"))["useWebSocket"]> + readonly useWebWorker: UnwrapRef<(typeof import("@vueuse/core"))["useWebWorker"]> + readonly useWebWorkerFn: UnwrapRef<(typeof import("@vueuse/core"))["useWebWorkerFn"]> + readonly useWindowFocus: UnwrapRef<(typeof import("@vueuse/core"))["useWindowFocus"]> + readonly useWindowScroll: UnwrapRef<(typeof import("@vueuse/core"))["useWindowScroll"]> + readonly useWindowSize: UnwrapRef<(typeof import("@vueuse/core"))["useWindowSize"]> + readonly watch: UnwrapRef<(typeof import("vue"))["watch"]> + readonly watchArray: UnwrapRef<(typeof import("@vueuse/core"))["watchArray"]> + readonly watchAtMost: UnwrapRef<(typeof import("@vueuse/core"))["watchAtMost"]> + readonly watchDebounced: UnwrapRef<(typeof import("@vueuse/core"))["watchDebounced"]> + readonly watchDeep: UnwrapRef<(typeof import("@vueuse/core"))["watchDeep"]> + readonly watchEffect: UnwrapRef<(typeof import("vue"))["watchEffect"]> + readonly watchIgnorable: UnwrapRef<(typeof import("@vueuse/core"))["watchIgnorable"]> + readonly watchImmediate: UnwrapRef<(typeof import("@vueuse/core"))["watchImmediate"]> + readonly watchOnce: UnwrapRef<(typeof import("@vueuse/core"))["watchOnce"]> + readonly watchPausable: UnwrapRef<(typeof import("@vueuse/core"))["watchPausable"]> + readonly watchPostEffect: UnwrapRef<(typeof import("vue"))["watchPostEffect"]> + readonly watchSyncEffect: UnwrapRef<(typeof import("vue"))["watchSyncEffect"]> + readonly watchThrottled: UnwrapRef<(typeof import("@vueuse/core"))["watchThrottled"]> + readonly watchTriggerable: UnwrapRef<(typeof import("@vueuse/core"))["watchTriggerable"]> + readonly watchWithFilter: UnwrapRef<(typeof import("@vueuse/core"))["watchWithFilter"]> + readonly whenever: UnwrapRef<(typeof import("@vueuse/core"))["whenever"]> } -} \ No newline at end of file +} diff --git a/src/renderer/index.html b/src/renderer/index.html index 002b389..5d6ed52 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -1,26 +1,27 @@ - - + Electron - - + img-src 'self' data: *;" + /> + - +
- - + diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index 9777f45..62ef43b 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -1,30 +1,30 @@ diff --git a/src/renderer/src/assets/libs/scrollbot.ts b/src/renderer/src/assets/libs/scrollbot.ts index 765657d..b715938 100644 --- a/src/renderer/src/assets/libs/scrollbot.ts +++ b/src/renderer/src/assets/libs/scrollbot.ts @@ -1,246 +1,245 @@ interface ScrollStyle { - [key: string]: string + [key: string]: string } class Scrollbot { - private orgPar!: HTMLElement - private sbw: number = 5 - private scrollSpeed: number = 200 - private parContent!: string - private newPar!: HTMLDivElement - // private sbContainer!: HTMLDivElement - private scrollBarHolder!: HTMLDivElement - private scrollBar!: HTMLDivElement - private inP!: HTMLDivElement - private sbHeight: number = 0 - private mdown: boolean = false - private customHeight: boolean = false - // private scrollElement!: HTMLElement - private onScrollF?: () => void - private sB: ScrollStyle = {} - private sBH: ScrollStyle = {} - private posCorrection: number = 0 - private btmCorrection: number = 0 - private relY: number = 0 - private pC: number = 0 - - getDom(selector: string | HTMLElement) { - if (typeof selector === "string") { - return document.querySelector(selector) - } - return selector + private orgPar!: HTMLElement + private sbw: number = 5 + private scrollSpeed: number = 200 + private parContent!: string + private newPar!: HTMLDivElement + // private sbContainer!: HTMLDivElement + private scrollBarHolder!: HTMLDivElement + private scrollBar!: HTMLDivElement + private inP!: HTMLDivElement + private sbHeight: number = 0 + private mdown: boolean = false + private customHeight: boolean = false + // private scrollElement!: HTMLElement + private onScrollF?: () => void + private sB: ScrollStyle = {} + private sBH: ScrollStyle = {} + private posCorrection: number = 0 + private btmCorrection: number = 0 + private relY: number = 0 + private pC: number = 0 + + getDom(selector: string | HTMLElement) { + if (typeof selector === "string") { + return document.querySelector(selector) } + return selector + } - constructor(selector: string | HTMLElement, width?: number) { - const element = this.getDom(selector) - if (!element) throw new Error("Element not found") - this.orgPar = element + constructor(selector: string | HTMLElement, width?: number) { + const element = this.getDom(selector) + if (!element) throw new Error("Element not found") + this.orgPar = element - const ieVersion = this.isIE() - if (!ieVersion || (ieVersion && ieVersion < 9)) { - this.init(width) - } + const ieVersion = this.isIE() + if (!ieVersion || (ieVersion && ieVersion < 9)) { + this.init(width) } - - private init(width?: number): void { - this.sbw = width ?? 5 - this.parContent = this.orgPar.innerHTML - this.orgPar.innerHTML = "" - - this.setupElements() - this.setupStyles() - this.setupEventListeners() - this.refresh() + } + + private init(width?: number): void { + this.sbw = width ?? 5 + this.parContent = this.orgPar.innerHTML + this.orgPar.innerHTML = "" + + this.setupElements() + this.setupStyles() + this.setupEventListeners() + this.refresh() + } + + private setupElements(): void { + this.newPar = document.createElement("div") + // this.sbContainer = document.createElement("div") + this.scrollBarHolder = document.createElement("div") + this.scrollBar = document.createElement("div") + this.inP = document.createElement("div") + + this.newPar.className = "scrollbot-outer-parent" + this.scrollBarHolder.className = "scrollbot-scrollbar-holder" + this.scrollBar.className = "scrollbot-scrollbar" + this.inP.className = "scrollbot-inner-parent" + + this.inP.innerHTML = this.parContent + this.newPar.appendChild(this.inP) + this.scrollBarHolder.appendChild(this.scrollBar) + this.newPar.appendChild(this.scrollBarHolder) + this.orgPar.appendChild(this.newPar) + } + + private setupStyles(): void { + this.newPar.style.position = "relative" + this.newPar.style.paddingRight = `${this.sbw}px` + this.newPar.style.zIndex = "9999999" + this.newPar.style.height = "100%" + this.newPar.style.overflow = "hidden" + + this.inP.style.cssText = `height:100%;overflow-y:auto;overflow-x:hidden;padding-right:${ + this.sbw + 20 + }px;width:100%;box-sizing:content-box;` + + this.sbHeight = (this.inP.clientHeight * 100) / this.inP.scrollHeight + // this.scrollElement = this.inP + + this.updateScrollbarStyles() + } + + private updateScrollbarStyles(): void { + this.sB = { + width: `${this.sbw}px`, + height: `${this.sbHeight}%`, + position: "absolute", + right: "0", + top: "0", + backgroundColor: "#444444", + borderRadius: "15px", } - private setupElements(): void { - this.newPar = document.createElement("div") - // this.sbContainer = document.createElement("div") - this.scrollBarHolder = document.createElement("div") - this.scrollBar = document.createElement("div") - this.inP = document.createElement("div") - - this.newPar.className = "scrollbot-outer-parent" - this.scrollBarHolder.className = "scrollbot-scrollbar-holder" - this.scrollBar.className = "scrollbot-scrollbar" - this.inP.className = "scrollbot-inner-parent" - - this.inP.innerHTML = this.parContent - this.newPar.appendChild(this.inP) - this.scrollBarHolder.appendChild(this.scrollBar) - this.newPar.appendChild(this.scrollBarHolder) - this.orgPar.appendChild(this.newPar) - } - - private setupStyles(): void { - this.newPar.style.position = "relative" - this.newPar.style.paddingRight = `${this.sbw}px` - this.newPar.style.zIndex = "9999999" - this.newPar.style.height = "100%" - this.newPar.style.overflow = "hidden" - - this.inP.style.cssText = `height:100%;overflow-y:auto;overflow-x:hidden;padding-right:${ - this.sbw + 20 - }px;width:100%;box-sizing:content-box;` - - this.sbHeight = (this.inP.clientHeight * 100) / this.inP.scrollHeight - // this.scrollElement = this.inP - - this.updateScrollbarStyles() + this.sBH = { + width: `${this.sbw}px`, + height: "100%", + position: "absolute", + right: "0", + top: "0", + backgroundColor: "#ADADAD", + borderRadius: "15px", } - private updateScrollbarStyles(): void { - this.sB = { - width: `${this.sbw}px`, - height: `${this.sbHeight}%`, - position: "absolute", - right: "0", - top: "0", - backgroundColor: "#444444", - borderRadius: "15px", - } + Object.assign(this.scrollBar.style, this.sB) + Object.assign(this.scrollBarHolder.style, this.sBH) + } - this.sBH = { - width: `${this.sbw}px`, - height: "100%", - position: "absolute", - right: "0", - top: "0", - backgroundColor: "#ADADAD", - borderRadius: "15px", - } + public refresh(): void { + this.sbHeight = (this.inP.clientHeight * 100) / this.inP.scrollHeight + this.scrollBarHolder.style.display = this.sbHeight >= 100 ? "none" : "block" - Object.assign(this.scrollBar.style, this.sB) - Object.assign(this.scrollBarHolder.style, this.sBH) + if (this.inP.scrollHeight > this.inP.clientHeight) { + this.scrollBar.style.height = this.customHeight ? this.sB.height : `${this.sbHeight}%` } - - public refresh(): void { - this.sbHeight = (this.inP.clientHeight * 100) / this.inP.scrollHeight - this.scrollBarHolder.style.display = this.sbHeight >= 100 ? "none" : "block" - - if (this.inP.scrollHeight > this.inP.clientHeight) { - this.scrollBar.style.height = this.customHeight ? this.sB.height : `${this.sbHeight}%` - } + } + + public destroy(): void { + this.orgPar.innerHTML = this.parContent + this.orgPar.style.overflow = "auto" + } + + private isIE(): number | false { + const userAgent = navigator.userAgent.toLowerCase() + const msie = userAgent.indexOf("msie") + return msie !== -1 ? parseInt(userAgent.split("msie")[1]) : false + } + + public onScroll(callback: () => void): void { + this.onScrollF = callback + } + + private setupEventListeners(): void { + this.setupScrollListener() + this.setupMouseEvents() + } + + private setupScrollListener(): void { + this.inP.addEventListener("scroll", () => { + const scrollPercentage = (this.inP.scrollTop * 100) / this.inP.scrollHeight + const correction = + ((this.sbHeight - parseFloat(this.sB.height)) * this.inP.scrollTop) / (this.inP.scrollHeight - this.inP.clientHeight) + + this.scrollBar.style.top = `${scrollPercentage + correction}%` + + if (this.onScrollF) { + this.onScrollF() + } + }) + } + + private setScroll(position: number, duration: number = 500): void { + if (position >= this.inP.scrollHeight - this.inP.clientHeight) { + position = this.inP.scrollHeight - this.inP.clientHeight } - public destroy(): void { - this.orgPar.innerHTML = this.parContent - this.orgPar.style.overflow = "auto" + const difference = position - this.inP.scrollTop + const perTick = (difference / duration) * 10 + + setTimeout(() => { + this.inP.scrollTop += perTick + if (Math.abs(position - this.inP.scrollTop) < 5) return + this.setScroll(position, duration - 10) + }, 10) + } + + private setupMouseEvents(): void { + // 滚动条容器点击事件 + this.scrollBarHolder.onmousedown = (e: MouseEvent) => { + if (e.target !== this.scrollBarHolder) return + const relPos = ((e.pageY - this.scrollBarHolder.getBoundingClientRect().top) * 100) / this.scrollBarHolder.clientHeight + this.setScroll((this.inP.scrollHeight * relPos) / 100, this.scrollSpeed) } - private isIE(): number | false { - const userAgent = navigator.userAgent.toLowerCase() - const msie = userAgent.indexOf("msie") - return msie !== -1 ? parseInt(userAgent.split("msie")[1]) : false + // 滚动条拖动事件 + this.scrollBar.onmousedown = (e: MouseEvent) => { + this.mdown = true + this.posCorrection = e.pageY - this.scrollBar.getBoundingClientRect().top + this.btmCorrection = (this.scrollBar.clientHeight * 100) / this.newPar.clientHeight + return false } - public onScroll(callback: () => void): void { - this.onScrollF = callback + // 全局鼠标事件 + document.onmouseup = () => { + this.mdown = false } - private setupEventListeners(): void { - this.setupScrollListener() - this.setupMouseEvents() - } - - private setupScrollListener(): void { - this.inP.addEventListener("scroll", () => { - const scrollPercentage = (this.inP.scrollTop * 100) / this.inP.scrollHeight - const correction = - ((this.sbHeight - parseFloat(this.sB.height)) * this.inP.scrollTop) / (this.inP.scrollHeight - this.inP.clientHeight) - - this.scrollBar.style.top = `${scrollPercentage + correction}%` - - if (this.onScrollF) { - this.onScrollF() - } - }) - } - - private setScroll(position: number, duration: number = 500): void { - if (position >= this.inP.scrollHeight - this.inP.clientHeight) { - position = this.inP.scrollHeight - this.inP.clientHeight - } - - const difference = position - this.inP.scrollTop - const perTick = (difference / duration) * 10 - - setTimeout(() => { - this.inP.scrollTop += perTick - if (Math.abs(position - this.inP.scrollTop) < 5) return - this.setScroll(position, duration - 10) - }, 10) - } - - private setupMouseEvents(): void { - // 滚动条容器点击事件 - this.scrollBarHolder.onmousedown = (e: MouseEvent) => { - if (e.target !== this.scrollBarHolder) return - const relPos = ((e.pageY - this.scrollBarHolder.getBoundingClientRect().top) * 100) / this.scrollBarHolder.clientHeight - this.setScroll((this.inP.scrollHeight * relPos) / 100, this.scrollSpeed) - } - - // 滚动条拖动事件 - this.scrollBar.onmousedown = (e: MouseEvent) => { - this.mdown = true - this.posCorrection = e.pageY - this.scrollBar.getBoundingClientRect().top - this.btmCorrection = (this.scrollBar.clientHeight * 100) / this.newPar.clientHeight - return false + document.onmousemove = (e: MouseEvent) => { + if (this.mdown) { + // 清除文本选择 + window.getSelection()?.removeAllRanges() + + this.relY = e.pageY - this.newPar.getBoundingClientRect().top + this.pC = ((this.relY - this.posCorrection) * 100) / this.newPar.clientHeight + + if (this.pC >= 0 && this.pC + this.btmCorrection <= 100) { + this.scrollBar.style.top = `${this.pC}%` + this.inP.scrollTop = + ((parseFloat(this.scrollBar.style.top) - + ((this.sbHeight - parseFloat(this.sB.height)) * this.inP.scrollTop) / (this.inP.scrollHeight - this.inP.clientHeight)) * + this.inP.scrollHeight) / + 100 + } else if (this.pC < 0 && parseFloat(this.scrollBar.style.top) > 0) { + this.scrollBar.style.top = "0%" + this.inP.scrollTop = 0 } - // 全局鼠标事件 - document.onmouseup = () => { - this.mdown = false - } - - document.onmousemove = (e: MouseEvent) => { - if (this.mdown) { - // 清除文本选择 - window.getSelection()?.removeAllRanges() - - this.relY = e.pageY - this.newPar.getBoundingClientRect().top - this.pC = ((this.relY - this.posCorrection) * 100) / this.newPar.clientHeight - - if (this.pC >= 0 && this.pC + this.btmCorrection <= 100) { - this.scrollBar.style.top = `${this.pC}%` - this.inP.scrollTop = - ((parseFloat(this.scrollBar.style.top) - - ((this.sbHeight - parseFloat(this.sB.height)) * this.inP.scrollTop) / - (this.inP.scrollHeight - this.inP.clientHeight)) * - this.inP.scrollHeight) / - 100 - } else if (this.pC < 0 && parseFloat(this.scrollBar.style.top) > 0) { - this.scrollBar.style.top = "0%" - this.inP.scrollTop = 0 - } - - if (this.onScrollF) { - this.onScrollF() - } - } - return false + if (this.onScrollF) { + this.onScrollF() } + } + return false + } + } + + public setStyle(scrollbar?: ScrollStyle, scrollbarHolder?: ScrollStyle): Scrollbot { + if (scrollbar) { + scrollbar.width = `${this.sbw}px` + if ("height" in scrollbar) { + this.customHeight = true + scrollbar.height = `${(parseFloat(scrollbar.height) * 100) / this.newPar.clientHeight}%` + } + Object.assign(this.sB, scrollbar) + Object.assign(this.scrollBar.style, scrollbar) } - public setStyle(scrollbar?: ScrollStyle, scrollbarHolder?: ScrollStyle): Scrollbot { - if (scrollbar) { - scrollbar.width = `${this.sbw}px` - if ("height" in scrollbar) { - this.customHeight = true - scrollbar.height = `${(parseFloat(scrollbar.height) * 100) / this.newPar.clientHeight}%` - } - Object.assign(this.sB, scrollbar) - Object.assign(this.scrollBar.style, scrollbar) - } - - if (scrollbarHolder) { - scrollbarHolder.width = `${this.sbw}px` - Object.assign(this.sBH, scrollbarHolder) - Object.assign(this.scrollBarHolder.style, scrollbarHolder) - } - - return this + if (scrollbarHolder) { + scrollbarHolder.width = `${this.sbw}px` + Object.assign(this.sBH, scrollbarHolder) + Object.assign(this.scrollBarHolder.style, scrollbarHolder) } + + return this + } } export default Scrollbot diff --git a/src/renderer/src/assets/style/_common.scss b/src/renderer/src/assets/style/_common.scss index 2570dfe..7789fba 100644 --- a/src/renderer/src/assets/style/_common.scss +++ b/src/renderer/src/assets/style/_common.scss @@ -1,44 +1,44 @@ *, *::before, *::after { - box-sizing: border-box; - margin: 0; - font-weight: normal; + box-sizing: border-box; + margin: 0; + font-weight: normal; } html { - --text-normal: #6b6b6b; - --text-hover: #000000; - height: 100%; + --text-normal: #6b6b6b; + --text-hover: #000000; + height: 100%; } body { - --at-apply: text-normal; - height: 100%; + --at-apply: text-normal; + height: 100%; } #app { - height: 100%; + height: 100%; } * { - user-select: none; - outline: none; + user-select: none; + outline: none; } .simplebar-scrollbar::before { - background-color: #bdbdbd; - border-radius: 0; - left: 0; - right: 0; - bottom: 0; - top: 0; + background-color: #bdbdbd; + border-radius: 0; + left: 0; + right: 0; + bottom: 0; + top: 0; } .simplebar-hover .simplebar-scrollbar::before { - background-color: #909090; + background-color: #909090; } .simplebar-wrapper:hover ~ .simplebar-track > .simplebar-scrollbar:before { - opacity: 0.5 !important; + opacity: 0.5 !important; } diff --git a/src/renderer/src/bridge/PopupMenu.ts b/src/renderer/src/bridge/PopupMenu.ts index 96820ed..0d44297 100644 --- a/src/renderer/src/bridge/PopupMenu.ts +++ b/src/renderer/src/bridge/PopupMenu.ts @@ -12,58 +12,58 @@ let _idx: number = 0 type OffFunction = () => void export class PopupMenu { - private _id: string - private _items: IMenuItemOption[] - private _offs: any[] = [] + private _id: string + private _items: IMenuItemOption[] + private _offs: any[] = [] - constructor(menu_items: IMenuItemOption[]) { - this._id = `popup_menu_${Math.floor(Math.random() * 1e8)}` - this._items = menu_items - } + constructor(menu_items: IMenuItemOption[]) { + this._id = `popup_menu_${Math.floor(Math.random() * 1e8)}` + this._items = menu_items + } - show(popupOptions?: PopupOptions) { - // console.log('show') - this.onHide() - // eslint-disable-next-line @typescript-eslint/no-this-alias - const that = this - function readMenu(_items: IMenuItemOption[]) { - return _items.map(i => { - const d = { ...i } - if (typeof d.click === "function") { - const r = Math.floor(Math.random() * 1e8) - const evt = `popup_menu_item_${_idx++}_${r}` - const off = api.once(evt, d.click as any) - that._offs.push(off) - d._click_evt = evt - delete d.click - } - if (d.submenu && Array.isArray(d.submenu)) { - d.submenu = readMenu(d.submenu) - } - return d - }) + show(popupOptions?: PopupOptions) { + // console.log('show') + this.onHide() + // eslint-disable-next-line @typescript-eslint/no-this-alias + const that = this + function readMenu(_items: IMenuItemOption[]) { + return _items.map(i => { + const d = { ...i } + if (typeof d.click === "function") { + const r = Math.floor(Math.random() * 1e8) + const evt = `popup_menu_item_${_idx++}_${r}` + const off = api.once(evt, d.click as any) + that._offs.push(off) + d._click_evt = evt + delete d.click } - const items = readMenu(this._items) - - // popupOptions 中的 x,y 必须为整数 - api.popupMenu({ - menu_id: this._id, - items, - popupOptions, - }) - ;((offs: OffFunction[]) => { - api.once(`popup_menu_close:${this._id}`, () => { - // console.log(`on popup_menu_close:${this._id}`) - setTimeout(() => { - offs.map(o => o()) - }, 100) - }) - })(this._offs) + if (d.submenu && Array.isArray(d.submenu)) { + d.submenu = readMenu(d.submenu) + } + return d + }) } + const items = readMenu(this._items) - private onHide() { - // console.log('hide...') - this._offs.map(o => o()) - this._offs = [] - } + // popupOptions 中的 x,y 必须为整数 + api.popupMenu({ + menu_id: this._id, + items, + popupOptions, + }) + ;((offs: OffFunction[]) => { + api.once(`popup_menu_close:${this._id}`, () => { + // console.log(`on popup_menu_close:${this._id}`) + setTimeout(() => { + offs.map(o => o()) + }, 100) + }) + })(this._offs) + } + + private onHide() { + // console.log('hide...') + this._offs.map(o => o()) + this._offs = [] + } } diff --git a/src/renderer/src/components/AdjustLine.vue b/src/renderer/src/components/AdjustLine.vue index ee93019..a448944 100644 --- a/src/renderer/src/components/AdjustLine.vue +++ b/src/renderer/src/components/AdjustLine.vue @@ -1,25 +1,25 @@ diff --git a/src/renderer/src/components/CodeEditor/PlaceholderContentWidget.ts b/src/renderer/src/components/CodeEditor/PlaceholderContentWidget.ts index bc1861b..98942b7 100644 --- a/src/renderer/src/components/CodeEditor/PlaceholderContentWidget.ts +++ b/src/renderer/src/components/CodeEditor/PlaceholderContentWidget.ts @@ -5,53 +5,53 @@ import { monaco } from "./monaco" * Roughly based on https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts */ export class PlaceholderContentWidget implements monaco.editor.IContentWidget { - private static readonly ID = "editor.widget.placeholderHint" - - private domNode: HTMLElement | undefined - - constructor( - private readonly placeholder: string, - private readonly editor: monaco.editor.ICodeEditor, - ) { - // register a listener for editor code changes - editor.onDidChangeModelContent(() => this.onDidChangeModelContent()) - // ensure that on initial load the placeholder is shown - this.onDidChangeModelContent() + private static readonly ID = "editor.widget.placeholderHint" + + private domNode: HTMLElement | undefined + + constructor( + private readonly placeholder: string, + private readonly editor: monaco.editor.ICodeEditor, + ) { + // register a listener for editor code changes + editor.onDidChangeModelContent(() => this.onDidChangeModelContent()) + // ensure that on initial load the placeholder is shown + this.onDidChangeModelContent() + } + + private onDidChangeModelContent(): void { + if (this.editor.getValue() === "") { + this.editor.addContentWidget(this) + } else { + this.editor.removeContentWidget(this) } - - private onDidChangeModelContent(): void { - if (this.editor.getValue() === "") { - this.editor.addContentWidget(this) - } else { - this.editor.removeContentWidget(this) - } + } + + getId(): string { + return PlaceholderContentWidget.ID + } + + getDomNode(): HTMLElement { + if (!this.domNode) { + this.domNode = document.createElement("div") + this.domNode.style.width = "max-content" + this.domNode.style.pointerEvents = "none" + this.domNode.textContent = this.placeholder + this.domNode.style.fontStyle = "italic" + this.editor.applyFontInfo(this.domNode) } - getId(): string { - return PlaceholderContentWidget.ID - } + return this.domNode + } - getDomNode(): HTMLElement { - if (!this.domNode) { - this.domNode = document.createElement("div") - this.domNode.style.width = "max-content" - this.domNode.style.pointerEvents = "none" - this.domNode.textContent = this.placeholder - this.domNode.style.fontStyle = "italic" - this.editor.applyFontInfo(this.domNode) - } - - return this.domNode + getPosition(): monaco.editor.IContentWidgetPosition | null { + return { + position: { lineNumber: 1, column: 1 }, + preference: [monaco.editor.ContentWidgetPositionPreference.EXACT], } + } - getPosition(): monaco.editor.IContentWidgetPosition | null { - return { - position: { lineNumber: 1, column: 1 }, - preference: [monaco.editor.ContentWidgetPositionPreference.EXACT], - } - } - - dispose(): void { - this.editor.removeContentWidget(this) - } + dispose(): void { + this.editor.removeContentWidget(this) + } } diff --git a/src/renderer/src/components/CodeEditor/a.d.ts b/src/renderer/src/components/CodeEditor/a.d.ts index 44df1a0..5fa7024 100644 --- a/src/renderer/src/components/CodeEditor/a.d.ts +++ b/src/renderer/src/components/CodeEditor/a.d.ts @@ -1 +1 @@ -type A = string \ No newline at end of file +type A = string diff --git a/src/renderer/src/components/CodeEditor/code-editor.vue b/src/renderer/src/components/CodeEditor/code-editor.vue index c6d7c9a..69abc8a 100644 --- a/src/renderer/src/components/CodeEditor/code-editor.vue +++ b/src/renderer/src/components/CodeEditor/code-editor.vue @@ -1,300 +1,299 @@ diff --git a/src/renderer/src/components/CodeEditor/readme.md b/src/renderer/src/components/CodeEditor/readme.md index b43aab9..d098231 100644 --- a/src/renderer/src/components/CodeEditor/readme.md +++ b/src/renderer/src/components/CodeEditor/readme.md @@ -1,3 +1,3 @@ 占位符 https://github.com/Microsoft/monaco-editor/issues/1228 -https://github.com/microsoft/monaco-editor/issues/568#issuecomment-1499966160 \ No newline at end of file +https://github.com/microsoft/monaco-editor/issues/568#issuecomment-1499966160 diff --git a/src/renderer/src/components/CodeEditor/utils.ts b/src/renderer/src/components/CodeEditor/utils.ts index 08b192b..3dd3739 100644 --- a/src/renderer/src/components/CodeEditor/utils.ts +++ b/src/renderer/src/components/CodeEditor/utils.ts @@ -1,32 +1,32 @@ export function judgeFile(filename: string) { - if (!filename) return - let ext = [ - { language: "vue", ext: ".vue", index: -1 }, - { language: "javascript", ext: ".js", index: -1 }, - { language: "css", ext: ".css", index: -1 }, - { language: "html", ext: ".html", index: -1 }, - { language: "tsx", ext: ".tsx", index: -1 }, - { language: "typescript", ext: ".ts", index: -1 }, - { language: "markdown", ext: ".md", index: -1 }, - { language: "json", ext: ".json", index: -1 }, - { language: "web", ext: ".web", index: -1 }, - { language: "dot", pre: ".", index: -1 }, - ] - let cur - for (let i = 0; i < ext.length; i++) { - const e = ext[i] - if (e.ext && filename.endsWith(e.ext)) { - let index = filename.lastIndexOf(e.ext) - e.index = index - cur = e - break - } - if (e.pre && filename.startsWith(e.pre)) { - let index = filename.indexOf(e.pre) - e.index = index - cur = e - break - } + if (!filename) return + let ext = [ + { language: "vue", ext: ".vue", index: -1 }, + { language: "javascript", ext: ".js", index: -1 }, + { language: "css", ext: ".css", index: -1 }, + { language: "html", ext: ".html", index: -1 }, + { language: "tsx", ext: ".tsx", index: -1 }, + { language: "typescript", ext: ".ts", index: -1 }, + { language: "markdown", ext: ".md", index: -1 }, + { language: "json", ext: ".json", index: -1 }, + { language: "web", ext: ".web", index: -1 }, + { language: "dot", pre: ".", index: -1 }, + ] + let cur + for (let i = 0; i < ext.length; i++) { + const e = ext[i] + if (e.ext && filename.endsWith(e.ext)) { + let index = filename.lastIndexOf(e.ext) + e.index = index + cur = e + break } - return cur + if (e.pre && filename.startsWith(e.pre)) { + let index = filename.indexOf(e.pre) + e.index = index + cur = e + break + } + } + return cur } diff --git a/src/renderer/src/components/NavBar.vue b/src/renderer/src/components/NavBar.vue index 95a01d9..78594bf 100644 --- a/src/renderer/src/components/NavBar.vue +++ b/src/renderer/src/components/NavBar.vue @@ -1,102 +1,102 @@ diff --git a/src/renderer/src/components/Versions.vue b/src/renderer/src/components/Versions.vue index 73e5785..1e48081 100644 --- a/src/renderer/src/components/Versions.vue +++ b/src/renderer/src/components/Versions.vue @@ -1,13 +1,13 @@ diff --git a/src/renderer/src/composables/useTest.ts b/src/renderer/src/composables/useTest.ts index 25f2304..c272572 100644 --- a/src/renderer/src/composables/useTest.ts +++ b/src/renderer/src/composables/useTest.ts @@ -1,3 +1,3 @@ export function useTest() { - console.log("test") + console.log("test") } diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/env.d.ts index 193f404..38d1901 100644 --- a/src/renderer/src/env.d.ts +++ b/src/renderer/src/env.d.ts @@ -2,8 +2,8 @@ /// declare module "*.vue" { - import type { DefineComponent } from "vue" - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types - const component: DefineComponent<{}, {}, any> - export default component + import type { DefineComponent } from "vue" + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any> + export default component } diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index ad1e772..ed3e591 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -13,13 +13,13 @@ import { datetimeFormats } from "locales" // 引入以便热更新同时提供da console.log(messages) const i18n = createI18n({ - legacy: false, - allowComposition: true, - locale: "zh", - fallbackLocale: "zh", - messages: messages, - // @ts-ignore ... - datetimeFormats, + legacy: false, + allowComposition: true, + locale: "zh", + fallbackLocale: "zh", + messages: messages, + // @ts-ignore ... + datetimeFormats, }) export { i18n } diff --git a/src/renderer/src/layouts/default.vue b/src/renderer/src/layouts/default.vue index c008a8a..aa89d67 100644 --- a/src/renderer/src/layouts/default.vue +++ b/src/renderer/src/layouts/default.vue @@ -1,15 +1,15 @@ diff --git a/src/renderer/src/main.ts b/src/renderer/src/main.ts index 1102c55..ef89f0e 100644 --- a/src/renderer/src/main.ts +++ b/src/renderer/src/main.ts @@ -13,14 +13,14 @@ const app = createApp(App) // 全局错误处理 app.config.errorHandler = (err, instance, info) => { - console.error("应用错误:", err) - console.info("错误信息:", info) - // 可以添加错误上报逻辑 + console.error("应用错误:", err) + console.info("错误信息:", info) + // 可以添加错误上报逻辑 } // 开发环境下的性能监控 if (import.meta.env.DEV) { - app.config.performance = true + app.config.performance = true } app.use(i18n) diff --git a/src/renderer/src/pages/[...all].vue b/src/renderer/src/pages/[...all].vue index 991be30..520eef9 100644 --- a/src/renderer/src/pages/[...all].vue +++ b/src/renderer/src/pages/[...all].vue @@ -1,3 +1,3 @@ diff --git a/src/renderer/src/pages/_ui/App.vue b/src/renderer/src/pages/_ui/App.vue index f886cae..6bcd122 100644 --- a/src/renderer/src/pages/_ui/App.vue +++ b/src/renderer/src/pages/_ui/App.vue @@ -1,14 +1,14 @@ diff --git a/src/renderer/src/pages/_ui/Browser.vue b/src/renderer/src/pages/_ui/Browser.vue index f5136be..e34435e 100644 --- a/src/renderer/src/pages/_ui/Browser.vue +++ b/src/renderer/src/pages/_ui/Browser.vue @@ -1,141 +1,141 @@ diff --git a/src/renderer/src/pages/about/index.vue b/src/renderer/src/pages/about/index.vue index 88bdcda..8717ee4 100644 --- a/src/renderer/src/pages/about/index.vue +++ b/src/renderer/src/pages/about/index.vue @@ -1,37 +1,37 @@ diff --git a/src/renderer/src/pages/browser.vue b/src/renderer/src/pages/browser.vue index 3c53203..cdd1f90 100644 --- a/src/renderer/src/pages/browser.vue +++ b/src/renderer/src/pages/browser.vue @@ -1,143 +1,143 @@ diff --git a/src/renderer/src/pages/index.vue b/src/renderer/src/pages/index.vue index c09e322..34b257b 100644 --- a/src/renderer/src/pages/index.vue +++ b/src/renderer/src/pages/index.vue @@ -1,31 +1,31 @@ diff --git a/src/renderer/src/router/index.ts b/src/renderer/src/router/index.ts index 2972c59..7eed2e0 100644 --- a/src/renderer/src/router/index.ts +++ b/src/renderer/src/router/index.ts @@ -5,8 +5,8 @@ import { setupLayouts } from "virtual:generated-layouts" const routes = setupLayouts(generatedRoutes) const router = createRouter({ - history: createWebHashHistory(), - routes, + history: createWebHashHistory(), + routes, }) export { router } @@ -14,5 +14,5 @@ export { router } export default router if (import.meta.hot) { - handleHotUpdate(router) + handleHotUpdate(router) } diff --git a/src/renderer/src/shims.d.ts b/src/renderer/src/shims.d.ts index 53b5860..cd6f1a7 100644 --- a/src/renderer/src/shims.d.ts +++ b/src/renderer/src/shims.d.ts @@ -1,5 +1,5 @@ import type { AttributifyAttributes } from "@unocss/preset-attributify" declare module "@vue/runtime-dom" { - interface HTMLAttributes extends AttributifyAttributes {} + interface HTMLAttributes extends AttributifyAttributes {} } diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts index 646bfa8..ce311a3 100644 --- a/src/renderer/src/utils/index.ts +++ b/src/renderer/src/utils/index.ts @@ -4,8 +4,8 @@ * @returns 图片路径 */ export const getAssetsFile = url => { - const urlArr = String(url).split("/") - const prefix = urlArr.slice(-2)[0] - const fileName = urlArr.slice(-1)[0] - return new URL(`../assets/images/${prefix}/${fileName}`, import.meta.url).href + const urlArr = String(url).split("/") + const prefix = urlArr.slice(-2)[0] + const fileName = urlArr.slice(-1)[0] + return new URL(`../assets/images/${prefix}/${fileName}`, import.meta.url).href } diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 49fda75..91fdc0c 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,21 +1,20 @@ - type Api = { - call: (command: string, ...args: any[]) => Promise - callLong: (command: string, ...args: any[]) => Promise - callSync: (command: string, ...args: any[]) => any - send: (command: string, ...argu: any[]) => any - sendSync: (command: string, ...argu: any[]) => any - on: (command: T, cb: (event: IpcRendererEvent, ...args: any[]) => void) => () => void - once: (command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) => () => void - off: (command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) => void - offAll: (command: string) => void - popupMenu: (options: IPopupMenuOption) => void + call: (command: string, ...args: any[]) => Promise + callLong: (command: string, ...args: any[]) => Promise + callSync: (command: string, ...args: any[]) => any + send: (command: string, ...argu: any[]) => any + sendSync: (command: string, ...argu: any[]) => any + on: (command: T, cb: (event: IpcRendererEvent, ...args: any[]) => void) => () => void + once: (command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) => () => void + off: (command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) => void + offAll: (command: string) => void + popupMenu: (options: IPopupMenuOption) => void } declare const electron: typeof import("@electron-toolkit/preload").electronAPI declare const api: Api interface Window { - electron: typeof import("@electron-toolkit/preload").electronAPI - api: Api + electron: typeof import("@electron-toolkit/preload").electronAPI + api: Api } diff --git a/src/types/popup-menu.ts b/src/types/popup-menu.ts index c0501ef..bbbdb26 100644 --- a/src/types/popup-menu.ts +++ b/src/types/popup-menu.ts @@ -1,12 +1,12 @@ import type { PopupOptions } from "electron" export interface IMenuItemOption extends Electron.MenuItemConstructorOptions { - // 参见:https://www.electronjs.org/docs/api/menu-item - _click_evt?: string + // 参见:https://www.electronjs.org/docs/api/menu-item + _click_evt?: string } export interface IPopupMenuOption { - menu_id: string - items: IMenuItemOption[] - popupOptions?: PopupOptions + menu_id: string + items: IMenuItemOption[] + popupOptions?: PopupOptions } diff --git a/uno.config.ts b/uno.config.ts index 9c6f0aa..9f4630d 100644 --- a/uno.config.ts +++ b/uno.config.ts @@ -2,75 +2,75 @@ import { defineConfig, presetAttributify, presetUno, transformerDirectives } fro import presetRemToPx from "@unocss/preset-rem-to-px" export default defineConfig({ - presets: [presetAttributify(), presetUno(), presetRemToPx()], - transformers: [transformerDirectives()], - shortcuts: [ - // 正方形 square-100px - [ - /^square-\[?(.*?)\]?$/, - ([, size]) => { - return `w-${size} h-${size}` - }, - ], - // 圆形 circle-100px - [ - /^circle-\[?(.*?)\]?$/, - ([, size]) => { - return `square-${size} rounded-full` - }, - ], - // 垂直水平居中 - ["flex-center", "flex justify-center items-center"], + presets: [presetAttributify(), presetUno(), presetRemToPx()], + transformers: [transformerDirectives()], + shortcuts: [ + // 正方形 square-100px + [ + /^square-\[?(.*?)\]?$/, + ([, size]) => { + return `w-${size} h-${size}` + }, ], - rules: [ - [ - /^text-(.*)$/, - ([, c]) => { - if (c === "normal") return { color: "var(--text-normal)" } - if (c === "hover") return { color: "var(--text-hover)" } - }, - ], - // 多行文本超出部分省略号 line-n (已内置 line-clamp-n) - [ - /^line-(\d+)$/, - ([, l]) => { - if (~~l === 1) { - return { - overflow: "hidden", - "text-overflow": "ellipsis", - "white-space": "nowrap", - width: "100%", - } - } - return { - overflow: "hidden", - display: "-webkit-box", - "-webkit-box-orient": "vertical", - "-webkit-line-clamp": l, - } - }, - ], - // 一侧圆角 rounded-left-5px (已内置 rounded-l-n) - [ - /^rounded-(left|right|top|bottom)-(.*?)$/, - ([, position, m]) => { - let x1, x2, y1, y2 - if (["left", "right"].includes(position)) { - y1 = "top" - y2 = "bottom" - x1 = x2 = position - } else { - x1 = "left" - x2 = "right" - y1 = y2 = position - } - if (m === "full") m = "99999px" + // 圆形 circle-100px + [ + /^circle-\[?(.*?)\]?$/, + ([, size]) => { + return `square-${size} rounded-full` + }, + ], + // 垂直水平居中 + ["flex-center", "flex justify-center items-center"], + ], + rules: [ + [ + /^text-(.*)$/, + ([, c]) => { + if (c === "normal") return { color: "var(--text-normal)" } + if (c === "hover") return { color: "var(--text-hover)" } + }, + ], + // 多行文本超出部分省略号 line-n (已内置 line-clamp-n) + [ + /^line-(\d+)$/, + ([, l]) => { + if (~~l === 1) { + return { + overflow: "hidden", + "text-overflow": "ellipsis", + "white-space": "nowrap", + width: "100%", + } + } + return { + overflow: "hidden", + display: "-webkit-box", + "-webkit-box-orient": "vertical", + "-webkit-line-clamp": l, + } + }, + ], + // 一侧圆角 rounded-left-5px (已内置 rounded-l-n) + [ + /^rounded-(left|right|top|bottom)-(.*?)$/, + ([, position, m]) => { + let x1, x2, y1, y2 + if (["left", "right"].includes(position)) { + y1 = "top" + y2 = "bottom" + x1 = x2 = position + } else { + x1 = "left" + x2 = "right" + y1 = y2 = position + } + if (m === "full") m = "99999px" - return { - [`border-${y1}-${x1}-radius`]: m, - [`border-${y2}-${x2}-radius`]: m, - } - }, - ], + return { + [`border-${y1}-${x1}-radius`]: m, + [`border-${y2}-${x2}-radius`]: m, + } + }, ], + ], }) diff --git a/vue-macros.config.ts b/vue-macros.config.ts index 8b39ea1..df7ac9d 100644 --- a/vue-macros.config.ts +++ b/vue-macros.config.ts @@ -1,4 +1,4 @@ import { defineConfig } from "unplugin-vue-macros" export default defineConfig({ - // 选项 + // 选项 }) diff --git a/推荐.md b/推荐.md index 84b5413..091f7ee 100644 --- a/推荐.md +++ b/推荐.md @@ -1,15 +1,11 @@ - 插件化: https://rubickcenter.github.io/docs/core/index.html#%E5%9F%BA%E4%BA%8E-browserview-%E5%AE%9E%E7%8E%B0%E6%8F%92%E4%BB%B6%E5%8C%96%E8%83%BD%E5%8A%9B - electron+vue虚拟桌面开发遇坑之透明窗口鼠标穿透 - https://blog.csdn.net/weixin_42421494/article/details/102800491 - 截图 https://zhuanlan.zhihu.com/p/46043613?from_voters_page=true