diff --git a/.gitignore b/.gitignore index d7803c8..b02b952 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,18 @@ dev-dist # debugs *.http +temp +phyEngine/ +*Circuit*/ +*circuit*/ +src/views/CircuitLab2D.vue +src/views/CircuitLab2D_Simple.vue +src/services/circuitSav.ts +src/config/components.ts +src/composables/useSimulation.ts +src/composables/useCircuitEngine.ts +src/assets/styles/circuit-lab.css +src/composables/useCamera.ts +.gitignore +src/types/circuit.ts +src/composables/useDragAndDrop.ts diff --git a/package-lock.json b/package-lock.json index 754f4c3..d9bbb3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "date-fns": "^4.1.0", "highlight.js": "^11.11.1", "katex": "^0.16.45", + "md-editor-v3": "^6.5.0", "mermaid": "^11.15.0", "prettier": "^3.6.2", "vue": "^3.5.24", @@ -1464,6 +1465,407 @@ "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", "license": "Apache-2.0" }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.2", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.2.tgz", + "integrity": "sha512-G5FPkgIiLjOgZMjqVjvuKQ1rGPtHogLldJr33eFJdVLtmwY+giGrlv/ewljLz6b9BSQLkjxuwBc6g6omDM+YxQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", + "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-angular": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@codemirror/lang-angular/-/lang-angular-0.1.4.tgz", + "integrity": "sha512-oap+gsltb/fzdlTQWD6BFF4bSLKcDnlxDsLdePiJpCVNKWXSTAbiiQeYI3UmES+BLAdkmIC1WjyztC1pi/bX4g==", + "license": "MIT", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.3" + } + }, + "node_modules/@codemirror/lang-cpp": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-cpp/-/lang-cpp-6.0.3.tgz", + "integrity": "sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/cpp": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-go": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-go/-/lang-go-6.0.1.tgz", + "integrity": "sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/go": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", + "integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.12" + } + }, + "node_modules/@codemirror/lang-java": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.2.tgz", + "integrity": "sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/java": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-jinja": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-jinja/-/lang-jinja-6.0.1.tgz", + "integrity": "sha512-P5kyHLObzjtbGj16h+hyvZTxJhSjBEeSx4wMjbnAf3b0uwTy2+F0zGjMZL4PQOm/mh2eGZ5xUDVZXgwP783Nsw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.2.0", + "@lezer/lr": "^1.4.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-less": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-less/-/lang-less-6.0.2.tgz", + "integrity": "sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==", + "license": "MIT", + "dependencies": { + "@codemirror/lang-css": "^6.2.0", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-liquid": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-liquid/-/lang-liquid-6.3.2.tgz", + "integrity": "sha512-6PDVU3ZnfeYyz1at1E/ttorErZvZFXXt1OPhtfe1EZJ2V2iDFa0CwPqPgG5F7NXN0yONGoBogKmFAafKTqlwIw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.1" + } + }, + "node_modules/@codemirror/lang-markdown": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz", + "integrity": "sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.7.1", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.3.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/markdown": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-php": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.2.tgz", + "integrity": "sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==", + "license": "MIT", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/php": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-python": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.2.1.tgz", + "integrity": "sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/python": "^1.1.4" + } + }, + "node_modules/@codemirror/lang-rust": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.2.tgz", + "integrity": "sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/rust": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-sass": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sass/-/lang-sass-6.0.2.tgz", + "integrity": "sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==", + "license": "MIT", + "dependencies": { + "@codemirror/lang-css": "^6.2.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/sass": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-sql": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz", + "integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-vue": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-vue/-/lang-vue-0.1.3.tgz", + "integrity": "sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==", + "license": "MIT", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-javascript": "^6.1.2", + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.1" + } + }, + "node_modules/@codemirror/lang-wast": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-wast/-/lang-wast-6.0.2.tgz", + "integrity": "sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-yaml": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.3.tgz", + "integrity": "sha512-AZ8DJBuXGVHybpBQhmZtgew5//4hv3tdkXnr3vDmOUMJRuB6vn/uuwtmTOTlqEaQFg3hQSVeA90NmvIQyUV6FQ==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.2.0", + "@lezer/lr": "^1.0.0", + "@lezer/yaml": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", + "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/language-data": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/language-data/-/language-data-6.5.2.tgz", + "integrity": "sha512-CPkWBKrNS8stYbEU5kwBwTf3JB1kghlbh4FSAwzGW2TEscdeHHH4FGysREW86Mqnj3Qn09s0/6Ea/TutmoTobg==", + "license": "MIT", + "dependencies": { + "@codemirror/lang-angular": "^0.1.0", + "@codemirror/lang-cpp": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-go": "^6.0.0", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-java": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/lang-jinja": "^6.0.0", + "@codemirror/lang-json": "^6.0.0", + "@codemirror/lang-less": "^6.0.0", + "@codemirror/lang-liquid": "^6.0.0", + "@codemirror/lang-markdown": "^6.0.0", + "@codemirror/lang-php": "^6.0.0", + "@codemirror/lang-python": "^6.0.0", + "@codemirror/lang-rust": "^6.0.0", + "@codemirror/lang-sass": "^6.0.0", + "@codemirror/lang-sql": "^6.0.0", + "@codemirror/lang-vue": "^0.1.1", + "@codemirror/lang-wast": "^6.0.0", + "@codemirror/lang-xml": "^6.0.0", + "@codemirror/lang-yaml": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/legacy-modes": "^6.4.0" + } + }, + "node_modules/@codemirror/legacy-modes": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.3.tgz", + "integrity": "sha512-xCsmIzH78MyWkib9jlPaaun57XNkfbMIhagfaZVd0iLTqlpw3jXaIcbZm72MTmmn64eTZpBVNjbyYh+QXnxRsg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.6.tgz", + "integrity": "sha512-6Kp7r6XfCi/D/5sdXieMfg9pJU1bUEx96WITuLU6ESaKizCz0QHFMjY/TaFSbigDdEAIgi93itLBIUETP4oK+A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.42.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.0.tgz", + "integrity": "sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", + "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz", + "integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.6.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@css-render/plugin-bem": { "version": "0.15.14", "dev": true, @@ -2223,6 +2625,189 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@lezer/common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", + "license": "MIT" + }, + "node_modules/@lezer/cpp": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.5.tgz", + "integrity": "sha512-DIhSXmYtJKLehrjzDFN+2cPt547ySQ41nA8yqcDf/GxMc+YM736xqltFkvADL2M0VebU5I+3+4ks2Vv+Kyq3Aw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/css": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.3.tgz", + "integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/go": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.1.tgz", + "integrity": "sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/java": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.3.tgz", + "integrity": "sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/markdown": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.3.tgz", + "integrity": "sha512-jpGm5Ps+XErS+xA4urw7ogEGkeZOahVQF21Z6oECF0sj+2liwZopd2+I8uH5I/vZsRuuze3OxBREIANLf6KKUw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@lezer/php": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.5.tgz", + "integrity": "sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.1.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.18.tgz", + "integrity": "sha512-31FiUrU7z9+d/ElGQLJFXl+dKOdx0jALlP3KEOsGTex8mvj+SoE1FgItcHWK/axkxCHGUSpqIHt6JAWfWu9Rhg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/rust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz", + "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/sass": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lezer/sass/-/sass-1.1.0.tgz", + "integrity": "sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/yaml": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.4.tgz", + "integrity": "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.4.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, "node_modules/@mermaid-js/parser": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.1.tgz", @@ -3030,6 +3615,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, "node_modules/@types/lodash": { "version": "4.17.20", "dev": true, @@ -3043,6 +3634,22 @@ "@types/lodash": "*" } }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.10.1", "dev": true, @@ -3307,6 +3914,18 @@ "d3-transition": "^3.0.1" } }, + "node_modules/@vavt/copy2clipboard": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@vavt/copy2clipboard/-/copy2clipboard-1.0.3.tgz", + "integrity": "sha512-HtG48r2FBYp9eRvGB3QGmtRBH1zzRRAVvFbGgFstOwz4/DDaNiX0uZc3YVKPydqgOav26pibr9MtoCaWxn7aeA==", + "license": "MIT" + }, + "node_modules/@vavt/util": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@vavt/util/-/util-2.1.2.tgz", + "integrity": "sha512-L3UbSJthJwr3wq0x93O5TrCepimrmVZaIl2ciZbeL18G5++gBhJXNhcH7RcVk/6rr3SavWOvwhig0mqRLoR7dw==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-vue": { "version": "6.0.1", "dev": true, @@ -3579,7 +4198,6 @@ }, "node_modules/argparse": { "version": "2.0.1", - "dev": true, "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { @@ -3914,6 +4532,49 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chevrotain": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-12.0.0.tgz", + "integrity": "sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "12.0.0", + "@chevrotain/gast": "12.0.0", + "@chevrotain/regexp-to-ast": "12.0.0", + "@chevrotain/types": "12.0.0", + "@chevrotain/utils": "12.0.0" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.4.1.tgz", + "integrity": "sha512-PvVJm3oGqrveUVW2Vt/eZGeiAIsJszYweUcYwcskg9e+IubNYKKD+rHHem7A6XVO22eDAL+inxNIGAzZ/VIWlA==", + "license": "MIT", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^12.0.0" + } + }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "dev": true, @@ -3932,7 +4593,6 @@ }, "node_modules/commander": { "version": "2.20.3", - "dev": true, "license": "MIT" }, "node_modules/common-tags": { @@ -3980,6 +4640,12 @@ "layout-base": "^1.0.0" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "dev": true, @@ -4026,6 +4692,12 @@ "node": ">=4" } }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==", + "license": "MIT" + }, "node_modules/csstype": { "version": "3.2.0", "license": "MIT" @@ -6442,6 +7114,15 @@ "node": ">= 0.8.0" } }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "dev": true, @@ -6492,6 +7173,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-vue-next": { + "version": "0.543.0", + "resolved": "https://registry.npmjs.org/lucide-vue-next/-/lucide-vue-next-0.543.0.tgz", + "integrity": "sha512-Az5kpNm/koKAwSNIKjsZ4uHV2tVfmlQlcHwFBygQ8gc5/jFg7An9OrxgDy/aE5m+HLx7VfLYqDxLr8gWecZbQA==", + "license": "ISC", + "peerDependencies": { + "vue": ">=3.0.1" + } + }, "node_modules/magic-string": { "version": "0.30.21", "license": "MIT", @@ -6499,6 +7189,47 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-image-figures": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/markdown-it-image-figures/-/markdown-it-image-figures-2.1.1.tgz", + "integrity": "sha512-mwXSQ2nPeVUzCMIE3HlLvjRioopiqyJLNph0pyx38yf9mpqFDhNGnMpAXF9/A2Xv0oiF2cVyg9xwfF0HNAz05g==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "markdown-it": "*" + } + }, + "node_modules/markdown-it-sub": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-sub/-/markdown-it-sub-2.0.0.tgz", + "integrity": "sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA==", + "license": "MIT" + }, + "node_modules/markdown-it-sup": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-sup/-/markdown-it-sup-2.0.0.tgz", + "integrity": "sha512-5VgmdKlkBd8sgXuoDoxMpiU+BiEt3I49GItBzzw7Mxq9CxvnhE/k09HFli09zgfFDRixDQDfDxi0mgBCXtaTvA==", + "license": "MIT" + }, "node_modules/marked": { "version": "16.4.2", "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", @@ -6521,6 +7252,49 @@ "node": ">= 0.4" } }, + "node_modules/md-editor-v3": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/md-editor-v3/-/md-editor-v3-6.5.0.tgz", + "integrity": "sha512-GhGhPebfJeOrFuybFMUZRM6GzLMhu7Sg03MWLwO6tuIKMkRN71/SeTbLYYzY95qpaMbXDMWKlTkj3UNf9Bz4dg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.18.7", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-markdown": "^6.3.4", + "@codemirror/language": "^6.11.3", + "@codemirror/language-data": "^6.5.1", + "@codemirror/search": "^6.5.11", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.2", + "@lezer/highlight": "^1.2.1", + "@types/markdown-it": "^14.0.1", + "@vavt/copy2clipboard": "^1.0.1", + "@vavt/util": "^2.1.2", + "codemirror": "^6.0.2", + "lucide-vue-next": "^0.543.0", + "markdown-it": "^14.0.0", + "markdown-it-image-figures": "^2.1.1", + "markdown-it-sub": "^2.0.0", + "markdown-it-sup": "^2.0.0", + "medium-zoom": "^1.1.0", + "xss": "^1.0.15" + }, + "peerDependencies": { + "vue": "^3.5.3" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/medium-zoom": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/medium-zoom/-/medium-zoom-1.1.0.tgz", + "integrity": "sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "dev": true, @@ -7086,6 +7860,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "dev": true, @@ -7840,6 +8623,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, "node_modules/stylis": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", @@ -8090,6 +8879,12 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, "node_modules/ufo": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", @@ -8477,6 +9272,12 @@ "vue": "^3.0.11" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/webidl-conversions": { "version": "4.0.2", "dev": true, @@ -8946,6 +9747,22 @@ "node": ">=12" } }, + "node_modules/xss": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz", + "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==", + "license": "MIT", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "dev": true, diff --git a/package.json b/package.json index 9ccf023..8d69e98 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "date-fns": "^4.1.0", "highlight.js": "^11.11.1", "katex": "^0.16.45", + "md-editor-v3": "^6.5.0", "mermaid": "^11.15.0", "prettier": "^3.6.2", "vue": "^3.5.24", diff --git a/src/components/messages/MessageItem.vue b/src/components/messages/MessageItem.vue index 664e487..831c744 100644 --- a/src/components/messages/MessageItem.vue +++ b/src/components/messages/MessageItem.vue @@ -35,9 +35,8 @@ import showUserCard from '@popup/userProfileDialog.ts' import { getAvatarUrl } from '@services/getUserCurentAvatarByID' import storageManager from '@storage/index.ts' import { formatDate, getPath, getAnonymousAvatarByNickname } from '@services/utils' -import { useI18n } from 'vue-i18n' import type { CommentResult } from '@services/../pl-serve-type-main/type/main' - +import { useI18n } from 'vue-i18n' const { t } = useI18n() const props = defineProps<{ diff --git a/src/config/user.config.ts b/src/config/user.config.ts index ca04575..5f330ed 100644 --- a/src/config/user.config.ts +++ b/src/config/user.config.ts @@ -6,11 +6,26 @@ type SupportedLocale = 'English' | 'Chinese' | 'German' | 'Japanese' | 'French' function createLanguageOptions() { return [ - { label: i18n.global.t('settings.languageOptions.chinese'), value: 'Chinese' }, - { label: i18n.global.t('settings.languageOptions.english'), value: 'English' }, - { label: i18n.global.t('settings.languageOptions.german'), value: 'German' }, - { label: i18n.global.t('settings.languageOptions.japanese'), value: 'Japanese' }, - { label: i18n.global.t('settings.languageOptions.french'), value: 'French' }, + { + label: i18n.global.t('settings.languageOptions.chinese'), + value: 'Chinese', + }, + { + label: i18n.global.t('settings.languageOptions.english'), + value: 'English', + }, + { + label: i18n.global.t('settings.languageOptions.german'), + value: 'German', + }, + { + label: i18n.global.t('settings.languageOptions.japanese'), + value: 'Japanese', + }, + { + label: i18n.global.t('settings.languageOptions.french'), + value: 'French', + }, ] } @@ -33,7 +48,8 @@ export const settingsConfig = [ options: createLanguageOptions(), callBack: (newValue: string) => { i18n.global.locale.value = newValue as SupportedLocale - const languageItem = settingsConfig[0]?.items.find((item) => item.key === 'language') + const generalItems = settingsConfig[0]?.items || [] + const languageItem = generalItems.find((item) => item.key === 'language') if (languageItem?.type === 'link') { languageItem.options = createLanguageOptions() } diff --git a/src/i18n/de.ts b/src/i18n/de.ts index 52711cc..6ffa024 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -113,6 +113,7 @@ export default { copyInternalLink: 'Internen Link kopieren', copyExternalLink: 'Externen Link kopieren', changeCover: 'Cover ändern', + editWork: 'Werk bearbeiten', }, date: { justNow: 'Gerade eben', @@ -154,6 +155,33 @@ export default { off: 'Aus', }, }, + mdEditor: { + title: 'Markdown-Editor', + refreshWorks: 'Aktualisieren', + workListAria: 'Werkliste', + searchPlaceholder: 'Werke suchen', + emptyWorks: 'Keine bearbeitbaren Werke.', + subjectPlaceholder: 'Titel', + publish: 'Veröffentlichen', + previewTitle: 'Vorschau', + rendering: 'Wird gerendert...', + selectWork: 'Wählen Sie links ein Werk aus.', + loginRequiredTitle: 'Anmeldung erforderlich', + loginRequiredContent: 'Melden Sie sich an, bevor Sie Werke bearbeiten.', + login: 'Anmelden', + saveSuccess: 'Gespeichert', + emptyPreview: 'Kein Inhalt', + untitled: 'Ohne Titel', + noPermission: 'Dieses Konto kann dieses Werk nicht bearbeiten.', + readSummaryFailed: 'Werkzusammenfassung konnte nicht gelesen werden: {status}', + fetchWorksFailed: 'Werke konnten nicht geladen werden: {status}', + readWorkspaceFailed: 'Arbeitsbereich konnte nicht gelesen werden: {status}', + saveWorkFailed: 'Werk konnte nicht gespeichert werden: {status}', + changeCover: 'Cover ändern', + uploadImgHint: 'Bild-Upload wird noch nicht unterstützt', + editTags: 'Tags bearbeiten', + tagPlaceholder: 'Tag eingeben und Enter drücken', + }, footer: { home: 'Startseite', blackHole: 'Schwarzes Loch', diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 91fa9a2..2907d33 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -113,6 +113,7 @@ export default { copyInternalLink: 'Copy Internal Link', copyExternalLink: 'Copy External Link', changeCover: 'Change Cover', + editWork: 'Edit Work', }, date: { justNow: 'Just now', @@ -153,6 +154,33 @@ export default { off: 'Off', }, }, + mdEditor: { + title: 'Markdown Editor', + refreshWorks: 'Refresh', + workListAria: 'Work list', + searchPlaceholder: 'Search works', + emptyWorks: 'No editable works.', + subjectPlaceholder: 'Title', + publish: 'Publish', + previewTitle: 'Preview', + rendering: 'Rendering...', + selectWork: 'Select a work on the left.', + loginRequiredTitle: 'Login required', + loginRequiredContent: 'Log in before editing works.', + login: 'Login', + saveSuccess: 'Saved', + emptyPreview: 'No content', + untitled: 'Untitled', + noPermission: 'This account cannot edit this work.', + readSummaryFailed: 'Failed to read work summary: {status}', + fetchWorksFailed: 'Failed to fetch works: {status}', + readWorkspaceFailed: 'Failed to read workspace: {status}', + saveWorkFailed: 'Failed to save work: {status}', + changeCover: 'Change Cover', + uploadImgHint: 'Image upload is not supported yet', + editTags: 'Edit Tags', + tagPlaceholder: 'Type tag and press Enter', + }, footer: { home: 'Home', blackHole: 'Black Hole', diff --git a/src/i18n/fr.ts b/src/i18n/fr.ts index d9c55e7..42f7615 100644 --- a/src/i18n/fr.ts +++ b/src/i18n/fr.ts @@ -113,6 +113,7 @@ export default { copyInternalLink: 'Copier le lien interne', copyExternalLink: 'Copier le lien de partage', changeCover: 'Changer la couverture', + editWork: "Modifier l'œuvre", }, date: { justNow: "À l'instant", @@ -154,6 +155,33 @@ export default { off: 'Désactivé', }, }, + mdEditor: { + title: 'Éditeur Markdown', + refreshWorks: 'Actualiser', + workListAria: 'Liste des œuvres', + searchPlaceholder: 'Rechercher des œuvres', + emptyWorks: 'Aucune œuvre modifiable.', + subjectPlaceholder: 'Titre', + publish: 'Publier', + previewTitle: 'Aperçu', + rendering: 'Rendu...', + selectWork: 'Sélectionnez une œuvre à gauche.', + loginRequiredTitle: 'Connexion requise', + loginRequiredContent: 'Connectez-vous avant de modifier des œuvres.', + login: 'Connexion', + saveSuccess: 'Enregistré', + emptyPreview: 'Aucun contenu', + untitled: 'Sans titre', + noPermission: 'Ce compte ne peut pas modifier cette œuvre.', + readSummaryFailed: 'Impossible de lire le résumé de l’œuvre : {status}', + fetchWorksFailed: 'Impossible de charger les œuvres : {status}', + readWorkspaceFailed: 'Impossible de lire l’espace de travail : {status}', + saveWorkFailed: "Impossible d'enregistrer l'œuvre : {status}", + changeCover: 'Changer la couverture', + uploadImgHint: "Le téléchargement d'image n'est pas encore pris en charge", + editTags: 'Modifier les étiquettes', + tagPlaceholder: 'Saisir une étiquette et appuyer sur Entrée', + }, footer: { home: 'Accueil', blackHole: 'Trou noir', diff --git a/src/i18n/ja.ts b/src/i18n/ja.ts index 9008e7b..e232bd3 100644 --- a/src/i18n/ja.ts +++ b/src/i18n/ja.ts @@ -113,6 +113,7 @@ export default { copyInternalLink: '内部リンクをコピー', copyExternalLink: '外部リンクをコピー', changeCover: 'カバーを変更', + editWork: '作品を編集', }, date: { justNow: 'たった今', @@ -154,6 +155,33 @@ export default { off: 'オフ', }, }, + mdEditor: { + title: 'Markdown エディター', + refreshWorks: '更新', + workListAria: '作品リスト', + searchPlaceholder: '作品を検索', + emptyWorks: '編集できる作品がありません。', + subjectPlaceholder: 'タイトル', + publish: '公開', + previewTitle: 'プレビュー', + rendering: 'レンダリング中...', + selectWork: '左側から作品を選択してください。', + loginRequiredTitle: 'ログインが必要です', + loginRequiredContent: '作品を編集する前にログインしてください。', + login: 'ログイン', + saveSuccess: '保存しました', + emptyPreview: '内容がありません', + untitled: '無題', + noPermission: 'このアカウントではこの作品を編集できません。', + readSummaryFailed: '作品概要の読み込みに失敗しました: {status}', + fetchWorksFailed: '作品一覧の取得に失敗しました: {status}', + readWorkspaceFailed: 'ワークスペースの読み込みに失敗しました: {status}', + saveWorkFailed: '作品の保存に失敗しました: {status}', + changeCover: 'カバーを変更', + uploadImgHint: '画像アップロードは未対応', + editTags: 'タグを編集', + tagPlaceholder: 'タグを入力してEnter', + }, footer: { home: 'ホーム', blackHole: 'ブラックホール', diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index d79ed74..1d539f7 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -110,6 +110,7 @@ export default { copyInternalLink: '复制内部链接', copyExternalLink: '复制外部链接', changeCover: '更换封面', + editWork: '编辑作品', }, date: { justNow: '刚刚', @@ -150,6 +151,33 @@ export default { off: '关闭', }, }, + mdEditor: { + title: 'Markdown 编辑器', + refreshWorks: '刷新作品', + workListAria: '作品列表', + searchPlaceholder: '搜索作品', + emptyWorks: '没有可编辑的作品。', + subjectPlaceholder: '标题', + publish: '发布', + previewTitle: '预览', + rendering: '渲染中...', + selectWork: '请从左侧选择作品。', + loginRequiredTitle: '需要登录', + loginRequiredContent: '请先登录后再编辑作品。', + login: '去登录', + saveSuccess: '保存成功', + emptyPreview: '暂无内容', + untitled: '未命名', + noPermission: '当前账号没有编辑该作品的权限。', + readSummaryFailed: '读取作品摘要失败:{status}', + fetchWorksFailed: '获取作品列表失败:{status}', + readWorkspaceFailed: '读取工作区失败:{status}', + saveWorkFailed: '保存作品失败:{status}', + changeCover: '更换封面', + uploadImgHint: '暂不支持图片上传', + editTags: '编辑标签', + tagPlaceholder: '输入标签后回车', + }, footer: { home: '首页', blackHole: '黑洞', diff --git a/src/router/index.ts b/src/router/index.ts index abbf402..c083754 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -68,6 +68,12 @@ const routes: RouteRecordRaw[] = [ { path: '/notifications', redirect: '/n' }, { path: '/messages', redirect: '/m' }, + { + path: '/e/:category?/:id?', + name: 'Editor', + component: () => import('../views/Editor.vue'), + meta: { keepAlive: false }, + }, { path: '/ExperimentSummary/:category/:id', redirect: (to) => ({ diff --git a/src/services/api/getData.ts b/src/services/api/getData.ts index ac7927e..09f28d0 100644 --- a/src/services/api/getData.ts +++ b/src/services/api/getData.ts @@ -6,7 +6,7 @@ import { showMessage } from '@popup/naiveui.ts' import { getPath } from '../utils.ts' import { normalizePath } from './types.ts' import { readApiCache, writeApiCache } from './cache.ts' -import { updateNotificationUnread } from '../notificationUnread.ts' +import { updateNotificationUnread } from '@services/notificationUnread.ts' import type { ApiPath, APIParam, APIResult } from './types.ts' import type { Device, Result, ResultOf, Users } from '../../pl-serve-type-main/type/main' diff --git a/src/services/editor/cloudWorks.ts b/src/services/editor/cloudWorks.ts new file mode 100644 index 0000000..6d32e03 --- /dev/null +++ b/src/services/editor/cloudWorks.ts @@ -0,0 +1,222 @@ +import i18n from '@i18n/index' +import { getData } from '@services/api/getData.ts' +import storageManager from '@storage/index.ts' +import type { + Category, + ExperimentQuery, + Result, + Summary, + SummaryTag, + UserInfo, + Workspace, +} from '@services/../pl-serve-type-main/type/main' + +export type EditorWork = { + id: string + contentId: string + category: Category + subject: string + markdown: string + language: string + tags: SummaryTag[] + rawSummary?: Summary +} + +export type publishEditorWorkResult = { + requestBody: Record + response: Result + updatedWork: EditorWork +} + +export type CategoryCursor = { + category: Category + from?: string +} + +export type FetchWorksResult = { + works: EditorWork[] + hasMore: boolean + cursors: CategoryCursor[] +} + +const EDITABLE_VERIFICATIONS = new Set(['Editor', 'Administrator']) + +function t(key: string, params?: Record): string { + return i18n.global.t(key, params || {}) as string +} + +function getCurrentUser(): UserInfo | null { + return storageManager.getObj('userInfo').value as UserInfo | null +} + +export function getCurrentUserId(): string { + return getCurrentUser()?.ID || '' +} + +export function canEditSummary(summary: Summary): boolean { + const currentUser = getCurrentUser() + if (!currentUser?.ID) return false + if (summary.User?.ID === currentUser.ID) return true + if (summary.Coauthors?.some((user) => user.ID === currentUser.ID)) return true + return EDITABLE_VERIFICATIONS.has(String(currentUser.Verification || '')) +} + +function normalizeDescription(description: Summary['Description']): string { + if (Array.isArray(description)) return description.join('\n') + return '' +} + +function toEditorWork(summary: Summary): EditorWork { + return { + id: summary.ID, + contentId: summary.ContentID || summary.ID, + category: summary.Category || 'Discussion', + subject: summary.Subject || t('mdEditor.untitled'), + markdown: normalizeDescription(summary.Description), + language: summary.Language || 'Chinese', + tags: summary.Tags || [], + rawSummary: summary, + } +} + +async function fetchSummary(category: Category, id: string): Promise { + const res = await getData('/Contents/GetSummary', { + ContentID: id, + Category: category, + }) + if (res.Status !== 200 || !res.Data) { + throw new Error(res.Message || t('mdEditor.readSummaryFailed', { status: res.Status })) + } + return res.Data +} + +async function queryCategory( + userId: string, + category: Category, + from: string | undefined, + take: number, +): Promise<{ summaries: Summary[]; lastId: string }> { + const query: ExperimentQuery = { + Category: category, + Languages: [], + ExcludeLanguages: [], + Tags: [], + ExcludeTags: [], + ModelTags: [], + ModelID: undefined, + ParentID: undefined, + UserID: userId, + Special: null, + From: from || null, + Skip: 0, + Take: take, + Days: 0, + Sort: 0, + ShowAnnouncement: false, + } + + const res = await getData('/Contents/QueryExperiments', { Query: query }) + if (res.Status !== 200) { + throw new Error(res.Message || t('mdEditor.fetchWorksFailed', { status: res.Status })) + } + + const summaries: Summary[] = res.Data?.$values || [] + const lastId = summaries.length > 0 ? summaries[summaries.length - 1].ID : from || '' + return { summaries, lastId } +} + +export async function fetchEditableWorks( + cursors: CategoryCursor[], + take = 20, +): Promise { + const userId = getCurrentUserId() + if (!userId) return { works: [], hasMore: false, cursors: [] } + + const cats = cursors.length ? cursors : [{ category: 'Discussion' as Category }] + + const results = await Promise.all( + cats.map((c) => queryCategory(userId, c.category, c.from, take)), + ) + + const allSummaries = results.flatMap((r) => r.summaries) + const works = allSummaries.filter(canEditSummary).map(toEditorWork) + const hasMore = results.some((r) => r.summaries.length >= take) + + const nextCursors: CategoryCursor[] = cats.map((c, i) => ({ + category: c.category, + from: results[i].lastId, + })) + + return { works, hasMore, cursors: nextCursors } +} + +export async function fetchEditableWork(category: Category, id: string): Promise { + const summary = await fetchSummary(category, id) + if (!canEditSummary(summary)) { + throw new Error(t('mdEditor.noPermission')) + } + return toEditorWork(summary) +} + +export async function loadWorkDetail(work: EditorWork): Promise { + const summary = await fetchSummary(work.category, work.id) + return { + ...toEditorWork(summary), + contentId: summary.ContentID || summary.ID, + } +} + +export async function fetchWorkspace(work: EditorWork): Promise { + const res = await getData('/Contents/GetWorkspace', { + ContentID: work.contentId, + Language: work.language || 'Chinese', + }) + if (res.Status !== 200) { + throw new Error(res.Message || t('mdEditor.readWorkspaceFailed', { status: res.Status })) + } + return res.Data || null +} + +export async function publishEditorWork( + work: EditorWork, + markdown: string, + subject: string, +): Promise { + if (!work.rawSummary) { + throw new Error(t('mdEditor.noPermission')) + } + if (!canEditSummary(work.rawSummary)) { + throw new Error(t('mdEditor.noPermission')) + } + + const workspace = await fetchWorkspace(work) + const summary: Summary = { + ...work.rawSummary, + Subject: subject.trim() || work.subject, + Description: markdown.split('\n'), + Language: work.language as Summary['Language'], + } + + const requestBody: Record = { + Summary: summary, + Workspace: workspace ? { ...workspace, Summary: null } : null, + } + + const response = (await getData( + '/Contents/SubmitExperiment', + requestBody as any, + )) as unknown as Result + if (response.Status !== 200) { + throw new Error(response.Message || t('mdEditor.saveWorkFailed', { status: response.Status })) + } + + const updatedWork: EditorWork = { + ...work, + subject: summary.Subject || work.subject, + markdown, + tags: summary.Tags || [], + rawSummary: summary, + } + + return { requestBody, response, updatedWork } +} diff --git a/src/services/errorLogger.ts b/src/services/errorLogger.ts index 53e6b8a..d982045 100644 --- a/src/services/errorLogger.ts +++ b/src/services/errorLogger.ts @@ -67,8 +67,6 @@ class ErrorLogger { this.setupGlobalHandlers(app) } - - private appendToLogBuffer(log: ErrorLog) { this.logTextBuffer += `[${new Date(log.timestamp).toISOString()}] ${log.type.toUpperCase()}\n` this.logTextBuffer += `Message: ${log.message}\n` diff --git a/src/services/pltxt2htm/advancedParser.ts b/src/services/pltxt2htm/advancedParser.ts index 854e7d4..06f1ad6 100644 --- a/src/services/pltxt2htm/advancedParser.ts +++ b/src/services/pltxt2htm/advancedParser.ts @@ -27,27 +27,6 @@ function ensureMermaidInitialized() { mermaidInitialized = true } -function getMermaidSvgWidth(svg: SVGSVGElement) { - const maxWidth = svg.style.maxWidth.match(/^([0-9.]+)px$/)?.[1] - if (maxWidth) return Number(maxWidth) - - const width = svg.getAttribute('width')?.match(/^([0-9.]+)(?:px)?$/)?.[1] - if (width) return Number(width) - - const viewBox = svg.getAttribute('viewBox')?.trim().split(/\s+/) - return viewBox?.[2] ? Number(viewBox[2]) : undefined -} - -function keepMermaidSvgIntrinsicWidth(wrapper: HTMLElement) { - const svg = wrapper.querySelector('svg') - if (!(svg instanceof SVGSVGElement)) return - - const width = getMermaidSvgWidth(svg) - if (width && Number.isFinite(width)) { - svg.style.setProperty('width', `${width}px`) - } - svg.style.setProperty('max-width', 'none') -} async function renderMermaidDiagrams(container: HTMLElement) { ensureMermaidInitialized() @@ -55,22 +34,21 @@ async function renderMermaidDiagrams(container: HTMLElement) { await Promise.all( mermaidBlocks.map(async (block, index) => { - const source = block.textContent?.trim() - if (!source) return + const source = block.textContent?.trim().replace(/\u00a0/g, " "); + if (!source) return; const pre = block.closest('pre') if (!pre) return try { - const renderId = `mermaid-${Date.now()}-${index}` - const { svg } = await mermaid.render(renderId, source) - const wrapper = document.createElement('div') - wrapper.className = 'mermaid-diagram' - wrapper.innerHTML = svg - keepMermaidSvgIntrinsicWidth(wrapper) - pre.replaceWith(wrapper) - } catch { - console.warn('Mermaid render failed') + const renderId = `mermaid-${Date.now()}-${index}`; + const { svg } = await mermaid.render(renderId, source); + const wrapper = document.createElement("div"); + wrapper.className = "mermaid-diagram"; + wrapper.innerHTML = svg; + pre.replaceWith(wrapper); + } catch (e) { + console.warn("mermaid render failed:", e); } }), ) @@ -87,14 +65,11 @@ async function advancedParser( const wasmInstance = await getWasmInstance() const instanceAny: any = wasmInstance if (!instanceAny.__advanced_parser_fn) { - instanceAny.__advanced_parser_fn = wasmInstance.cwrap('fixedadv_parser', 'number', [ - 'string', - 'string', - 'string', - 'string', - 'string', - 'string', - ]) + instanceAny.__advanced_parser_fn = wasmInstance.cwrap( + "fixedadv_parser", + "number", + ["string", "string", "string", "string", "string", "string"], + ); } const deallocate = await getDeallocator() @@ -145,9 +120,11 @@ async function parse(source: string, context: ParseContext = {}) { await renderMermaidDiagrams(tempDiv) } - tempDiv.querySelectorAll('pre code:not(.language-mermaid)').forEach((block) => { - hljs.highlightElement(block as HTMLElement) - }) + tempDiv + .querySelectorAll("pre code:not(.language-mermaid)") + .forEach((block) => { + hljs.highlightElement(block as HTMLElement); + }); return tempDiv.innerHTML } diff --git a/src/services/pltxt2htm/deallocator.ts b/src/services/pltxt2htm/deallocator.ts index 740ad2a..276a51c 100644 --- a/src/services/pltxt2htm/deallocator.ts +++ b/src/services/pltxt2htm/deallocator.ts @@ -4,7 +4,7 @@ export async function getDeallocator(): Promise<(ptr: number) => void> { const wasmInstance = await getWasmInstance() const instanceAny: any = wasmInstance if (!instanceAny.__deallocate_fn) { - instanceAny.__deallocate_fn = wasmInstance.cwrap('free', null, ['number']) + instanceAny.__deallocate_fn = wasmInstance.cwrap("free", null, ["number"]); } return instanceAny.__deallocate_fn as (ptr: number) => void } diff --git a/src/services/utils.ts b/src/services/utils.ts index 98e39c2..7451d3e 100644 --- a/src/services/utils.ts +++ b/src/services/utils.ts @@ -31,30 +31,35 @@ const baseUrl = import.meta.env.VITE_BASE_URL let coverImgSuffix = getCoverImgSuffix() let avatarImgSuffix = getAvatarImgSuffix() -function getCoverImgSuffix () { - const connection = (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection +function getCoverImgSuffix() { + const connection = + (navigator as any).connection || + (navigator as any).mozConnection || + (navigator as any).webkitConnection if (connection) { - if(connection.saveData){ + if (connection.saveData) { return '!block' } - if(['2g','3g'].includes(connection.effectiveType)){ + if (['2g', '3g'].includes(connection.effectiveType)) { return '!block' } } return '' - } -function getAvatarImgSuffix () { - const connection = (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection +function getAvatarImgSuffix() { + const connection = + (navigator as any).connection || + (navigator as any).mozConnection || + (navigator as any).webkitConnection if (connection) { - if(connection.saveData){ + if (connection.saveData) { return '!tiny.round' } - if(connection.effectiveType === '2g'){ + if (connection.effectiveType === '2g') { return '!tiny.round' } - if(connection.effectiveType === '3g'){ + if (connection.effectiveType === '3g') { return '!small.round' } } @@ -62,8 +67,11 @@ function getAvatarImgSuffix () { } export function registerNetworkListener() { - const connection = (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection - if(connection){ + const connection = + (navigator as any).connection || + (navigator as any).mozConnection || + (navigator as any).webkitConnection + if (connection) { connection.addEventListener('change', () => { coverImgSuffix = getCoverImgSuffix() avatarImgSuffix = getAvatarImgSuffix() diff --git a/src/views/Editor.vue b/src/views/Editor.vue new file mode 100644 index 0000000..bf452c3 --- /dev/null +++ b/src/views/Editor.vue @@ -0,0 +1,930 @@ + + + + + diff --git a/src/views/ExperimentSummary.vue b/src/views/ExperimentSummary.vue index 17f1100..b3d75cc 100644 --- a/src/views/ExperimentSummary.vue +++ b/src/views/ExperimentSummary.vue @@ -159,8 +159,9 @@