diff --git a/global.d.ts b/global.d.ts index 5da4f5d..b396180 100644 --- a/global.d.ts +++ b/global.d.ts @@ -73,17 +73,6 @@ declare global { setFrameless: (route?: string) => Promise, loadPage: (page: string) => Promise }, - tabs: { - create: (url?: string) => Promise, - list: () => Promise, - navigate: (tabId: string, url: string) => Promise, - reload: (tabId: string) => Promise, - back: (tabId: string) => Promise, - forward: (tabId: string) => Promise, - switch: (tabId: string) => Promise, - close: (tabId: string) => Promise, - on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => () => void - }, readFile: (filePath: string) => Promise<{success: boolean, data?: string, error?: string}>, logger: { debug: (message: string, ...meta?: any[]) => void; diff --git a/html/dialog.html b/html/dialog.html deleted file mode 100644 index f871326..0000000 --- a/html/dialog.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - NIANXX - - - - -
- - - diff --git a/html/home.html b/html/home.html deleted file mode 100644 index f80b85e..0000000 --- a/html/home.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - NIANXX - - - - -
- - - \ No newline at end of file diff --git a/html/knowledge.html b/html/knowledge.html deleted file mode 100644 index efe804d..0000000 --- a/html/knowledge.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - NIANXX - - - - -
- - - diff --git a/html/login.html b/html/login.html deleted file mode 100644 index a08a57f..0000000 --- a/html/login.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - NIANXX - - - - -
- - - diff --git a/html/setting.html b/html/setting.html deleted file mode 100644 index 23e9991..0000000 --- a/html/setting.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - NIANXX - - - - -
- - - diff --git a/html/task.html b/html/task.html deleted file mode 100644 index f4a25b3..0000000 --- a/html/task.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - NIANXX - - - - -
- - - diff --git a/html/index.html b/index.html similarity index 100% rename from html/index.html rename to index.html diff --git a/package-lock.json b/package-lock.json index d9b8037..92f8598 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1501,99 +1501,6 @@ "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/@exodus/schemasafe": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", @@ -1633,47 +1540,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause", - "peer": true - }, "node_modules/@iconify-json/material-symbols": { "version": "1.2.50", "resolved": "https://registry.npmmirror.com/@iconify-json/material-symbols/-/material-symbols-1.2.50.tgz", @@ -3270,14 +3136,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC", - "peer": true - }, "node_modules/@vitejs/plugin-vue": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz", @@ -3683,17 +3541,6 @@ "acorn": "^8.14.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peer": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -4738,14 +4585,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -4864,20 +4703,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dotenv": { "version": "17.2.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", @@ -5901,6 +5726,7 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">=10" }, @@ -5908,64 +5734,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -5993,75 +5761,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/esniff": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/esniff/-/esniff-2.0.1.tgz", @@ -6078,50 +5777,6 @@ "node": ">=0.10" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -6165,17 +5820,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz", @@ -6406,14 +6050,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "license": "MIT" }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmmirror.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -6476,20 +6112,6 @@ } } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -6548,22 +6170,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, "node_modules/flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", @@ -6908,37 +6514,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -7022,14 +6597,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7390,17 +6957,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-2.2.2.tgz", @@ -7583,14 +7139,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -7632,21 +7180,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -8097,14 +7630,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -8719,14 +8244,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", @@ -9242,25 +8759,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -9735,17 +9233,6 @@ "node": "^12.20.0 || >=14" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/prettier": { "version": "3.7.4", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", @@ -9820,17 +9307,6 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/punycode.js": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", @@ -9901,16 +9377,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/react": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-binary-file-arch": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", @@ -10885,20 +10351,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-literal": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", @@ -11166,14 +10618,6 @@ "dev": true, "license": "MIT" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/timers-ext": { "version": "0.1.8", "resolved": "https://registry.npmmirror.com/timers-ext/-/timers-ext-0.1.8.tgz", @@ -11339,20 +10783,6 @@ "dev": true, "license": "ISC" }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -11370,7 +10800,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -11572,17 +11002,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/username": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/username/-/username-5.1.0.tgz", @@ -11913,6 +11332,7 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">=0.10.0" } @@ -12146,16 +11566,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zod": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz", - "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/package.json b/package.json index 350b901..cdd2fbb 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "zn-ai", - "productName": "zn-ai", + "productName": "NIANXX", "version": "1.0.0", - "description": "My Electron application description", + "description": "智念AI - 一款应用智能的多功能桌面应用", "main": ".vite/build/main.js", "scripts": { "start": "dotenv -e .env -- electron-forge start", @@ -74,4 +74,4 @@ "vue-markdown-render": "^2.3.0", "vue-router": "^4.5.1" } -} +} \ No newline at end of file diff --git a/src/common/WebSocketManager.ts b/src/common/WebSocketManager.ts new file mode 100644 index 0000000..95c8259 --- /dev/null +++ b/src/common/WebSocketManager.ts @@ -0,0 +1,306 @@ +/** + * 简化的 WebSocket 管理器(Web 版) + * 专门负责 WebSocket 连接和消息传输,不包含打字机逻辑 + */ + +import { IdUtils, CallbackUtils, MessageUtils, TimerUtils } from './index' + +/* ======================= + * 类型定义 + * ======================= */ + +export interface WebSocketCallbacks { + onConnect?: (event?: Event) => void + onDisconnect?: (event?: CloseEvent) => void + onError?: (error: any) => void + onMessage?: (message: any) => void + getConversationId?: () => string + getAgentId?: () => string +} + +export interface WebSocketManagerOptions extends WebSocketCallbacks { + wsUrl?: string + protocols?: string[] + reconnectInterval?: number + maxReconnectAttempts?: number + heartbeatInterval?: number + messageId?: string + baseDelay?: number + retries?: number + maxDelay?: number + tryReconnect?: boolean +} + +export interface QueuedMessage { + [key: string]: any + retryCount?: number + timestamp?: number +} + +export interface WebSocketStats { + messagesReceived: number + messagesSent: number + messagesDropped: number + reconnectCount: number + connectionStartTime: number | null + lastMessageTime: number | null +} + +/* ======================= + * WebSocketManager + * ======================= */ + +export class WebSocketManager { + private wsUrl = '' + private protocols: string[] = [] + private reconnectInterval = 3000 + private maxReconnectAttempts = 5 + private heartbeatInterval = 30000 + + private ws: WebSocket | null = null + private reconnectAttempts = 0 + private isConnecting = false + private connectionState = false + + private heartbeatTimer: number | null = null + private reconnectTimer: number | null = null + + private callbacks: Required + + private messageQueue: QueuedMessage[] = [] + + private stats: WebSocketStats = { + messagesReceived: 0, + messagesSent: 0, + messagesDropped: 0, + reconnectCount: 0, + connectionStartTime: null, + lastMessageTime: null, + } + + constructor(options: WebSocketManagerOptions = {}) { + this.wsUrl = options.wsUrl ?? '' + this.protocols = options.protocols ?? [] + this.reconnectInterval = options.reconnectInterval ?? 3000 + this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5 + this.heartbeatInterval = options.heartbeatInterval ?? 30000 + + this.callbacks = { + onConnect: options.onConnect ?? (() => { }), + onDisconnect: options.onDisconnect ?? (() => { }), + onError: options.onError ?? (() => { }), + onMessage: options.onMessage ?? (() => { }), + getConversationId: options.getConversationId ?? (() => ''), + getAgentId: options.getAgentId ?? (() => ''), + } + } + + /* ======================= + * 内部工具 + * ======================= */ + + private safeCall(name: keyof WebSocketCallbacks, ...args: any[]): void { + CallbackUtils.safeCall(this.callbacks, name as string, ...args) + } + + /* ======================= + * 连接管理 + * ======================= */ + + async init(wsUrl?: string): Promise { + if (wsUrl) this.wsUrl = wsUrl + if (!this.wsUrl) throw new Error('WebSocket URL is required') + await this.connect() + } + + async connect(): Promise { + if (this.isConnecting || this.connectionState) return + this.isConnecting = true + + try { + this.ws = new WebSocket(this.wsUrl, this.protocols) + console.log('WebSocket connecting to:', this.wsUrl) + this.ws.onopen = this.handleOpen + this.ws.onmessage = this.handleMessage + this.ws.onclose = this.handleClose + this.ws.onerror = this.handleError + } catch (error) { + this.isConnecting = false + this.safeCall('onError', error) + this.scheduleReconnect() + } + } + + private handleOpen = (event: Event): void => { + this.isConnecting = false + this.connectionState = true + this.reconnectAttempts = 0 + this.stats.connectionStartTime = Date.now() + + this.startHeartbeat() + this.safeCall('onConnect', event) + this.processQueue() + } + + private handleMessage = (event: MessageEvent): void => { + const raw = event.data + if (MessageUtils.isPongMessage(raw)) return + + const data = + typeof raw === 'string' + ? MessageUtils.safeParseJSON(raw) + : raw + + if (!data) return + + this.stats.messagesReceived++ + this.stats.lastMessageTime = Date.now() + + this.safeCall('onMessage', data) + } + + private handleClose = (event: CloseEvent): void => { + this.connectionState = false + this.isConnecting = false + this.stopHeartbeat() + + this.safeCall('onDisconnect', event) + + if (event.code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) { + this.scheduleReconnect() + } + } + + private handleError = (error: Event): void => { + this.connectionState = false + this.isConnecting = false + + this.safeCall('onError', { + type: 'WEBSOCKET_ERROR', + error, + }) + + if (!this.reconnectTimer) { + this.scheduleReconnect() + } + } + + /* ======================= + * 消息发送 + * ======================= */ + + sendMessage(message: QueuedMessage): boolean { + const data = { + ...message, + timestamp: Date.now(), + retryCount: message.retryCount ?? 0, + } + + if (!this.isConnected()) { + this.messageQueue.push(data) + this.connect() + return false + } + + try { + this.ws!.send(JSON.stringify(data)) + this.stats.messagesSent++ + return true + } catch (error) { + this.messageQueue.push(data) + this.safeCall('onError', error) + return false + } + } + + private processQueue(): void { + while (this.messageQueue.length) { + const msg = this.messageQueue.shift()! + if ((msg.retryCount ?? 0) >= 3) { + this.stats.messagesDropped++ + continue + } + msg.retryCount = (msg.retryCount ?? 0) + 1 + this.sendMessage(msg) + } + } + + /* ======================= + * 心跳 & 重连 + * ======================= */ + + private startHeartbeat(): void { + this.stopHeartbeat() + this.heartbeatTimer = window.setInterval(() => { + if (!this.isConnected()) return + + this.sendMessage({ + messageType: '3', + messageContent: 'heartbeat', + messageId: IdUtils.generateMessageId(), + conversationId: this.callbacks.getConversationId(), + agentId: this.callbacks.getAgentId(), + }) + }, this.heartbeatInterval) + } + + private stopHeartbeat(): void { + this.heartbeatTimer = TimerUtils.clearTimer(this.heartbeatTimer, 'interval') + } + + private scheduleReconnect(): void { + if (this.reconnectAttempts >= this.maxReconnectAttempts) return + + this.reconnectAttempts++ + this.stats.reconnectCount++ + + const delay = Math.min( + this.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1), + 30000 + ) + + this.reconnectTimer = window.setTimeout(() => { + this.reconnectTimer = null + this.connect() + }, delay) + } + + /* ======================= + * 对外 API + * ======================= */ + + isConnected(): boolean { + return ( + this.connectionState && + !!this.ws && + this.ws.readyState === WebSocket.OPEN + ) + } + + getStats() { + return { + ...this.stats, + queueLength: this.messageQueue.length, + isConnected: this.isConnected(), + } + } + + close(): void { + this.stopHeartbeat() + TimerUtils.clearTimer(this.reconnectTimer) + this.reconnectTimer = null + + this.ws?.close(1000) + this.ws = null + + this.connectionState = false + this.isConnecting = false + this.messageQueue = [] + } + + destroy(): void { + this.close() + } +} + +export default WebSocketManager diff --git a/src/common/index.ts b/src/common/index.ts new file mode 100644 index 0000000..bd63a11 --- /dev/null +++ b/src/common/index.ts @@ -0,0 +1,331 @@ +/** + * 工具函数集合 + * 包含打字机效果、ID生成、回调安全调用等通用工具函数 + */ + +/* ======================= + * ID 生成工具 + * ======================= */ +export class IdUtils { + /** + * 生成消息 ID + */ + static generateMessageId(): string { + const timestamp = Date.now() + const chars = 'abcdefghijklmnopqrstuvwxyz' + const randomStr = Array.from({ length: 4 }, () => + chars.charAt(Math.floor(Math.random() * chars.length)) + ).join('') + return `mid${randomStr}${timestamp}` + } +} + +/* ======================= + * 回调安全调用工具 + * ======================= */ + +export type CallbackMap = Record any> + +export interface BatchCallbackConfig { + name: string + args?: any[] +} + +export class CallbackUtils { + /** + * 安全调用回调函数 + */ + static safeCall( + callbacks: CallbackMap | null | undefined, + callbackName: string, + ...args: any[] + ): void { + const cb = callbacks?.[callbackName] + if (typeof cb === 'function') { + try { + cb(...args) + } catch (error) { + console.error(`回调函数 ${callbackName} 执行出错:`, error) + } + } else { + console.warn(`回调函数 ${callbackName} 不可用`) + } + } + + /** + * 批量安全调用回调函数 + */ + static safeBatchCall( + callbacks: CallbackMap | null | undefined, + callbackConfigs: BatchCallbackConfig[] + ): void { + callbackConfigs.forEach(({ name, args = [] }) => { + this.safeCall(callbacks, name, ...args) + }) + } +} + +/* ======================= + * 消息处理工具 + * ======================= */ + +export interface BaseMessage { + type: string + content?: any + timestamp: number + isComplete?: boolean + [key: string]: any +} + +export class MessageUtils { + /** + * 验证消息格式 + */ + static validateMessage(message: unknown): message is BaseMessage { + return ( + typeof message === 'object' && + message !== null && + 'type' in message + ) + } + + /** + * 格式化消息 + */ + static formatMessage( + type: string, + content: T, + options: Partial = {} + ): BaseMessage { + return { + type, + content, + timestamp: Date.now(), + ...options, + } + } + + /** + * 是否为完整消息 + */ + static isCompleteMessage(message: Partial | null | undefined): boolean { + return message?.isComplete === true + } + + /** + * 是否为心跳 pong + */ + static isPongMessage(messageData: unknown): boolean { + if (typeof messageData === 'string') { + return messageData === 'pong' || messageData.toLowerCase().includes('pong') + } + if (typeof messageData === 'object' && messageData !== null) { + return (messageData as any).type === 'pong' + } + return false + } + + /** + * 安全解析 JSON + */ + static safeParseJSON(messageStr: string): T | null { + try { + return JSON.parse(messageStr) as T + } catch { + console.warn('JSON 解析失败:', messageStr) + return null + } + } + + /** + * 创建打字机消息 + */ + static createTypewriterMessage( + content: string, + isComplete = false, + type = 'typewriter' + ): BaseMessage { + return { + type, + content, + isComplete, + timestamp: Date.now(), + } + } + + /** + * 创建加载消息 + */ + static createLoadingMessage(content = '加载中...'): BaseMessage { + return { + type: 'loading', + content, + timestamp: Date.now(), + } + } + + /** + * 创建错误消息 + */ + static createErrorMessage(error: unknown): BaseMessage { + return { + type: 'error', + content: + error instanceof Error ? error.message : String(error ?? '未知错误'), + timestamp: Date.now(), + } + } +} + +/* ======================= + * 定时器工具 + * ======================= */ + +export type TimerType = 'timeout' | 'interval' + +export interface CancelableTimer { + cancel(): void + isActive(): boolean +} + +export class TimerUtils { + static safeClear( + timerId: number | null, + type: TimerType = 'timeout' + ): null { + if (timerId !== null) { + type === 'interval' ? clearInterval(timerId) : clearTimeout(timerId) + } + return null + } + + static clearTimer( + timerId: number | null, + type: TimerType = 'timeout' + ): null { + return this.safeClear(timerId, type) + } + + static createCancelableTimeout( + callback: () => void, + delay: number + ): CancelableTimer { + let timerId: number | null = window.setTimeout(callback, delay) + return { + cancel() { + if (timerId !== null) { + clearTimeout(timerId) + timerId = null + } + }, + isActive() { + return timerId !== null + }, + } + } + + static createCancelableInterval( + callback: () => void, + interval: number + ): CancelableTimer { + let timerId: number | null = window.setInterval(callback, interval) + return { + cancel() { + if (timerId !== null) { + clearInterval(timerId) + timerId = null + } + }, + isActive() { + return timerId !== null + }, + } + } +} + +/* ======================= + * 防抖工具 + * ======================= */ + +export class DebounceUtils { + static createDebounce void>( + func: T, + delay: number + ): (...args: Parameters) => void { + let timerId: number | null = null + + return function (this: unknown, ...args: Parameters) { + if (timerId !== null) { + clearTimeout(timerId) + } + timerId = window.setTimeout(() => func.apply(this, args), delay) + } + } +} + +/* ======================= + * 节流工具 + * ======================= */ + +export class ThrottleUtils { + static createThrottle void>( + func: T, + delay: number + ): (...args: Parameters) => void { + let prev = Date.now() + + return function (this: unknown, ...args: Parameters) { + const now = Date.now() + if (now - prev >= delay) { + func.apply(this, args) + prev = now + } + } + } +} + +/* ======================= + * 日期工具 + * ======================= */ + +export class DateUtils { + static formatDate( + date: Date = new Date(), + format: string = 'yyyy-MM-dd' + ): string { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + + return format + .replace('yyyy', String(year)) + .replace('MM', month) + .replace('dd', day) + } +} + +/* ======================= + * 手机号校验 + * ======================= */ + +export class PhoneUtils { + static validatePhone(phone: string): boolean { + const phoneRegex = /^1[3-9]\d{9}$/ + return phoneRegex.test(phone) + } +} + +/* ======================= + * 默认导出 + * ======================= */ + +export default { + IdUtils, + CallbackUtils, + MessageUtils, + TimerUtils, + DateUtils, + DebounceUtils, + ThrottleUtils, + PhoneUtils, +} \ No newline at end of file diff --git a/src/main/main.ts b/src/main/main.ts index 0c297dd..87cbf73 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,9 +1,9 @@ import { app, BrowserWindow } from 'electron' -import { setupWindows } from '@main/wins' +import { CONFIG_KEYS } from '@common/constants' +import { setupMainWindow } from './wins'; import started from 'electron-squirrel-startup' import configManager from '@main/service/config-service' import logManager from '@main/service/logger' -import { CONFIG_KEYS } from '@common/constants' // Handle creating/removing shortcuts on Windows when installing/uninstalling. if (started) { @@ -18,19 +18,8 @@ process.on('unhandledRejection', (reason, promise) => { logManager.error('unhandledRejection', reason, promise); }); -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. app.whenReady().then(() => { - setupWindows(); - - // On OS X it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - app.on('activate', () => { - if (BrowserWindow.getAllWindows().length === 0) { - setupWindows(); - } - }); + setupMainWindow(); }); // Quit when all windows are closed, except on macOS. There, it's common @@ -43,5 +32,10 @@ app.on('window-all-closed', () => { } }); -// In this file you can include the rest of your app's specific main process -// code. You can also put them in separate files and import them here. +// On OS X it's common to re-create a window in the app when the +// dock icon is clicked and there are no other windows open. +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + setupMainWindow(); + } +}); diff --git a/src/main/service/window-service/index.ts b/src/main/service/window-service/index.ts index 8107596..feb9e50 100644 --- a/src/main/service/window-service/index.ts +++ b/src/main/service/window-service/index.ts @@ -219,9 +219,9 @@ class WindowService { private _loadPage(window: BrowserWindow, pageName: string) { if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { - return window.loadURL(`${MAIN_WINDOW_VITE_DEV_SERVER_URL}/html/${pageName}.html`); + return window.loadURL(`${MAIN_WINDOW_VITE_DEV_SERVER_URL}/${pageName}.html`); } - window.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/html/${pageName}.html`)); + window.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/${pageName}.html`)); } private _loadWindowTemplate(window: BrowserWindow, name: WindowNames) { diff --git a/src/main/wins/dialog.ts b/src/main/wins/dialog.ts deleted file mode 100644 index 471517e..0000000 --- a/src/main/wins/dialog.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { IPC_EVENTS, WINDOW_NAMES } from '@common/constants' -import { BrowserWindow, ipcMain } from 'electron' -import { windowManager } from '@main/service/window-service' - -export function setupDialogWindow() { - let dialogWindow: BrowserWindow | void; - let params: CreateDialogProps | void - let feedback: string | void - - ipcMain.handle(WINDOW_NAMES.DIALOG + 'get-params',(e)=>{ - if(BrowserWindow.fromWebContents(e.sender) !== dialogWindow) return - return { - winId: e.sender.id, - ...params - } - }); - - ['confirm','cancel'].forEach(_feedback => { - ipcMain.on(WINDOW_NAMES.DIALOG + _feedback,(e,winId:number)=> { - if(e.sender.id !== winId) return - feedback = _feedback; - windowManager.close(BrowserWindow.fromWebContents(e.sender)); - }); - }); - - ipcMain.handle(`${IPC_EVENTS.OPEN_WINDOW}:${WINDOW_NAMES.DIALOG}`, (e, _params) => { - params = _params; - dialogWindow = windowManager.create( - WINDOW_NAMES.DIALOG, - { - width: 350, height: 200, - minWidth: 350, minHeight: 200, - maxWidth: 400, maxHeight: 300, - }, - { - parent: BrowserWindow.fromWebContents(e.sender) as BrowserWindow, - resizable: false - } - ); - - return new Promise((resolve) => dialogWindow?.on('closed', () => { - resolve(feedback); - feedback = void 0; - })) - }) - -} - -export default setupDialogWindow diff --git a/src/main/wins/index.ts b/src/main/wins/index.ts index 452cb34..8cca791 100644 --- a/src/main/wins/index.ts +++ b/src/main/wins/index.ts @@ -1,9 +1,162 @@ -import { setupMainWindow } from './main'; -import { setupDialogWindow } from './dialog'; -import { setupSetttingWindow } from './setting'; +import type { BrowserWindow } from 'electron' +import { ipcMain } from 'electron'; +import { WINDOW_NAMES, MAIN_WIN_SIZE, IPC_EVENTS, MENU_IDS, CONVERSATION_ITEM_MENU_IDS, CONVERSATION_LIST_MENU_IDS, MESSAGE_ITEM_MENU_IDS, CONFIG_KEYS } from '@common/constants' +import { createProvider } from '../providers' +import { windowManager } from '@main/service/window-service' +import { menuManager } from '@main/service/menu-service' +import { logManager } from '@main/service/logger' +import { configManager } from '@main/service/config-service' +import { trayManager } from '@main/service/tray-service' +import { TabManager } from '@service/tab-manager' -export function setupWindows() { - setupMainWindow(); - setupSetttingWindow(); - setupDialogWindow(); +const handleTray = (minimizeToTray: boolean) => { + if (minimizeToTray) { + trayManager.create(); + return; + } + + trayManager.destroy(); +} + +const registerMenus = (window: BrowserWindow) => { + + const conversationItemMenuItemClick = (id: string) => { + logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_ITEM}-${id}`) + window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_ITEM}`, id); + } + + menuManager.register(MENU_IDS.CONVERSATION_ITEM, [ + { + id: CONVERSATION_ITEM_MENU_IDS.PIN, + label: 'menu.conversation.pinConversation', + click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.PIN) + }, + { + id: CONVERSATION_ITEM_MENU_IDS.RENAME, + label: 'menu.conversation.renameConversation', + click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.RENAME) + }, + { + id: CONVERSATION_ITEM_MENU_IDS.DEL, + label: 'menu.conversation.delConversation', + click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.DEL) + }, + ]) + + const conversationListMenuItemClick = (id: string) => { + logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_LIST}-${id}`) + window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_LIST}`, id); + } + + menuManager.register(MENU_IDS.CONVERSATION_LIST, [ + { + id: CONVERSATION_LIST_MENU_IDS.NEW_CONVERSATION, + label: 'menu.conversation.newConversation', + click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.NEW_CONVERSATION) + }, + { type: 'separator' }, + { + id: CONVERSATION_LIST_MENU_IDS.SORT_BY, label: 'menu.conversation.sortBy', submenu: [ + { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_CREATE_TIME, label: 'menu.conversation.sortByCreateTime', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_CREATE_TIME) }, + { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_UPDATE_TIME, label: 'menu.conversation.sortByUpdateTime', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_UPDATE_TIME) }, + { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_NAME, label: 'menu.conversation.sortByName', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_NAME) }, + { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_MODEL, label: 'menu.conversation.sortByModel', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_MODEL) }, + { type: 'separator' }, + { id: CONVERSATION_LIST_MENU_IDS.SORT_ASCENDING, label: 'menu.conversation.sortAscending', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_ASCENDING) }, + { id: CONVERSATION_LIST_MENU_IDS.SORT_DESCENDING, label: 'menu.conversation.sortDescending', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_DESCENDING) }, + ] + }, + { + id: CONVERSATION_LIST_MENU_IDS.BATCH_OPERATIONS, + label: 'menu.conversation.batchOperations', + click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.BATCH_OPERATIONS) + } + ]) + + const messageItemMenuItemClick = (id: string) => { + logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.MESSAGE_ITEM}-${id}`) + window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.MESSAGE_ITEM}`, id); + } + + menuManager.register(MENU_IDS.MESSAGE_ITEM, [ + { + id: MESSAGE_ITEM_MENU_IDS.COPY, + label: 'menu.message.copyMessage', + click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.COPY) + }, + { + id: MESSAGE_ITEM_MENU_IDS.SELECT, + label: 'menu.message.selectMessage', + click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.SELECT) + }, + { type: 'separator' }, + { + id: MESSAGE_ITEM_MENU_IDS.DELETE, + label: 'menu.message.deleteMessage', + click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.DELETE) + }, + ]) +} + +export function setupMainWindow() { + windowManager.onWindowCreate(WINDOW_NAMES.MAIN, (mainWindow) => { + let minimizeToTray = configManager.get(CONFIG_KEYS.MINIMIZE_TO_TRAY); + + configManager.onConfigChange((config) => { + if (minimizeToTray === config[CONFIG_KEYS.MINIMIZE_TO_TRAY]) return; + minimizeToTray = config[CONFIG_KEYS.MINIMIZE_TO_TRAY]; + handleTray(minimizeToTray); + }); + + handleTray(minimizeToTray); + registerMenus(mainWindow); + + const tabManager = new TabManager(mainWindow) + tabManager.enable() + + mainWindow.on('closed', () => { + tabManager.destroy() + }) + }); + + windowManager.create(WINDOW_NAMES.MAIN, MAIN_WIN_SIZE); + + ipcMain.on(IPC_EVENTS.START_A_DIALOGUE, async (_event, props: CreateDialogueProps) => { + const { providerName, messages, messageId, selectedModel } = props; + const mainWindow = windowManager.get(WINDOW_NAMES.MAIN); + + if (!mainWindow) { + throw new Error('mainWindow not found'); + } + + try { + + const provider = createProvider(providerName); + const chunks = await provider?.chat(messages, selectedModel); + + if (!chunks) { + throw new Error('chunks or stream not found'); + } + + for await (const chunk of chunks) { + const chunkContent = { + messageId, + data: chunk + } + mainWindow.webContents.send(IPC_EVENTS.START_A_DIALOGUE + 'back' + messageId, chunkContent); + } + + } catch (error) { + const errorContent = { + messageId, + data: { + isEnd: true, + isError: true, + result: error instanceof Error ? error.message : String(error), + } + } + + mainWindow.webContents.send(IPC_EVENTS.START_A_DIALOGUE + 'back' + messageId, errorContent); + } + }) } diff --git a/src/main/wins/main.ts b/src/main/wins/main.ts deleted file mode 100644 index db307d9..0000000 --- a/src/main/wins/main.ts +++ /dev/null @@ -1,185 +0,0 @@ -import type { BrowserWindow } from 'electron' -import { ipcMain } from 'electron'; -import { WINDOW_NAMES, MAIN_WIN_SIZE, IPC_EVENTS, MENU_IDS, CONVERSATION_ITEM_MENU_IDS, CONVERSATION_LIST_MENU_IDS, MESSAGE_ITEM_MENU_IDS, CONFIG_KEYS } from '@common/constants' -import { createProvider } from '../providers' -import { windowManager } from '@main/service/window-service' -import { menuManager } from '@main/service/menu-service' -import { logManager } from '@main/service/logger' -import { configManager } from '@main/service/config-service' -import { trayManager } from '@main/service/tray-service' -import { TabManager } from '@service/tab-manager' -import path from 'node:path' - -const handleTray = (minimizeToTray: boolean) => { - if (minimizeToTray) { - trayManager.create(); - return; - } - - trayManager.destroy(); -} - -const registerMenus = (window: BrowserWindow) => { - - const conversationItemMenuItemClick = (id: string) => { - logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_ITEM}-${id}`) - window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_ITEM}`, id); - } - - menuManager.register(MENU_IDS.CONVERSATION_ITEM, [ - { - id: CONVERSATION_ITEM_MENU_IDS.PIN, - label: 'menu.conversation.pinConversation', - click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.PIN) - }, - { - id: CONVERSATION_ITEM_MENU_IDS.RENAME, - label: 'menu.conversation.renameConversation', - click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.RENAME) - }, - { - id: CONVERSATION_ITEM_MENU_IDS.DEL, - label: 'menu.conversation.delConversation', - click: () => conversationItemMenuItemClick(CONVERSATION_ITEM_MENU_IDS.DEL) - }, - ]) - - const conversationListMenuItemClick = (id: string) => { - logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_LIST}-${id}`) - window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.CONVERSATION_LIST}`, id); - } - - menuManager.register(MENU_IDS.CONVERSATION_LIST, [ - { - id: CONVERSATION_LIST_MENU_IDS.NEW_CONVERSATION, - label: 'menu.conversation.newConversation', - click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.NEW_CONVERSATION) - }, - { type: 'separator' }, - { - id: CONVERSATION_LIST_MENU_IDS.SORT_BY, label: 'menu.conversation.sortBy', submenu: [ - { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_CREATE_TIME, label: 'menu.conversation.sortByCreateTime', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_CREATE_TIME) }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_UPDATE_TIME, label: 'menu.conversation.sortByUpdateTime', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_UPDATE_TIME) }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_NAME, label: 'menu.conversation.sortByName', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_NAME) }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_BY_MODEL, label: 'menu.conversation.sortByModel', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_BY_MODEL) }, - { type: 'separator' }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_ASCENDING, label: 'menu.conversation.sortAscending', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_ASCENDING) }, - { id: CONVERSATION_LIST_MENU_IDS.SORT_DESCENDING, label: 'menu.conversation.sortDescending', type: 'radio', checked: false, click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.SORT_DESCENDING) }, - ] - }, - { - id: CONVERSATION_LIST_MENU_IDS.BATCH_OPERATIONS, - label: 'menu.conversation.batchOperations', - click: () => conversationListMenuItemClick(CONVERSATION_LIST_MENU_IDS.BATCH_OPERATIONS) - } - ]) - - const messageItemMenuItemClick = (id: string) => { - logManager.logUserOperation(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.MESSAGE_ITEM}-${id}`) - window.webContents.send(`${IPC_EVENTS.SHOW_CONTEXT_MENU}:${MENU_IDS.MESSAGE_ITEM}`, id); - } - - menuManager.register(MENU_IDS.MESSAGE_ITEM, [ - { - id: MESSAGE_ITEM_MENU_IDS.COPY, - label: 'menu.message.copyMessage', - click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.COPY) - }, - { - id: MESSAGE_ITEM_MENU_IDS.SELECT, - label: 'menu.message.selectMessage', - click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.SELECT) - }, - { type: 'separator' }, - { - id: MESSAGE_ITEM_MENU_IDS.DELETE, - label: 'menu.message.deleteMessage', - click: () => messageItemMenuItemClick(MESSAGE_ITEM_MENU_IDS.DELETE) - }, - ]) -} - -export function setupMainWindow() { - windowManager.onWindowCreate(WINDOW_NAMES.MAIN, (mainWindow) => { - let minimizeToTray = configManager.get(CONFIG_KEYS.MINIMIZE_TO_TRAY); - configManager.onConfigChange((config) => { - if (minimizeToTray === config[CONFIG_KEYS.MINIMIZE_TO_TRAY]) return; - minimizeToTray = config[CONFIG_KEYS.MINIMIZE_TO_TRAY]; - handleTray(minimizeToTray); - }); - handleTray(minimizeToTray); - registerMenus(mainWindow); - - const tabManager = new TabManager(mainWindow) - tabManager.enable() - let tabsInitialized = false - - mainWindow.on('closed', () => { - tabManager.destroy() - }) - - const getPageUrl = (page: string) => { - if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { - return `${MAIN_WINDOW_VITE_DEV_SERVER_URL}/html/${page}.html` - } - return `file://${path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/html/${page}.html`)}` - } - - const initTabs = () => { - if (tabsInitialized) return - tabsInitialized = true - tabManager.create(getPageUrl('home'), true) - tabManager.create(getPageUrl('knowledge'), false) - tabManager.create(getPageUrl('task'), false) - tabManager.create(getPageUrl('setting'), false) - } - - mainWindow.webContents.on('did-finish-load', () => { - const url = mainWindow.webContents.getURL() - const isDevRoot = MAIN_WINDOW_VITE_DEV_SERVER_URL && (url === MAIN_WINDOW_VITE_DEV_SERVER_URL || url === `${MAIN_WINDOW_VITE_DEV_SERVER_URL}/`) - if (url.includes('/html/index.html') || url.endsWith('index.html') || isDevRoot) { - initTabs() - } - }) - }); - windowManager.create(WINDOW_NAMES.MAIN, MAIN_WIN_SIZE); - - ipcMain.on(IPC_EVENTS.START_A_DIALOGUE, async (_event, props: CreateDialogueProps) => { - const { providerName, messages, messageId, selectedModel } = props; - const mainWindow = windowManager.get(WINDOW_NAMES.MAIN); - - if (!mainWindow) { - throw new Error('mainWindow not found'); - } - - try { - - const provider = createProvider(providerName); - const chunks = await provider?.chat(messages, selectedModel); - - if (!chunks) { - throw new Error('chunks or stream not found'); - } - - for await (const chunk of chunks) { - const chunkContent = { - messageId, - data: chunk - } - mainWindow.webContents.send(IPC_EVENTS.START_A_DIALOGUE + 'back' + messageId, chunkContent); - } - - } catch (error) { - const errorContent = { - messageId, - data: { - isEnd: true, - isError: true, - result: error instanceof Error ? error.message : String(error), - } - } - - mainWindow.webContents.send(IPC_EVENTS.START_A_DIALOGUE + 'back' + messageId, errorContent); - } - }) -} diff --git a/src/main/wins/setting.ts b/src/main/wins/setting.ts deleted file mode 100644 index 2d1dee9..0000000 --- a/src/main/wins/setting.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IPC_EVENTS, WINDOW_NAMES } from '@common/constants' -import { ipcMain } from 'electron' -import { windowManager } from '@main/service/window-service' - -export function setupSetttingWindow() { - ipcMain.on(`${IPC_EVENTS.OPEN_WINDOW}:${WINDOW_NAMES.SETTING}`, () => { - const settingWindow = windowManager.get(WINDOW_NAMES.SETTING); - if (settingWindow && !settingWindow.isDestroyed()) - return windowManager.focus(settingWindow); - - windowManager.create(WINDOW_NAMES.SETTING, { - width: 800, - height: 600, - minHeight: 600, - minWidth: 800, - }); - }) -} - -export default setupSetttingWindow; diff --git a/src/preload.ts b/src/preload.ts index 271aae7..1fa92ff 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -20,22 +20,6 @@ const api: WindowApi = { loadPage: (page: string) => ipcRenderer.invoke(IPC_EVENTS.APP_LOAD_PAGE, page) }, - tabs: { - create: (url?: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_CREATE, url), - list: () => ipcRenderer.invoke(IPC_EVENTS.TAB_LIST), - navigate: (tabId: string, url: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_NAVIGATE, { tabId, url }), - reload: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_RELOAD, tabId), - back: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_BACK, tabId), - forward: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_FORWARD, tabId), - switch: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_SWITCH, tabId), - close: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_CLOSE, tabId), - on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => { - const listener = (_e: any, payload: any) => handler(payload) - ipcRenderer.on(event, listener) - return () => ipcRenderer.removeListener(event, listener) - } - }, - // 通过 IPC 调用主进程 readFile: (filePath: string) => ipcRenderer.invoke(IPC_EVENTS.READ_FILE, filePath), diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 6dd241c..5dad25a 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -1,5 +1,9 @@ diff --git a/src/renderer/components/Layout/index.vue b/src/renderer/components/Layout/index.vue index f970d0d..b87083f 100644 --- a/src/renderer/components/Layout/index.vue +++ b/src/renderer/components/Layout/index.vue @@ -1,18 +1,19 @@ \ No newline at end of file diff --git a/src/renderer/components/SideMenus/index.vue b/src/renderer/components/SideMenus/index.vue index a5a5a8d..1c47e55 100644 --- a/src/renderer/components/SideMenus/index.vue +++ b/src/renderer/components/SideMenus/index.vue @@ -21,107 +21,16 @@ diff --git a/src/renderer/main.ts b/src/renderer/main.ts index b108594..8399cbf 100644 --- a/src/renderer/main.ts +++ b/src/renderer/main.ts @@ -10,7 +10,7 @@ import locale from 'element-plus/es/locale/lang/zh-cn' // 引入 i18n 插件 import i18n from './i18n' -// import './permission' +import './permission' // 样式文件隔离 import "./styles/index.css"; diff --git a/src/renderer/router/index.ts b/src/renderer/router/index.ts index 8d8bc14..cb2691e 100644 --- a/src/renderer/router/index.ts +++ b/src/renderer/router/index.ts @@ -5,12 +5,36 @@ const routes = [ path: '/', redirect: '/home' }, + { + path: "/login", + component: () => import("@renderer/views/login/index.vue"), + name: "Login", + meta: { requiresAuth: true }, + }, { path: "/home", component: () => import("@renderer/views/home/index.vue"), name: "Home", meta: { requiresAuth: true }, }, + { + path: "/task", + component: () => import("@renderer/views/task/index.vue"), + name: "Task", + meta: { requiresAuth: true }, + }, + { + path: "/knowledge", + component: () => import("@renderer/views/knowledge/index.vue"), + name: "Knowledge", + meta: { requiresAuth: true }, + }, + { + path: "/setting", + component: () => import("@renderer/views/setting/index.vue"), + name: "Setting", + meta: { requiresAuth: true }, + }, ]; const router = createRouter({ diff --git a/src/renderer/styles/index.css b/src/renderer/styles/index.css index ebcdea7..a1c9e3f 100644 --- a/src/renderer/styles/index.css +++ b/src/renderer/styles/index.css @@ -1,5 +1,6 @@ @import "tailwindcss"; @import "./theme/index.css"; +@import "./tailwind.css"; @plugin "@tailwindcss/typography"; .bg { @@ -58,60 +59,73 @@ body { border-radius: 10px; border: 1px solid #E5E8EE; } + .el-table { - margin-top: 0!important; + margin-top: 0 !important; } + .el-table th.el-table__cell { - background-color: #F5F7FA!important; + background-color: #F5F7FA !important; } + .el-dialog { - padding: 0!important; - border-radius: 20px!important; + padding: 0 !important; + border-radius: 20px !important; } + .el-dialog__header { - padding: 16px 20px!important; + padding: 16px 20px !important; border-bottom: 1px solid #E5E8EE; } + .el-dialog__title { font-weight: 600; font-size: 14px; color: #171717; } + .el-dialog__close { font-size: 14px; font-weight: 600; color: #525866; } + .dialog-footer { - padding: 16px 20px!important; + padding: 16px 20px !important; border-top: 1px solid #E5E8EE; } + .dialog-footer .el-button { - border-radius: 8px!important; - padding: 8px 10px!important; + border-radius: 8px !important; + padding: 8px 10px !important; font-weight: 500; font-size: 14px; } + .dialog-footer .el-button--primary { - background: #2B7FFF!important; - border-color: #2B7FFF!important; + background: #2B7FFF !important; + border-color: #2B7FFF !important; } -.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label:before, .el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label-wrap>.el-form-item__label:before { - content: ""!important; - margin-left: 0px!important; + +.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label:before, +.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label-wrap>.el-form-item__label:before { + content: "" !important; + margin-left: 0px !important; } .el-form-item__label { font-weight: 400; - font-size: 14px!important; - color: #171717!important; + font-size: 14px !important; + color: #171717 !important; } + .el-input__wrapper { - border-radius: 8px!important; + border-radius: 8px !important; /* border: 1px solid #E5E8EE; */ } -.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label:after, .el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label-wrap>.el-form-item__label:after { +.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label:after, +.el-form-item.is-required:not(.is-no-asterisk).asterisk-left>.el-form-item__label-wrap>.el-form-item__label:after { content: "*"; color: #FF4949; margin-left: 3px; diff --git a/src/renderer/styles/tailwind.css b/src/renderer/styles/tailwind.css new file mode 100644 index 0000000..4dfaa52 --- /dev/null +++ b/src/renderer/styles/tailwind.css @@ -0,0 +1,34 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + + /* Firefox */ + * { + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.08) transparent; + } + + /* Webkit */ + *::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + *::-webkit-scrollbar-track { + background: transparent; + } + + *::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.08); + border-radius: 9999px; + border: 2px solid transparent; + /* 产生内边距,mac 风格关键 */ + background-clip: padding-box; + } + + *::-webkit-scrollbar-thumb:hover { + background-color: rgba(0, 0, 0, 0.16); + } +} \ No newline at end of file diff --git a/src/renderer/views/dialog/index.ts b/src/renderer/views/dialog/index.ts deleted file mode 100644 index a075b40..0000000 --- a/src/renderer/views/dialog/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import '@renderer/styles/index.css' - -import errorHandler from '@utils/errorHandler' -import i18n from '@renderer/i18n' -import HeaderBar from '@renderer/components/HeaderBar/index.vue' -import DragRegion from '@renderer/components/DragRegion/index.vue' - -import Dialog from './index.vue' - -createApp(Dialog) - .use(i18n) - .use(errorHandler) - .component('HeaderBar', HeaderBar) - .component('DragRegion', DragRegion) - .mount('#app') diff --git a/src/renderer/views/home/ChatBox.vue b/src/renderer/views/home/ChatBox.vue new file mode 100644 index 0000000..514c792 --- /dev/null +++ b/src/renderer/views/home/ChatBox.vue @@ -0,0 +1,729 @@ +