diff --git a/.gitignore b/.gitignore index dcc534a..c4b3e65 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,9 @@ test_ui_design.py # 个人文档和测试数据 docs/my.md + +# 运行时数据 +data/ +*.db +*.sqlite +*.sqlite3 diff --git a/package-lock.json b/package-lock.json index afc8ccb..21d4363 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,11 @@ "@tiptap/starter-kit": "^3.15.3", "@tiptap/vue-3": "^3.15.3", "axios": "^1.6.0", + "better-sqlite3": "^11.0.0", + "cors": "^2.8.5", "diff-match-patch": "^1.0.5", "docx": "^9.5.1", + "express": "^4.18.2", "file-saver": "^2.0.5", "marked": "^9.1.0", "pinia": "^2.1.0", @@ -26,6 +29,7 @@ "@tailwindcss/postcss": "^4.1.18", "@vitejs/plugin-vue": "^4.5.0", "autoprefixer": "^10.4.23", + "concurrently": "^8.2.0", "dotenv": "^16.3.1", "playwright": "^1.57.0", "postcss": "^8.5.6", @@ -79,6 +83,16 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", @@ -1734,12 +1748,57 @@ "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", "license": "MIT" }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1794,6 +1853,26 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/baseline-browser-mapping": { "version": "2.9.14", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", @@ -1804,6 +1883,75 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -1839,6 +1987,39 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1852,6 +2033,22 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001764", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", @@ -1873,6 +2070,77 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1885,12 +2153,109 @@ "node": ">= 0.8" } }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/crelt": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", @@ -1903,6 +2268,56 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1912,6 +2327,25 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1989,6 +2423,12 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.267", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", @@ -1996,6 +2436,31 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.4", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", @@ -2114,6 +2579,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2132,12 +2603,120 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/file-saver": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", "license": "MIT" }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -2174,6 +2753,15 @@ "node": ">= 6" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fraction.js": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", @@ -2188,6 +2776,21 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2211,6 +2814,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2248,6 +2861,12 @@ "node": ">= 0.4" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -2266,6 +2885,16 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -2315,6 +2944,58 @@ "node": ">= 0.4" } }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -2327,6 +3008,31 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2627,6 +3333,13 @@ "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==", "license": "MIT" }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -2692,6 +3405,45 @@ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "license": "MIT" }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -2713,12 +3465,45 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "license": "ISC" }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -2737,6 +3522,33 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.86.0.tgz", + "integrity": "sha512-sn9Et4N3ynsetj3spsZR729DVlGH6iBG4RiDMV7HEp3guyOW6W3S0unGpLDxT50mXortGUMax/ykUNQXdqc/Xg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -2744,6 +3556,48 @@ "dev": true, "license": "MIT" }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/orderedmap": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", @@ -2756,6 +3610,21 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2867,6 +3736,32 @@ "dev": true, "license": "MIT" }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -3071,12 +3966,35 @@ "prosemirror-transform": "^1.1.0" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode.js": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", @@ -3086,6 +4004,60 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -3101,6 +4073,16 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.55.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", @@ -3151,12 +4133,28 @@ "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", "license": "MIT" }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/sax": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", @@ -3166,12 +4164,205 @@ "node": ">=11.0.0" } }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "license": "MIT" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3181,12 +4372,27 @@ "node": ">=0.10.0" } }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, "node_modules/sql.js": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.13.0.tgz", "integrity": "sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA==", "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3196,6 +4402,59 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/tailwindcss": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", @@ -3215,6 +4474,99 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "devOptional": true, + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -3227,6 +4579,15 @@ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -3264,6 +4625,24 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vite": { "version": "5.4.21", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", @@ -3378,6 +4757,30 @@ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "license": "MIT" }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", @@ -3395,6 +4798,45 @@ "bin": { "xml-js": "bin/cli.js" } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } } } } diff --git a/package.json b/package.json index 8f8c845..ab1c5bf 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "server": "node server/index.js", + "start": "concurrently \"npm run server\" \"npm run dev\"" }, "dependencies": { "@tailwindcss/vite": "^4.1.18", @@ -14,8 +16,11 @@ "@tiptap/starter-kit": "^3.15.3", "@tiptap/vue-3": "^3.15.3", "axios": "^1.6.0", + "better-sqlite3": "^11.0.0", + "cors": "^2.8.5", "diff-match-patch": "^1.0.5", "docx": "^9.5.1", + "express": "^4.18.2", "file-saver": "^2.0.5", "marked": "^9.1.0", "pinia": "^2.1.0", @@ -26,10 +31,11 @@ "@tailwindcss/postcss": "^4.1.18", "@vitejs/plugin-vue": "^4.5.0", "autoprefixer": "^10.4.23", + "concurrently": "^8.2.0", "dotenv": "^16.3.1", "playwright": "^1.57.0", "postcss": "^8.5.6", "tailwindcss": "^4.1.18", "vite": "^5.0.0" } -} +} \ No newline at end of file diff --git a/server/db.js b/server/db.js new file mode 100644 index 0000000..79cb315 --- /dev/null +++ b/server/db.js @@ -0,0 +1,130 @@ +import Database from 'better-sqlite3'; +import path from 'path'; +import fs from 'fs'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const DATA_DIR = path.join(__dirname, '../data'); +const DB_PATH = path.join(DATA_DIR, 'paradigms.db'); + +// 确保数据目录存在 +if (!fs.existsSync(DATA_DIR)) { + fs.mkdirSync(DATA_DIR, { recursive: true }); +} + +// 创建数据库连接 +const db = new Database(DB_PATH); + +// 初始化表结构 +db.exec(` + CREATE TABLE IF NOT EXISTS paradigms ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + icon TEXT DEFAULT 'sparkles', + tag_class TEXT DEFAULT 'bg-purple-900/30 text-purple-300', + tags TEXT, + specialized_prompt TEXT, + expert_guidelines TEXT, + is_custom INTEGER DEFAULT 1, + created_at TEXT, + updated_at TEXT + ) +`); + +console.log('📦 SQLite 数据库初始化完成:', DB_PATH); + +// CRUD 方法 +export function getAllParadigms() { + const rows = db.prepare('SELECT * FROM paradigms ORDER BY created_at DESC').all(); + return rows.map(row => ({ + id: row.id, + name: row.name, + description: row.description, + icon: row.icon, + tagClass: row.tag_class, + tags: row.tags ? JSON.parse(row.tags) : [], + specializedPrompt: row.specialized_prompt, + expertGuidelines: row.expert_guidelines ? JSON.parse(row.expert_guidelines) : [], + isCustom: Boolean(row.is_custom), + createdAt: row.created_at, + updatedAt: row.updated_at + })); +} + +export function getParadigmById(id) { + const row = db.prepare('SELECT * FROM paradigms WHERE id = ?').get(id); + if (!row) return null; + return { + id: row.id, + name: row.name, + description: row.description, + icon: row.icon, + tagClass: row.tag_class, + tags: row.tags ? JSON.parse(row.tags) : [], + specializedPrompt: row.specialized_prompt, + expertGuidelines: row.expert_guidelines ? JSON.parse(row.expert_guidelines) : [], + isCustom: Boolean(row.is_custom), + createdAt: row.created_at, + updatedAt: row.updated_at + }; +} + +export function createParadigm(paradigm) { + const stmt = db.prepare(` + INSERT INTO paradigms (id, name, description, icon, tag_class, tags, specialized_prompt, expert_guidelines, is_custom, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `); + + const now = new Date().toISOString(); + stmt.run( + paradigm.id, + paradigm.name, + paradigm.description || '', + paradigm.icon || 'sparkles', + paradigm.tagClass || 'bg-purple-900/30 text-purple-300', + JSON.stringify(paradigm.tags || []), + paradigm.specializedPrompt || '', + JSON.stringify(paradigm.expertGuidelines || []), + paradigm.isCustom ? 1 : 0, + paradigm.createdAt || now, + now + ); + + return getParadigmById(paradigm.id); +} + +export function updateParadigm(id, updates) { + const existing = getParadigmById(id); + if (!existing) return null; + + const merged = { ...existing, ...updates }; + const stmt = db.prepare(` + UPDATE paradigms + SET name = ?, description = ?, icon = ?, tag_class = ?, tags = ?, + specialized_prompt = ?, expert_guidelines = ?, updated_at = ? + WHERE id = ? + `); + + stmt.run( + merged.name, + merged.description, + merged.icon, + merged.tagClass, + JSON.stringify(merged.tags || []), + merged.specializedPrompt, + JSON.stringify(merged.expertGuidelines || []), + new Date().toISOString(), + id + ); + + return getParadigmById(id); +} + +export function deleteParadigm(id) { + const stmt = db.prepare('DELETE FROM paradigms WHERE id = ?'); + const result = stmt.run(id); + return result.changes > 0; +} + +export default db; diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..130cf7f --- /dev/null +++ b/server/index.js @@ -0,0 +1,93 @@ +import express from 'express'; +import cors from 'cors'; +import { getAllParadigms, getParadigmById, createParadigm, updateParadigm, deleteParadigm } from './db.js'; + +const app = express(); +const PORT = process.env.API_PORT || 3001; + +// 中间件 +app.use(cors()); +app.use(express.json()); + +// API 路由 +// 获取所有范式 +app.get('/api/paradigms', (req, res) => { + try { + const paradigms = getAllParadigms(); + res.json({ success: true, data: paradigms }); + } catch (error) { + console.error('获取范式列表失败:', error); + res.status(500).json({ success: false, error: error.message }); + } +}); + +// 获取单个范式 +app.get('/api/paradigms/:id', (req, res) => { + try { + const paradigm = getParadigmById(req.params.id); + if (!paradigm) { + return res.status(404).json({ success: false, error: '范式不存在' }); + } + res.json({ success: true, data: paradigm }); + } catch (error) { + console.error('获取范式失败:', error); + res.status(500).json({ success: false, error: error.message }); + } +}); + +// 创建范式 +app.post('/api/paradigms', (req, res) => { + try { + const paradigm = createParadigm(req.body); + console.log('✅ 创建范式:', paradigm.name); + res.status(201).json({ success: true, data: paradigm }); + } catch (error) { + console.error('创建范式失败:', error); + res.status(500).json({ success: false, error: error.message }); + } +}); + +// 更新范式 +app.put('/api/paradigms/:id', (req, res) => { + try { + const paradigm = updateParadigm(req.params.id, req.body); + if (!paradigm) { + return res.status(404).json({ success: false, error: '范式不存在' }); + } + console.log('✅ 更新范式:', paradigm.name); + res.json({ success: true, data: paradigm }); + } catch (error) { + console.error('更新范式失败:', error); + res.status(500).json({ success: false, error: error.message }); + } +}); + +// 删除范式 +app.delete('/api/paradigms/:id', (req, res) => { + try { + const deleted = deleteParadigm(req.params.id); + if (!deleted) { + return res.status(404).json({ success: false, error: '范式不存在' }); + } + console.log('✅ 删除范式:', req.params.id); + res.json({ success: true }); + } catch (error) { + console.error('删除范式失败:', error); + res.status(500).json({ success: false, error: error.message }); + } +}); + +// 健康检查 +app.get('/api/health', (req, res) => { + res.json({ status: 'ok', timestamp: new Date().toISOString() }); +}); + +// 启动服务器 +app.listen(PORT, () => { + console.log(`🚀 API 服务器已启动: http://localhost:${PORT}`); + console.log(` - GET /api/paradigms`); + console.log(` - GET /api/paradigms/:id`); + console.log(` - POST /api/paradigms`); + console.log(` - PUT /api/paradigms/:id`); + console.log(` - DELETE /api/paradigms/:id`); +}); diff --git a/src/App.vue b/src/App.vue index 6cabaf4..c83e02b 100644 --- a/src/App.vue +++ b/src/App.vue @@ -16,7 +16,13 @@ + + + - - + + appStore.currentPage) diff --git a/src/components/AnalysisPanel.vue b/src/components/AnalysisPanel.vue index 946ab04..9c3bb9f 100644 --- a/src/components/AnalysisPanel.vue +++ b/src/components/AnalysisPanel.vue @@ -174,6 +174,7 @@ import { ref, reactive, onMounted, onUnmounted, computed, watch } from 'vue' import { storeToRefs } from 'pinia' import { useAppStore } from '../stores/app' import { useDatabaseStore } from '../stores/database.js' +import { useParadigmStore } from '../stores/paradigm.js' import DeepSeekAPI from '../api/deepseek.js' import { getParadigmList } from '../config/paradigms.js' import RequirementParserPanel from './RequirementParserPanel.vue' @@ -186,6 +187,9 @@ const { analysisText, isAnalyzing } = storeToRefs(appStore) const dbStore = useDatabaseStore() const { isInitialized: dbInitialized } = storeToRefs(dbStore) +// 范式 Store +const paradigmStore = useParadigmStore() + // 选中的范式 const selectedParadigm = ref(null) @@ -231,13 +235,12 @@ const initParadigms = () => { // 先加载默认范式 const defaultParadigms = getParadigmList() - // 从本地存储加载自定义修改 + // 从本地存储加载自定义修改(仅用于默认范式的个性化配置) const savedCustomizations = localStorage.getItem('paradigmCustomizations') const customizations = savedCustomizations ? JSON.parse(savedCustomizations) : {} - // 从本地存储加载自定义范式 - const savedCustomParadigms = localStorage.getItem('customParadigms') - const customParadigms = savedCustomParadigms ? JSON.parse(savedCustomParadigms) : [] + // 从数据库 store 加载自定义范式(唯一来源) + const customParadigms = paradigmStore.customParadigms || [] // 合并默认范式和自定义修改 const mergedParadigms = defaultParadigms.map(p => { @@ -323,7 +326,7 @@ const resetEditForm = () => { // 保存范式 -const saveParadigm = () => { +const saveParadigm = async () => { const tags = editForm.tagsInput.split(',').map(t => t.trim()).filter(t => t) if (isAddMode.value) { @@ -337,17 +340,12 @@ const saveParadigm = () => { tagClass: editForm.tagClass, isCustom: true, createdAt: new Date().toISOString(), - // ⭐ 核心字段:完整 Prompt specializedPrompt: editForm.specializedPrompt } + // 保存到数据库(唯一来源) + await paradigmStore.addCustomParadigm(newParadigm) paradigms.value.push(newParadigm) - - // 保存到本地存储 - const savedCustomParadigms = localStorage.getItem('customParadigms') - const customParadigms = savedCustomParadigms ? JSON.parse(savedCustomParadigms) : [] - customParadigms.push(newParadigm) - localStorage.setItem('customParadigms', JSON.stringify(customParadigms)) } else { // 编辑现有范式 const index = paradigms.value.findIndex(p => p.id === editingParadigmId.value) @@ -359,24 +357,16 @@ const saveParadigm = () => { description: editForm.description, tags, tagClass: editForm.tagClass, - // ⭐ 核心字段:完整 Prompt specializedPrompt: editForm.specializedPrompt } paradigms.value[index] = updatedParadigm - // 根据是否是自定义范式决定存储位置 if (updatedParadigm.isCustom) { - // 更新自定义范式 - const savedCustomParadigms = localStorage.getItem('customParadigms') - const customParadigms = savedCustomParadigms ? JSON.parse(savedCustomParadigms) : [] - const customIndex = customParadigms.findIndex(p => p.id === editingParadigmId.value) - if (customIndex !== -1) { - customParadigms[customIndex] = updatedParadigm - localStorage.setItem('customParadigms', JSON.stringify(customParadigms)) - } + // 更新数据库中的自定义范式 + await paradigmStore.addCustomParadigm(updatedParadigm) } else { - // 保存对默认范式的自定义修改 + // 保存对默认范式的自定义修改(仍用 localStorage) const savedCustomizations = localStorage.getItem('paradigmCustomizations') const customizations = savedCustomizations ? JSON.parse(savedCustomizations) : {} customizations[editingParadigmId.value] = { @@ -406,7 +396,7 @@ const isCustomParadigm = (paradigm) => { } // 删除自定义范式 -const deleteParadigm = (paradigm) => { +const deleteParadigm = async (paradigm) => { if (!isCustomParadigm(paradigm)) return if (!confirm(`确定要删除"${paradigm.name}"吗?`)) return @@ -414,11 +404,8 @@ const deleteParadigm = (paradigm) => { // 从列表中移除 paradigms.value = paradigms.value.filter(p => p.id !== paradigm.id) - // 从本地存储中移除 - const savedCustomParadigms = localStorage.getItem('customParadigms') - const customParadigms = savedCustomParadigms ? JSON.parse(savedCustomParadigms) : [] - const filtered = customParadigms.filter(p => p.id !== paradigm.id) - localStorage.setItem('customParadigms', JSON.stringify(filtered)) + // 从数据库中删除(唯一来源) + await paradigmStore.deleteCustomParadigm(paradigm.id) // 如果正在选中该范式,清除选中状态 if (selectedParadigm.value?.id === paradigm.id) { diff --git a/src/components/ArticleFusionPanel.vue b/src/components/ArticleFusionPanel.vue new file mode 100644 index 0000000..1b001ee --- /dev/null +++ b/src/components/ArticleFusionPanel.vue @@ -0,0 +1,360 @@ + + + + + diff --git a/src/components/FusionResultPanel.vue b/src/components/FusionResultPanel.vue new file mode 100644 index 0000000..ed6a9d1 --- /dev/null +++ b/src/components/FusionResultPanel.vue @@ -0,0 +1,595 @@ + + + + + diff --git a/src/components/GlobalSidebar.vue b/src/components/GlobalSidebar.vue index d56bd68..61f6102 100644 --- a/src/components/GlobalSidebar.vue +++ b/src/components/GlobalSidebar.vue @@ -61,8 +61,10 @@ const currentPage = computed(() => appStore.currentPage) const navItems = [ { id: 'writer', label: 'AI 写作', icon: 'edit' }, + { id: 'mimicWriter', label: '以稿写稿', icon: 'copy' }, { id: 'analysis', label: '范式库', icon: 'analysis' }, { id: 'paradigmWriter', label: '范式写作', icon: 'article' }, + { id: 'articleFusion', label: '文章融合', icon: 'sparkles' }, { id: 'documents', label: '文稿库', icon: 'folder' }, { id: 'materials', label: '素材库', icon: 'chart' }, { id: 'rewrite', label: '范式润色', icon: 'sparkles' }, diff --git a/src/components/HomePage.vue b/src/components/HomePage.vue index aecb9dd..5e759ef 100644 --- a/src/components/HomePage.vue +++ b/src/components/HomePage.vue @@ -100,11 +100,13 @@ import { ref, onMounted } from 'vue' import { useAppStore } from '../stores/app' import { useDatabaseStore } from '../stores/database' +import { useParadigmStore } from '../stores/paradigm' import { getParadigmList } from '../config/paradigms' import IconLibrary from './icons/IconLibrary.vue' const appStore = useAppStore() const dbStore = useDatabaseStore() +const paradigmStore = useParadigmStore() // 统计数据 const stats = ref({ @@ -141,12 +143,20 @@ const quickActions = [ description: '管理和创建写作范式', icon: 'analysis', gradient: 'linear-gradient(135deg, #10b981, #06b6d4)' + }, + { + id: 'mimicWriter', + title: '以稿写稿', + description: '模仿范文风格创作新内容', + icon: 'copy', + gradient: 'linear-gradient(135deg, #8b5cf6, #ec4899)' } ] // 全部功能 const features = [ { id: 'writer', name: 'AI 写作', icon: 'edit' }, + { id: 'mimicWriter', name: '以稿写稿', icon: 'copy' }, { id: 'analysis', name: '范式库', icon: 'analysis' }, { id: 'paradigmWriter', name: '范式写作', icon: 'article' }, { id: 'documents', name: '文稿库', icon: 'folder' }, @@ -163,9 +173,9 @@ const navigateTo = (page) => { // 加载统计数据 onMounted(() => { - // 获取范式数量 + // 获取范式数量(默认 + 数据库自定义) const defaultParadigms = getParadigmList() - const customParadigms = JSON.parse(localStorage.getItem('customParadigms') || '[]') + const customParadigms = paradigmStore.customParadigms || [] stats.value.paradigms = defaultParadigms.length + customParadigms.length // 获取文稿和素材数量 diff --git a/src/components/MainContent.vue b/src/components/MainContent.vue index c3be63e..a782c97 100644 --- a/src/components/MainContent.vue +++ b/src/components/MainContent.vue @@ -494,11 +494,13 @@ import { computed, ref, watch } from 'vue' import { storeToRefs } from 'pinia' import { useAppStore } from '../stores/app' +import { useParadigmStore } from '../stores/paradigm' import { buildPrompt } from '../utils/promptBuilder.js' import { marked } from 'marked' import IconLibrary from './icons/IconLibrary.vue' const appStore = useAppStore() +const paradigmStore = useParadigmStore() const { currentPage, showPromptDebug, @@ -605,7 +607,7 @@ const closeParadigmEdit = () => { } // 保存范式编辑 -const saveParadigmEdit = () => { +const saveParadigmEdit = async () => { const form = paradigmEditState.value.editForm // 构建范式对象 @@ -623,22 +625,8 @@ const saveParadigmEdit = () => { createdAt: paradigmEditState.value.isAddMode ? new Date().toISOString() : undefined } - // 保存到 localStorage - const customParadigms = JSON.parse(localStorage.getItem('customParadigms') || '[]') - - if (paradigmEditState.value.isAddMode) { - customParadigms.push(paradigm) - } else { - const index = customParadigms.findIndex(p => p.id === paradigm.id) - if (index >= 0) { - customParadigms[index] = { ...customParadigms[index], ...paradigm } - } else { - // 可能是系统范式的修改,添加为新的自定义范式 - customParadigms.push(paradigm) - } - } - - localStorage.setItem('customParadigms', JSON.stringify(customParadigms)) + // 保存到数据库(唯一来源) + await paradigmStore.addCustomParadigm(paradigm) // 关闭编辑 closeParadigmEdit() diff --git a/src/components/MimicWriterPanel.vue b/src/components/MimicWriterPanel.vue new file mode 100644 index 0000000..9fdd6d8 --- /dev/null +++ b/src/components/MimicWriterPanel.vue @@ -0,0 +1,595 @@ + + + + + diff --git a/src/components/ParadigmSelectorModal.vue b/src/components/ParadigmSelectorModal.vue index 2981e91..61410c6 100644 --- a/src/components/ParadigmSelectorModal.vue +++ b/src/components/ParadigmSelectorModal.vue @@ -113,8 +113,11 @@ diff --git a/src/config/paradigms.js b/src/config/paradigms.js index 7647c3e..b86fe1a 100644 --- a/src/config/paradigms.js +++ b/src/config/paradigms.js @@ -326,23 +326,16 @@ export const getParadigmList = () => { } // 根据ID获取范式详情(同时查找内置和自定义范式) -export const getParadigmById = (id) => { +// 注意:需要传入 customParadigms 数组,因为此函数是同步的 +export const getParadigmById = (id, customParadigms = []) => { // 1. 先从内置范式中查找 if (PARADIGMS[id]) { return PARADIGMS[id] } - // 2. 从 localStorage 的自定义范式中查找 - try { - const savedCustomParadigms = localStorage.getItem('customParadigms') - if (savedCustomParadigms) { - const customParadigms = JSON.parse(savedCustomParadigms) - const found = customParadigms.find(p => p.id === id) - if (found) return found - } - } catch (e) { - console.error('获取自定义范式失败:', e) - } + // 2. 从传入的自定义范式数组中查找(来自 paradigmStore) + const found = customParadigms.find(p => p.id === id) + if (found) return found return null } diff --git a/src/db/index.js b/src/db/index.js index 74eb3d8..ceaff69 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -27,12 +27,12 @@ const loadSqlJs = () => { resolve(window.initSqlJs) return } - + // 动态加载脚本 const script = document.createElement('script') script.src = 'https://sql.js.org/dist/sql-wasm.js' script.async = true - + script.onload = () => { if (window.initSqlJs) { resolve(window.initSqlJs) @@ -40,7 +40,7 @@ const loadSqlJs = () => { reject(new Error('sql.js 加载失败')) } } - + script.onerror = () => reject(new Error('sql.js 脚本加载失败')) document.head.appendChild(script) }) @@ -48,22 +48,24 @@ const loadSqlJs = () => { export const initDatabase = async () => { if (db) return db - + try { // 从 CDN 加载 sql.js const initSqlJs = await loadSqlJs() - + // 加载 sql.js WASM SQL = await initSqlJs({ locateFile: file => `https://sql.js.org/dist/${file}` }) - + // 尝试从 IndexedDB 加载现有数据库 const savedData = await loadFromIndexedDB() - + if (savedData) { db = new SQL.Database(savedData) console.log('📦 从 IndexedDB 加载数据库成功') + // 对已有数据库执行迁移以添加新字段 + migrateDatabase() } else { db = new SQL.Database() console.log('🆕 创建新数据库') @@ -72,7 +74,7 @@ export const initDatabase = async () => { // 导入默认数据 await importDefaultData() } - + return db } catch (error) { console.error('❌ 数据库初始化失败:', error) @@ -99,7 +101,7 @@ const initTables = async () => { is_default INTEGER DEFAULT 0 ) `) - + // 素材摘录表 db.run(` CREATE TABLE IF NOT EXISTS reference_excerpts ( @@ -112,7 +114,7 @@ const initTables = async () => { FOREIGN KEY (reference_id) REFERENCES materials(id) ON DELETE CASCADE ) `) - + // 范式表 db.run(` CREATE TABLE IF NOT EXISTS paradigms ( @@ -137,7 +139,7 @@ const initTables = async () => { is_custom INTEGER DEFAULT 0 ) `) - + // 维度集表 db.run(` CREATE TABLE IF NOT EXISTS dimension_sets ( @@ -150,7 +152,7 @@ const initTables = async () => { is_custom INTEGER DEFAULT 0 ) `) - + // 维度表 db.run(` CREATE TABLE IF NOT EXISTS dimensions ( @@ -165,7 +167,7 @@ const initTables = async () => { FOREIGN KEY (dimension_set_id) REFERENCES dimension_sets(id) ON DELETE CASCADE ) `) - + // 用户配置表 db.run(` CREATE TABLE IF NOT EXISTS user_config ( @@ -174,7 +176,7 @@ const initTables = async () => { updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `) - + // 分析历史表 db.run(` CREATE TABLE IF NOT EXISTS analysis_history ( @@ -185,7 +187,7 @@ const initTables = async () => { created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `) - + // 文稿记录表 db.run(` CREATE TABLE IF NOT EXISTS documents ( @@ -202,7 +204,7 @@ const initTables = async () => { updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `) - + // 文稿版本历史表 db.run(` CREATE TABLE IF NOT EXISTS document_versions ( @@ -215,7 +217,7 @@ const initTables = async () => { FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE ) `) - + console.log('✅ 数据表初始化完成') // 运行数据库迁移 @@ -267,27 +269,27 @@ const migrateDatabase = () => { */ export const saveToIndexedDB = async () => { if (!db) return - + return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, 1) - + request.onerror = () => reject(request.error) - + request.onupgradeneeded = (event) => { const idb = event.target.result if (!idb.objectStoreNames.contains(DB_STORE)) { idb.createObjectStore(DB_STORE) } } - + request.onsuccess = (event) => { const idb = event.target.result const transaction = idb.transaction([DB_STORE], 'readwrite') const store = transaction.objectStore(DB_STORE) - + const data = db.export() const buffer = new Uint8Array(data) - + const putRequest = store.put(buffer, DB_KEY) putRequest.onsuccess = () => { console.log('💾 数据库已保存到 IndexedDB') @@ -304,21 +306,21 @@ export const saveToIndexedDB = async () => { const loadFromIndexedDB = async () => { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, 1) - + request.onerror = () => reject(request.error) - + request.onupgradeneeded = (event) => { const idb = event.target.result if (!idb.objectStoreNames.contains(DB_STORE)) { idb.createObjectStore(DB_STORE) } } - + request.onsuccess = (event) => { const idb = event.target.result const transaction = idb.transaction([DB_STORE], 'readonly') const store = transaction.objectStore(DB_STORE) - + const getRequest = store.get(DB_KEY) getRequest.onsuccess = () => { resolve(getRequest.result || null) @@ -337,17 +339,17 @@ const loadFromIndexedDB = async () => { */ export const query = (sql, params = []) => { if (!db) throw new Error('数据库未初始化') - + try { const stmt = db.prepare(sql) stmt.bind(params) - + const results = [] while (stmt.step()) { results.push(stmt.getAsObject()) } stmt.free() - + return results } catch (error) { console.error('查询失败:', sql, error) @@ -368,7 +370,7 @@ export const queryOne = (sql, params = []) => { */ export const execute = (sql, params = []) => { if (!db) throw new Error('数据库未初始化') - + try { db.run(sql, params) // 自动保存到 IndexedDB @@ -385,7 +387,7 @@ export const execute = (sql, params = []) => { */ export const executeBatch = (statements) => { if (!db) throw new Error('数据库未初始化') - + try { statements.forEach(({ sql, params = [] }) => { db.run(sql, params) @@ -407,7 +409,7 @@ export const executeBatch = (statements) => { */ export const getAllReferences = () => { const refs = query('SELECT * FROM materials ORDER BY created_at DESC') - + return refs.map(ref => ({ ...ref, tags: ref.tags ? JSON.parse(ref.tags) : [], @@ -422,7 +424,7 @@ export const getAllReferences = () => { export const getReferenceById = (id) => { const ref = queryOne('SELECT * FROM materials WHERE id = ?', [id]) if (!ref) return null - + return { ...ref, tags: ref.tags ? JSON.parse(ref.tags) : [], @@ -436,7 +438,7 @@ export const getReferenceById = (id) => { */ export const getReferencesByType = (type) => { const refs = query('SELECT * FROM materials WHERE type = ? ORDER BY created_at DESC', [type]) - + return refs.map(ref => ({ ...ref, tags: ref.tags ? JSON.parse(ref.tags) : [], @@ -450,7 +452,7 @@ export const getReferencesByType = (type) => { */ const getExcerptsByReferenceId = (referenceId) => { const excerpts = query('SELECT * FROM reference_excerpts WHERE reference_id = ?', [referenceId]) - + return excerpts.map(e => ({ ...e, applicableDimensions: e.applicable_dimensions ? JSON.parse(e.applicable_dimensions) : [] @@ -462,7 +464,7 @@ const getExcerptsByReferenceId = (referenceId) => { */ export const addReference = (reference) => { const id = reference.id || `ref-${Date.now()}` - + execute(` INSERT INTO materials (id, type, title, source, date, tags, related_dimension_sets, is_default) VALUES (?, ?, ?, ?, ?, ?, ?, ?) @@ -476,7 +478,7 @@ export const addReference = (reference) => { JSON.stringify(reference.relatedDimensionSets || []), reference.isDefault ? 1 : 0 ]) - + // 添加摘录 if (reference.excerpts?.length) { reference.excerpts.forEach((excerpt, index) => { @@ -493,7 +495,7 @@ export const addReference = (reference) => { ]) }) } - + return id } @@ -503,7 +505,7 @@ export const addReference = (reference) => { export const updateReference = (id, updates) => { const setClauses = [] const params = [] - + if (updates.type !== undefined) { setClauses.push('type = ?') params.push(updates.type) @@ -524,12 +526,12 @@ export const updateReference = (id, updates) => { setClauses.push('related_dimension_sets = ?') params.push(JSON.stringify(updates.relatedDimensionSets)) } - + setClauses.push('updated_at = CURRENT_TIMESTAMP') params.push(id) - + execute(`UPDATE materials SET ${setClauses.join(', ')} WHERE id = ?`, params) - + return true } @@ -684,7 +686,7 @@ export const deleteParadigm = (id) => { export const getConfig = (key, defaultValue = null) => { const result = queryOne('SELECT value FROM user_config WHERE key = ?', [key]) if (!result) return defaultValue - + try { return JSON.parse(result.value) } catch { @@ -697,13 +699,13 @@ export const getConfig = (key, defaultValue = null) => { */ export const setConfig = (key, value) => { const valueStr = typeof value === 'string' ? value : JSON.stringify(value) - + execute(` INSERT INTO user_config (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP) ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP `, [key, valueStr, valueStr]) - + return true } @@ -716,7 +718,7 @@ export const setConfig = (key, value) => { */ export const getAllDocuments = () => { const docs = query('SELECT * FROM documents ORDER BY updated_at DESC') - + return docs.map(doc => ({ ...doc, tags: doc.tags ? JSON.parse(doc.tags) : [], @@ -730,7 +732,7 @@ export const getAllDocuments = () => { export const getDocumentById = (id) => { const doc = queryOne('SELECT * FROM documents WHERE id = ?', [id]) if (!doc) return null - + return { ...doc, tags: doc.tags ? JSON.parse(doc.tags) : [], @@ -751,7 +753,7 @@ export const getDocumentVersions = (documentId) => { */ export const createDocument = (document) => { const id = document.id || `doc-${Date.now()}` - + execute(` INSERT INTO documents (id, title, content, paradigm_id, dimension_set_id, selected_refs, status, word_count, tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) @@ -766,7 +768,7 @@ export const createDocument = (document) => { document.wordCount || 0, JSON.stringify(document.tags || []) ]) - + return id } @@ -776,7 +778,7 @@ export const createDocument = (document) => { export const updateDocument = (id, updates) => { const setClauses = [] const params = [] - + if (updates.title !== undefined) { setClauses.push('title = ?') params.push(updates.title) @@ -804,12 +806,12 @@ export const updateDocument = (id, updates) => { setClauses.push('selected_refs = ?') params.push(JSON.stringify(updates.selectedRefs)) } - + setClauses.push('updated_at = CURRENT_TIMESTAMP') params.push(id) - + execute(`UPDATE documents SET ${setClauses.join(', ')} WHERE id = ?`, params) - + return true } @@ -820,12 +822,12 @@ export const saveDocumentVersion = (documentId, content, changeNote = '') => { // 获取当前最大版本号 const result = queryOne('SELECT MAX(version_number) as max_version FROM document_versions WHERE document_id = ?', [documentId]) const nextVersion = (result?.max_version || 0) + 1 - + execute(` INSERT INTO document_versions (document_id, content, version_number, change_note) VALUES (?, ?, ?, ?) `, [documentId, content, nextVersion, changeNote]) - + return nextVersion } @@ -843,7 +845,7 @@ export const deleteDocument = (id) => { */ export const getDocumentsByStatus = (status) => { const docs = query('SELECT * FROM documents WHERE status = ? ORDER BY updated_at DESC', [status]) - + return docs.map(doc => ({ ...doc, tags: doc.tags ? JSON.parse(doc.tags) : [], @@ -868,10 +870,10 @@ export const exportDatabase = () => { */ export const importDatabase = async (data) => { if (!SQL) throw new Error('SQL.js 未初始化') - + db = new SQL.Database(new Uint8Array(data)) await saveToIndexedDB() - + return true } @@ -893,12 +895,12 @@ export const exportAsJSON = () => { */ export const resetDatabase = async () => { if (!SQL) throw new Error('SQL.js 未初始化') - + db = new SQL.Database() await initTables() await importDefaultData() await saveToIndexedDB() - + return true } @@ -912,14 +914,14 @@ export const resetDatabase = async () => { const importDefaultData = async () => { // 导入默认素材 const { REFERENCES } = await import('../config/references.js') - + Object.values(REFERENCES).forEach(ref => { addReference({ ...ref, isDefault: true }) }) - + console.log('✅ 默认素材导入完成') } diff --git a/src/stores/app.js b/src/stores/app.js index 6a22607..cf0bd1a 100644 --- a/src/stores/app.js +++ b/src/stores/app.js @@ -4,8 +4,12 @@ import { config, modelProviders, getConfiguredProviders, getDefaultProvider } fr import DeepSeekAPI from '../api/deepseek.js' import { buildPrompt, createStreamParser, parseGhostwriterOutput } from '../utils/promptBuilder.js' import { PARADIGMS, getParadigmById, buildParadigmConstraints } from '../config/paradigms.js' +import { useParadigmStore } from './paradigm.js' export const useAppStore = defineStore('app', () => { + // 获取 paradigmStore 用于查找自定义范式 + const paradigmStore = useParadigmStore() + // 页面状态 const currentPage = ref('home') // 默认进入主页 @@ -86,6 +90,39 @@ export const useAppStore = defineStore('app', () => { } }) + // 以稿写稿相关 (Mimic Writing) + const mimicWriterState = ref({ + sourceArticle: '', + sourceTitle: '', + writingDirection: '', + styleAnalysis: '', + styleIntensity: 80, + preserveElements: ['结构', '语气', '用词'], + isAnalyzing: false, + isGenerating: false, + generatedContent: '', + thinkingContent: '', + // 分段仿写相关 + paragraphs: [], // 原稿拆分后的段落数组 + currentParagraphIndex: 0, // 当前正在处理的段落索引 + generatedParagraphs: [], // 已生成的段落数组 + totalParagraphs: 0 // 总段落数 + }) + + // 文章融合相关 (Article Fusion) + const articleFusionState = ref({ + titleA: '', + titleB: '', + articleA: '', + articleB: '', + fusionMode: 'balanced', // balanced | preferA | preferB + isAnalyzing: false, + stage: '', // 'analyzing' | 'generating' + thinkingContent: '', + analysisResult: null, // { articleA: { pros: [], cons: [] }, articleB: { pros: [], cons: [] }, fusionStrategy: '' } + fusionResult: '' + }) + // UI状态 const showPromptDebug = ref(false) const showRefInput = ref(false) @@ -409,7 +446,7 @@ ${draft} // 加载范式预设 const loadParadigmPreset = (paradigmId) => { - const paradigm = getParadigmById(paradigmId) + const paradigm = getParadigmById(paradigmId, paradigmStore.customParadigms) if (!paradigm) { console.warn('Store: Paradigm not found:', paradigmId) return @@ -481,7 +518,7 @@ ${draft} * 范式写作:解析大纲模板为章节 */ const parseOutlineToSections = (paradigmId) => { - const paradigm = getParadigmById(paradigmId) + const paradigm = getParadigmById(paradigmId, paradigmStore.customParadigms) if (!paradigm) return [] // 如果有 outlineTemplate,则解析它 @@ -639,7 +676,7 @@ ${draft} * 范式写作:加载范式 */ const loadParadigmForWriting = (paradigmId) => { - const paradigm = getParadigmById(paradigmId) + const paradigm = getParadigmById(paradigmId, paradigmStore.customParadigms) if (!paradigm) return paradigmWriterState.value.selectedParadigmId = paradigmId @@ -658,7 +695,7 @@ ${draft} const section = paradigmWriterState.value.sections[index] if (!section) return - const paradigm = getParadigmById(paradigmWriterState.value.selectedParadigmId) + const paradigm = getParadigmById(paradigmWriterState.value.selectedParadigmId, paradigmStore.customParadigms) if (!paradigm) return section.isGenerating = true @@ -817,9 +854,451 @@ ${isMaterialType && materialDataText ? '4. ⚠️ 特别注意:用户提供的 generateSectionContentAction, updateTotalGeneratedContent, + // 以稿写稿 + mimicWriterState, + analyzeMimicStyleAction: async () => { + if (!mimicWriterState.value.sourceArticle.trim()) { + throw new Error('请先提供原稿内容') + } + + mimicWriterState.value.isAnalyzing = true + mimicWriterState.value.styleAnalysis = '' + + try { + const api = new DeepSeekAPI({ + url: apiUrl.value, + key: apiKey.value, + model: currentProvider.value.model, + appId: currentProvider.value.appId + }) + + await api.analyzeContent(mimicWriterState.value.sourceArticle, (content) => { + mimicWriterState.value.styleAnalysis += content + }) + } catch (error) { + console.error('Style analysis failed:', error) + throw error + } finally { + mimicWriterState.value.isAnalyzing = false + } + }, + + mimicGenerateAction: async () => { + if (!mimicWriterState.value.sourceArticle.trim() || !mimicWriterState.value.writingDirection.trim()) { + throw new Error('请提供原稿内容和写作方向') + } + + mimicWriterState.value.isGenerating = true + mimicWriterState.value.generatedContent = '' + mimicWriterState.value.thinkingContent = '' + + try { + const api = new DeepSeekAPI({ + url: apiUrl.value, + key: apiKey.value, + model: currentProvider.value.model, + appId: currentProvider.value.appId + }) + + const streamParser = createStreamParser() + + // 构建以稿写稿专用 Prompt + const { buildMimicPrompt, MIMIC_WRITER_SYSTEM_PROMPT } = await import('../utils/promptBuilder.js') + + const prompt = buildMimicPrompt( + mimicWriterState.value.sourceArticle, + mimicWriterState.value.writingDirection, + { + styleIntensity: mimicWriterState.value.styleIntensity, + preserveElements: mimicWriterState.value.preserveElements + } + ) + + await api._streamRequest([ + { role: 'system', content: MIMIC_WRITER_SYSTEM_PROMPT }, + { role: 'user', content: prompt } + ], { temperature: 0.7, max_tokens: 8192 }, (chunk) => { + const { section, buffer } = streamParser.process(chunk) + + const thinkingMatch = buffer.match(/([\s\S]*?)(?:<\/thinking>|$)/) + const draftMatch = buffer.match(/([\s\S]*?)(?:<\/draft>|$)/) + + if (thinkingMatch) { + mimicWriterState.value.thinkingContent = thinkingMatch[1].trim() + } + + if (draftMatch) { + mimicWriterState.value.generatedContent = draftMatch[1].trim() + } else if (!thinkingMatch && buffer.length > 50) { + mimicWriterState.value.generatedContent = buffer + } + }) + } catch (error) { + console.error('Mimic generation failed:', error) + throw error + } finally { + mimicWriterState.value.isGenerating = false + } + }, + + mimicContinueAction: async () => { + if (!mimicWriterState.value.sourceArticle.trim() || !mimicWriterState.value.generatedContent) { + throw new Error('缺少上下文,无法续写') + } + + mimicWriterState.value.isGenerating = true + + try { + const api = new DeepSeekAPI({ + url: apiUrl.value, + key: apiKey.value, + model: currentProvider.value.model, + appId: currentProvider.value.appId + }) + + const streamParser = createStreamParser() + + const { buildMimicContinuePrompt, MIMIC_WRITER_SYSTEM_PROMPT } = await import('../utils/promptBuilder.js') + + const prompt = buildMimicContinuePrompt( + mimicWriterState.value.sourceArticle, + mimicWriterState.value.writingDirection, + mimicWriterState.value.generatedContent, + { + styleIntensity: mimicWriterState.value.styleIntensity, + preserveElements: mimicWriterState.value.preserveElements + } + ) + + await api._streamRequest([ + { role: 'system', content: MIMIC_WRITER_SYSTEM_PROMPT }, + { role: 'user', content: prompt } + ], { temperature: 0.7, max_tokens: 8192 }, (chunk) => { + const { buffer } = streamParser.process(chunk) + + // 对于续写,我们主要关注 draft 内容 + const draftMatch = buffer.match(/([\s\S]*?)(?:<\/draft>|$)/) + + if (draftMatch) { + // 这里我们需要小心,不要替换掉之前的内容,而是追加 + // 但 buffer 是累积的,且 _streamRequest 的 callback 是针对整个流的累积处理吗? + // 不,通常 buffer 是当前流的缓冲区。 + // 让我们查看 _streamRequest 的实现。 + // 假设 buffer 包含了从本次请求开始到现在的所有内容。 + // 我们需要将新生成的内容追加到 mimicWriterState.value.generatedContent + + // 为了避免重复追加,我们需要记录本次续写的起始点或者只追加增量 + // 但简单的方法是:我们在开始续写前先保存旧内容 + // 这是一个流式过程。 + // 更好的做法是: + // 1. 在 Action 开始时记录 oldContent = mimicWriterState.value.generatedContent + // 2. 在 callback 中: mimicWriterState.value.generatedContent = oldContent + newContent + + } + }) + + // 由于 _streamRequest 的 callback 机制可能比较复杂(尤其是 streamParser), + // 重新审视 streamParser.process(chunk)。它通常返回 { section, buffer }。 + // buffer 是累积的当前 section 的内容。 + // 所以我们可以在 Action 外部变量记录 oldContent。 + + const oldContent = mimicWriterState.value.generatedContent + + await api._streamRequest([ + { role: 'system', content: MIMIC_WRITER_SYSTEM_PROMPT }, + { role: 'user', content: prompt } + ], { temperature: 0.7, max_tokens: 8192 }, (chunk) => { + const { buffer } = streamParser.process(chunk) + const draftMatch = buffer.match(/([\s\S]*?)(?:<\/draft>|$)/) + + if (draftMatch) { + // 追加新生成的内容 + mimicWriterState.value.generatedContent = oldContent + '\n\n' + draftMatch[1].trim() + } + }) + + } catch (error) { + console.error('Mimic continue failed:', error) + throw error + } finally { + mimicWriterState.value.isGenerating = false + } + }, + + // 分段仿写:拆分段落(使用 AI 智能拆分) + splitParagraphsAction: async () => { + const text = mimicWriterState.value.sourceArticle.trim() + if (!text) return [] + + mimicWriterState.value.isAnalyzing = true + + try { + const api = new DeepSeekAPI({ + url: apiUrl.value, + key: apiKey.value, + model: currentProvider.value.model, + appId: currentProvider.value.appId + }) + + const splitPrompt = `请分析以下文章的结构,将其拆分成语义完整的段落。 + +
+${text} +
+ +要求: +1. 按照内容的语义逻辑进行拆分,而非简单按换行符 +2. 每个段落应是一个完整的观点或主题 +3. 保持原文内容不变,只做拆分 +4. 以 JSON 数组格式输出,每个元素是一个段落 + +请直接输出 JSON 数组,格式如下: +\`\`\`json +["第一段内容...", "第二段内容...", "第三段内容..."] +\`\`\`` + + let responseText = '' + await api._streamRequest([ + { role: 'system', content: '你是一位专业的文本分析专家,擅长识别文章结构。' }, + { role: 'user', content: splitPrompt } + ], { temperature: 0.3, max_tokens: 4000 }, (chunk) => { + responseText += chunk + }) + + // 解析 JSON 结果 + const jsonMatch = responseText.match(/```json\n?([\s\S]*?)\n?```/) + let paragraphs = [] + + if (jsonMatch) { + try { + paragraphs = JSON.parse(jsonMatch[1]) + } catch (e) { + console.error('解析段落 JSON 失败:', e) + // 降级:使用原始正则拆分 + paragraphs = text + .split(/\n{2,}|\n(?=[\u3000\u0020]{2})|(?<=。)\n/) + .map(p => p.trim()) + .filter(p => p.length > 20) + } + } else { + // 降级:使用原始正则拆分 + paragraphs = text + .split(/\n{2,}|\n(?=[\u3000\u0020]{2})|(?<=。)\n/) + .map(p => p.trim()) + .filter(p => p.length > 20) + } + + mimicWriterState.value.paragraphs = paragraphs + mimicWriterState.value.totalParagraphs = paragraphs.length + mimicWriterState.value.generatedParagraphs = [] + mimicWriterState.value.currentParagraphIndex = 0 + mimicWriterState.value.generatedContent = '' + + console.log(`✅ AI 智能拆分完成,共 ${paragraphs.length} 段`) + return paragraphs + } catch (error) { + console.error('AI 拆分段落失败:', error) + throw error + } finally { + mimicWriterState.value.isAnalyzing = false + } + }, + + // 分段仿写:逐段生成 + mimicAllParagraphsAction: async () => { + const { paragraphs, writingDirection, styleIntensity, preserveElements } = mimicWriterState.value + + if (!paragraphs.length || !writingDirection.trim()) { + throw new Error('请先拆分段落并输入写作方向') + } + + mimicWriterState.value.isGenerating = true + mimicWriterState.value.generatedParagraphs = [] + mimicWriterState.value.generatedContent = '' + + try { + const api = new DeepSeekAPI({ + url: apiUrl.value, + key: apiKey.value, + model: currentProvider.value.model, + appId: currentProvider.value.appId + }) + + const { buildParagraphMimicPrompt } = await import('../utils/promptBuilder.js') + + for (let i = 0; i < paragraphs.length; i++) { + mimicWriterState.value.currentParagraphIndex = i + + const prompt = buildParagraphMimicPrompt( + paragraphs[i], + writingDirection, + i, + paragraphs.length, + { styleIntensity, preserveElements }, + mimicWriterState.value.generatedParagraphs // 传入已生成的段落作为上下文 + ) + + let paragraphContent = '' + + await api._streamRequest([ + { role: 'system', content: '你是一名专业的仿写助手。请严格按照要求仿写段落,直接输出内容,不要任何解释。' }, + { role: 'user', content: prompt } + ], { temperature: 0.7, max_tokens: 2048 }, (chunk) => { + paragraphContent += chunk + // 实时更新预览 + const allContent = [ + ...mimicWriterState.value.generatedParagraphs, + paragraphContent + ].join('\n\n') + mimicWriterState.value.generatedContent = allContent + }) + + // 保存当前段落 + mimicWriterState.value.generatedParagraphs.push(paragraphContent.trim()) + } + + // 最终合并 + mimicWriterState.value.generatedContent = mimicWriterState.value.generatedParagraphs.join('\n\n') + + } catch (error) { + console.error('Paragraph mimic failed:', error) + throw error + } finally { + mimicWriterState.value.isGenerating = false + } + }, + // 范式编辑 paradigmEditState, + // 文章融合 + articleFusionState, + startArticleFusionAction: async ({ titleA, titleB, articleA, articleB, fusionMode }) => { + // 保存输入 + articleFusionState.value.titleA = titleA + articleFusionState.value.titleB = titleB + articleFusionState.value.articleA = articleA + articleFusionState.value.articleB = articleB + articleFusionState.value.fusionMode = fusionMode + articleFusionState.value.isAnalyzing = true + articleFusionState.value.stage = 'analyzing' + articleFusionState.value.thinkingContent = '' + articleFusionState.value.analysisResult = null + articleFusionState.value.fusionResult = '' + + try { + const api = new DeepSeekAPI({ + url: apiUrl.value, + key: apiKey.value, + model: currentProvider.value.model, + appId: currentProvider.value.appId + }) + + // 第一阶段:分析优劣 + const analysisPrompt = `你是一位专业的文章分析专家。请分析以下两篇关于相同主题的文章,比较它们的优缺点。 + +## 文章 A${titleA ? `: ${titleA}` : ''} +${articleA} + +## 文章 B${titleB ? `: ${titleB}` : ''} +${articleB} + +请按以下JSON格式输出分析结果: +\`\`\`json +{ + "articleA": { + "pros": ["优点1", "优点2", "优点3"], + "cons": ["不足1", "不足2"] + }, + "articleB": { + "pros": ["优点1", "优点2", "优点3"], + "cons": ["不足1", "不足2"] + }, + "fusionStrategy": "融合策略说明:取A的XXX,取B的YYY..." +} +\`\`\` +` + + let analysisText = '' + await api._streamRequest([ + { role: 'system', content: '你是一位专业的内容分析与整合专家。' }, + { role: 'user', content: analysisPrompt } + ], { temperature: 0.3, max_tokens: 2000 }, (chunk) => { + analysisText += chunk + articleFusionState.value.thinkingContent = analysisText + }) + + // 解析分析结果 + const jsonMatch = analysisText.match(/```json\n?([\s\S]*?)\n?```/) + if (jsonMatch) { + try { + articleFusionState.value.analysisResult = JSON.parse(jsonMatch[1]) + } catch (e) { + console.error('解析分析结果失败:', e) + articleFusionState.value.analysisResult = { + articleA: { pros: ['分析结果解析失败'], cons: [] }, + articleB: { pros: ['分析结果解析失败'], cons: [] }, + fusionStrategy: analysisText + } + } + } + + // 第二阶段:生成融合文章 + articleFusionState.value.stage = 'generating' + articleFusionState.value.thinkingContent = '' + + const modeInstruction = fusionMode === 'preferA' + ? '请以文章A的内容和风格为主,适当补充文章B的优点。' + : fusionMode === 'preferB' + ? '请以文章B的内容和风格为主,适当补充文章A的优点。' + : '请均衡融合两篇文章的精华内容。' + + const fusionPrompt = `基于以下分析结果,融合两篇文章生成一篇更优秀的文章。 + +## 分析结果 +${JSON.stringify(articleFusionState.value.analysisResult, null, 2)} + +## 原文章 A +${articleA} + +## 原文章 B +${articleB} + +## 融合要求 +${modeInstruction} + +请直接输出融合后的完整文章,不需要任何解释。确保: +1. 综合两篇文章的优点 +2. 避免两篇文章的不足 +3. 保持逻辑连贯和风格统一 +4. 使用清晰的段落结构 +` + + let fusionText = '' + await api._streamRequest([ + { role: 'system', content: '你是一位专业的文章融合专家,擅长将多篇文章的精华整合为一篇高质量的文章。' }, + { role: 'user', content: fusionPrompt } + ], { temperature: 0.7, max_tokens: 4000 }, (chunk) => { + fusionText += chunk + articleFusionState.value.fusionResult = fusionText + }) + + } catch (error) { + console.error('文章融合失败:', error) + throw error + } finally { + articleFusionState.value.isAnalyzing = false + articleFusionState.value.stage = '' + } + }, + regenerateFusionAction: async () => { + // 使用已存储的数据重新生成 + const { titleA, titleB, articleA, articleB, fusionMode } = articleFusionState.value + if (!articleA || !articleB) return + + await this.startArticleFusionAction({ titleA, titleB, articleA, articleB, fusionMode }) + }, + // 方法 switchPage, setCurrentPage, diff --git a/src/stores/paradigm.js b/src/stores/paradigm.js index e5bddf7..c579881 100644 --- a/src/stores/paradigm.js +++ b/src/stores/paradigm.js @@ -1,17 +1,19 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' +// API 基础 URL +const API_BASE = 'http://localhost:3001/api' + /** * 自定义范式管理 Store - * 用于管理用户通过需求文档生成的自定义范式 - * 现已迁移到数据库存储(IndexedDB/SQLite) + * 使用服务器端 SQLite 数据库存储 */ export const useParadigmStore = defineStore('paradigm', () => { - // 自定义范式列表(从数据库加载) + // 自定义范式列表(从 API 加载) const customParadigms = ref([]) - // 数据库是否已初始化 - const isDbInitialized = ref(false) + // API 是否可用 + const isApiReady = ref(false) // 当前正在编辑的范式 const editingParadigm = ref(null) @@ -23,91 +25,59 @@ export const useParadigmStore = defineStore('paradigm', () => { const parsingProgress = ref('') /** - * 从数据库加载自定义范式 - * 如果 localStorage 中有旧数据,自动迁移到数据库 + * 从服务器 API 加载自定义范式 */ async function loadCustomParadigms() { try { - // 动态导入数据库模块(避免循环依赖) - const { getAllParadigms, addParadigm, updateParadigm } = await import('../db/index.js') + const response = await fetch(`${API_BASE}/paradigms`) + const result = await response.json() - // 从数据库加载所有范式 - const allParadigms = getAllParadigms() - - // 过滤出自定义范式 - customParadigms.value = allParadigms.filter(p => p.isCustom) - - // 检查 localStorage 中是否有旧数据需要迁移 - const localStorageData = localStorage.getItem('customParadigms') - if (localStorageData && !isDbInitialized.value) { - try { - const oldParadigms = JSON.parse(localStorageData) - - if (oldParadigms.length > 0) { - console.log(`🔄 检测到 localStorage 中有 ${oldParadigms.length} 个范式,开始迁移...`) - - // 迁移每个范式到数据库 - for (const oldParadigm of oldParadigms) { - // 检查数据库中是否已存在 - const existing = customParadigms.value.find(p => p.id === oldParadigm.id) - - if (existing) { - // 更新现有范式 - updateParadigm(oldParadigm.id, oldParadigm) - } else { - // 添加新范式 - addParadigm({ - ...oldParadigm, - isCustom: true, - autoMatchRefs: oldParadigm.autoMatchRefs !== false - }) - } - } - - // 重新加载从数据库 - const updatedParadigms = getAllParadigms() - customParadigms.value = updatedParadigms.filter(p => p.isCustom) - - // 清除 localStorage 中的旧数据 - localStorage.removeItem('customParadigms') - - console.log('✅ 范式迁移完成,localStorage 已清理') - } - } catch (migrateError) { - console.error('❌ 迁移 localStorage 数据失败:', migrateError) - } + if (result.success) { + customParadigms.value = result.data + isApiReady.value = true + console.log(`✅ 从服务器加载了 ${result.data.length} 个自定义范式`) + } else { + console.error('加载自定义范式失败:', result.error) + customParadigms.value = [] } - - isDbInitialized.value = true } catch (error) { - console.error('加载自定义范式失败:', error) + console.error('API 请求失败(服务器可能未启动):', error.message) customParadigms.value = [] + isApiReady.value = false } } /** - * 保存单个自定义范式到数据库(替换旧的批量保存) + * 保存单个自定义范式到服务器 + * 新范式使用 POST,已存在的使用 PUT */ async function saveCustomParadigm(paradigm) { try { - const { addParadigm, updateParadigm } = await import('../db/index.js') + // 先尝试 PUT 更新 + const putResponse = await fetch(`${API_BASE}/paradigms/${paradigm.id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(paradigm) + }) - // 检查是否已存在 - const existing = customParadigms.value.find(p => p.id === paradigm.id) - - if (existing) { - // 更新现有范式 - updateParadigm(paradigm.id, paradigm) - } else { - // 添加新范式 - addParadigm({ - ...paradigm, - isCustom: true, - autoMatchRefs: paradigm.autoMatchRefs !== false + if (putResponse.status === 404) { + // 范式不存在,使用 POST 创建 + const postResponse = await fetch(`${API_BASE}/paradigms`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(paradigm) }) + const result = await postResponse.json() + if (!result.success) throw new Error(result.error) + console.log('✅ 创建新范式:', paradigm.name) + } else { + const result = await putResponse.json() + if (!result.success) throw new Error(result.error) + console.log('✅ 更新范式:', paradigm.name) } } catch (error) { console.error('保存自定义范式失败:', error) + throw error } } @@ -134,11 +104,19 @@ export const useParadigmStore = defineStore('paradigm', () => { */ async function deleteCustomParadigm(paradigmId) { try { - const { deleteParadigm } = await import('../db/index.js') - deleteParadigm(paradigmId) - customParadigms.value = customParadigms.value.filter(p => p.id !== paradigmId) + const response = await fetch(`${API_BASE}/paradigms/${paradigmId}`, { + method: 'DELETE' + }) + const result = await response.json() + + if (result.success) { + customParadigms.value = customParadigms.value.filter(p => p.id !== paradigmId) + } else { + throw new Error(result.error) + } } catch (error) { console.error('删除自定义范式失败:', error) + throw error } } @@ -218,7 +196,7 @@ export const useParadigmStore = defineStore('paradigm', () => { * @param {string} jsonString - JSON字符串 * @returns {boolean} 是否成功 */ - function importParadigm(jsonString) { + async function importParadigm(jsonString) { try { const paradigm = JSON.parse(jsonString) @@ -231,7 +209,7 @@ export const useParadigmStore = defineStore('paradigm', () => { paradigm.id = `custom-imported-${Date.now()}` paradigm.createdAt = new Date().toISOString() - addCustomParadigm(paradigm) + await addCustomParadigm(paradigm) return true } catch (error) { console.error('导入范式失败:', error) @@ -244,8 +222,10 @@ export const useParadigmStore = defineStore('paradigm', () => { */ async function clearAllCustomParadigms() { try { - const { execute } = await import('../db/index.js') - execute('DELETE FROM paradigms WHERE is_custom = 1') + // 逐个删除 + for (const paradigm of customParadigms.value) { + await deleteCustomParadigm(paradigm.id) + } customParadigms.value = [] } catch (error) { console.error('清空自定义范式失败:', error) @@ -290,7 +270,6 @@ export const useParadigmStore = defineStore('paradigm', () => { expertGuidelines: paradigm.expertGuidelines.map(g => { if (typeof g === 'string') { // 字符串格式:转换为对象格式并推断 scope - // 提取前面的关键词作为标题(最多15个字符) const title = g.length > 15 ? g.substring(0, 15) : g return { title: title, @@ -320,7 +299,7 @@ export const useParadigmStore = defineStore('paradigm', () => { editingParadigm, isParsing, parsingProgress, - isDbInitialized, + isApiReady, // 计算属性 customParadigmCount, @@ -328,7 +307,7 @@ export const useParadigmStore = defineStore('paradigm', () => { // 方法 loadCustomParadigms, - saveCustomParadigm, // 单个保存(新) + saveCustomParadigm, addCustomParadigm, deleteCustomParadigm, getCustomParadigmById, diff --git a/src/utils/promptBuilder.js b/src/utils/promptBuilder.js index c1b0bec..691117d 100644 --- a/src/utils/promptBuilder.js +++ b/src/utils/promptBuilder.js @@ -20,7 +20,7 @@ export const buildSystemPrompt = (paradigm = null) => { 注意:仅输出正文,不要包含任何"好的,这是文章"之类的废话。
` } - + // 默认使用 Ghostwriter Protocol return GHOSTWRITER_SYSTEM_PROMPT } @@ -80,7 +80,7 @@ const selectRepresentativeReferences = (references) => { if (references.length <= MAX_REFERENCES) { return references.map(ref => ({ ...ref, - content: ref.content.length > MAX_REFERENCE_LENGTH + content: ref.content.length > MAX_REFERENCE_LENGTH ? ref.content.substring(0, MAX_REFERENCE_LENGTH) + '...(已截断)' : ref.content })) @@ -93,7 +93,7 @@ const selectRepresentativeReferences = (references) => { }) return sorted.slice(0, MAX_REFERENCES).map(ref => ({ ...ref, - content: ref.content.length > MAX_REFERENCE_LENGTH + content: ref.content.length > MAX_REFERENCE_LENGTH ? ref.content.substring(0, MAX_REFERENCE_LENGTH) + '...(已截断)' : ref.content })) @@ -107,8 +107,8 @@ export const buildPrompt = (task, constraints, references) => { let refText if (selectedRefs.length > 0) { refText = selectedRefs.map((ref, idx) => { - const styleInfo = ref.styleTags?.length > 0 - ? `\n[已识别风格: ${ref.styleTags.join(', ')}]` + const styleInfo = ref.styleTags?.length > 0 + ? `\n[已识别风格: ${ref.styleTags.join(', ')}]` : '' return `--- 范文 ${idx + 1}: ${ref.title} ---${styleInfo}\n${ref.content}` }).join('\n\n') @@ -139,7 +139,7 @@ ${task} export const parseGhostwriterOutput = (fullText) => { const thinkingMatch = fullText.match(/([\s\S]*?)<\/thinking>/) const draftMatch = fullText.match(/([\s\S]*?)<\/draft>/) - + return { thinking: thinkingMatch ? thinkingMatch[1].trim() : '', draft: draftMatch ? draftMatch[1].trim() : fullText.trim(), @@ -151,12 +151,12 @@ export const parseGhostwriterOutput = (fullText) => { export const createStreamParser = () => { let buffer = '' let currentSection = 'pre' // 'pre' | 'thinking' | 'between' | 'draft' | 'post' - + return { // 处理新的流式内容片段 process(chunk) { buffer += chunk - + // 检测状态转换 if (currentSection === 'pre' && buffer.includes('')) { currentSection = 'thinking' @@ -170,15 +170,15 @@ export const createStreamParser = () => { if (currentSection === 'draft' && buffer.includes('')) { currentSection = 'post' } - + return { section: currentSection, buffer } }, - + // 获取最终解析结果 getResult() { return parseGhostwriterOutput(buffer) }, - + // 获取当前 section getCurrentSection() { return currentSection @@ -189,12 +189,12 @@ export const createStreamParser = () => { export const parseStreamResponse = (chunk) => { const lines = chunk.split('\n').filter(line => line.trim()) const contents = [] - + for (const line of lines) { if (line.startsWith('data: ')) { const data = line.slice(6) if (data === '[DONE]') continue - + try { const parsed = JSON.parse(data) const content = parsed.choices?.[0]?.delta?.content || '' @@ -204,6 +204,135 @@ export const parseStreamResponse = (chunk) => { } } } - + return contents.join('') } + +// ============================================ +// Mimic Writer Protocol +// ============================================ + +export const MIMIC_WRITER_SYSTEM_PROMPT = `# Role +你是一名世界级的"影子写手"。你的核心能力是深度分析一篇文章的写作风格,并将这种风格完美复刻到一个全新的主题上。 + +# Input +1. : 需要分析和模仿的原稿 +2. : 用户希望撰写的新内容方向 +3. : 风格模仿强度 (0-100) +4. : 需要保留的风格元素 + +# Process (Thinking Chain) +不要直接开始写作!你必须严格按照以下步骤进行思维推理: + +Step 1: 【风格指纹提取】 (Style Analysis) +仔细阅读 ,从以下维度提取"风格指纹": +- 语调 (Tone): 是严肃、幽默、辛辣还是温情? +- 句式 (Sentence Structure): 喜欢长短句结合,还是大量使用从句?是否喜欢反问? +- 词调与偏好 (Vocabulary): 偏向学术词汇、互联网黑话还是平实口语?是否有特定高频词? +- 结构逻辑 (Logic): 文章的起承转合是如何安排的?是归纳法还是演绎法? + +Step 2: 【大纲构建】 (Structural Planning) +基于 的要求,结合 Step 1 分析出的结构特点,规划文章大纲。 + +Step 3: 【风格迁移创作】 (Drafting) +撰写正文。在这一步,你需要按照 的强度来模仿原稿风格: +- 如果强度高(80-100),请极力模仿其句式、词调。 +- 同时必须确保 中提到的元素被完美保留和体现。 + +Step 4: 【一致性自检】 (Verification) +检查生成的内容是否真的像原稿风格,如果偏差较大,请进行微调。 + +# Output Format +请严格按照以下 XML 格式输出你的思考过程和最终结果: + + +在此处输出你的 Step 1 风格分析 和 Step 2 大纲规划。 + + + +在此处输出最终的文章内容。 +注意:仅输出正文,不要包含任何多余的废话。 + +` + +export const buildMimicPrompt = (sourceArticle, writingDirection, options = {}) => { + return ` +${sourceArticle} + + + +${writingDirection} + + + +${options.styleIntensity || 80} + + + +${(options.preserveElements || []).join(', ')} +` +} + +export const buildMimicContinuePrompt = (sourceArticle, writingDirection, existingContent, options) => { + return ` +${sourceArticle} + + + +${writingDirection} + + + +${existingContent.slice(-2000)} + + + +请接续上面已写的内容,继续完成后续部分。 +要求: +1. 保持与原稿一致的风格强度(${options.styleIntensity || 80}%) +2. 重点保留这些元素:${(options.preserveElements || []).join('、')} +3. 不要重复已写内容,直接从停顿处续写 +4. 输出格式:新续写的内容 +` +} + +export const buildParagraphMimicPrompt = (originalParagraph, writingDirection, paragraphIndex, totalParagraphs, options, previousGeneratedParagraphs = []) => { + // 构建上下文:前文的要点摘要(而非全文,避免重复) + let contextSection = '' + if (previousGeneratedParagraphs.length > 0) { + // 只提供前文的主题提示,不提供完整内容 + const lastParagraph = previousGeneratedParagraphs[previousGeneratedParagraphs.length - 1] + // 截取最后一段的开头作为衔接提示 + const lastParagraphStart = lastParagraph.substring(0, Math.min(50, lastParagraph.length)) + contextSection = ` + +前文末尾:「${lastParagraphStart}...」 +已完成段落数:${previousGeneratedParagraphs.length} + +` + } + + return ` +${originalParagraph} + + + +${writingDirection} + +${contextSection} + +这是第 ${paragraphIndex + 1}/${totalParagraphs} 段。 +风格强度:${options.styleIntensity || 80}% +保留元素:${(options.preserveElements || []).join('、')} + + + +请仿照上面原段落的写作风格,按照新的写作方向创作对应的新段落。 +要求: +1. 保持原段落的结构、语气和行文节奏 +2. 内容要符合新的写作方向 +3. 段落长度与原段落相近 +4. ${previousGeneratedParagraphs.length > 0 ? '承接前文自然过渡,但绝对不要重复前面段落已经讲过的内容' : '这是开头段落,注意引入主题'} +5. 直接输出新段落内容,不要任何解释或标记 +` +} diff --git a/src/utils/requirementParser.js b/src/utils/requirementParser.js index 9bc559f..5264e83 100644 --- a/src/utils/requirementParser.js +++ b/src/utils/requirementParser.js @@ -136,6 +136,11 @@ export function buildParadigmObject(parsedConfig, sourceDocPath = null) { createdAt: new Date().toISOString(), sourceDoc: sourceDocPath, + // UI 显示必需字段 + icon: parsedConfig.metadata.icon || 'sparkles', // 默认使用 sparkles 图标 + tagClass: parsedConfig.metadata.tagClass || 'bg-purple-900/30 text-purple-300', // 默认紫色 + tags: parsedConfig.metadata.keyRequirements || [], // 使用关键要求作为标签 + specializedPrompt: parsedConfig.specializedPrompt, expertGuidelines: parsedConfig.expertGuidelines,