diff --git a/CHANGELOG.md b/CHANGELOG.md index 13fa8ed5..5eb1c258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,15 @@ Changelog * No changes. +Version 0.1.58 -- 2026-Jun-22 +----------------------------- + +* Load Client components at first use, rather than pre-loading all possible + components. +* Correct math translation from the Client back to the IDE. +* Fix Client bug which caused it to immediately remove newly added empty + paragraphs. + Version 0.1.57 -- 2026-Jun-15 ----------------------------- diff --git a/builder/Cargo.lock b/builder/Cargo.lock index c766c47f..90ad8626 100644 --- a/builder/Cargo.lock +++ b/builder/Cargo.lock @@ -174,6 +174,38 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a74858bcfe44b22016cb49337d7b6f04618c58e5dbfdef61b06b8c434324a0bc" +[[package]] +name = "defmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + [[package]] name = "dunce" version = "1.0.5" @@ -228,10 +260,11 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "jiff" -version = "0.2.28" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" +checksum = "34f877a98676d2fb664698d74cc6a51ce6c484ce8c770f05d0108ec9090aeb46" dependencies = [ + "defmt", "jiff-static", "log", "portable-atomic", @@ -241,9 +274,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.28" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" +checksum = "0666b5ab5ecaca213fc2a85b8c0083d9004e84ee2d5f9a7e0017aaf50986f25f" dependencies = [ "proc-macro2", "quote", @@ -258,9 +291,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "log" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "memchr" @@ -360,9 +393,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -430,9 +463,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", diff --git a/client/package.json5 b/client/package.json5 index 3360e6f9..aa960b08 100644 --- a/client/package.json5 +++ b/client/package.json5 @@ -43,7 +43,7 @@ url: 'https://github.com/bjones1/CodeChat_editor', }, type: 'module', - version: '0.1.57', + version: '0.1.58', dependencies: { '@codemirror/commands': '^6.10.3', '@codemirror/lang-cpp': '^6.0.3', @@ -63,8 +63,8 @@ '@codemirror/language': '^6.12.3', '@codemirror/legacy-modes': '^6.5.3', '@codemirror/state': '^6.6.0', - '@codemirror/view': '6.38.8', - '@hpcc-js/wasm-graphviz': '^1.22.0', + '@codemirror/view': '~6.38.8', + '@hpcc-js/wasm-graphviz': '^1.22.2', '@mathjax/mathjax-newcm-font': '^4.1.2', codemirror: '^6.0.2', mathjax: '^4.1.2', @@ -81,8 +81,8 @@ '@types/mocha': '^10.0.10', '@types/node': '^24.13.2', '@types/toastify-js': '^1.12.4', - '@typescript-eslint/eslint-plugin': '^8.61.0', - '@typescript-eslint/parser': '^8.61.0', + '@typescript-eslint/eslint-plugin': '^8.61.1', + '@typescript-eslint/parser': '^8.61.1', chai: '^6.2.2', esbuild: '^0.28.1', eslint: '^10.5.0', @@ -91,10 +91,10 @@ 'eslint-plugin-prettier': '^5.5.6', globals: '^17.6.0', mocha: '^11.7.6', - 'npm-check-updates': '^22.2.3', + 'npm-check-updates': '^22.2.7', prettier: '^3.8.4', typescript: '^6.0.3', - 'typescript-eslint': '^8.61.0', + 'typescript-eslint': '^8.61.1', }, scripts: { test: 'echo "Error: no test specified" && exit 1', diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index 5b21af1b..9b17276c 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -63,11 +63,11 @@ importers: specifier: ^6.6.0 version: 6.6.0 '@codemirror/view': - specifier: 6.38.8 + specifier: ~6.38.8 version: 6.38.8 '@hpcc-js/wasm-graphviz': - specifier: ^1.22.0 - version: 1.22.0 + specifier: ^1.22.2 + version: 1.22.2 '@mathjax/mathjax-newcm-font': specifier: ^4.1.2 version: 4.1.2 @@ -112,11 +112,11 @@ importers: specifier: ^1.12.4 version: 1.12.4 '@typescript-eslint/eslint-plugin': - specifier: ^8.61.0 - version: 8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3) + specifier: ^8.61.1 + version: 8.61.1(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3) '@typescript-eslint/parser': - specifier: ^8.61.0 - version: 8.61.0(eslint@10.5.0)(typescript@6.0.3) + specifier: ^8.61.1 + version: 8.61.1(eslint@10.5.0)(typescript@6.0.3) chai: specifier: ^6.2.2 version: 6.2.2 @@ -131,7 +131,7 @@ importers: version: 10.1.8(eslint@10.5.0) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0) + version: 2.32.0(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0) eslint-plugin-prettier: specifier: ^5.5.6 version: 5.5.6(eslint-config-prettier@10.1.8(eslint@10.5.0))(eslint@10.5.0)(prettier@3.8.4) @@ -142,8 +142,8 @@ importers: specifier: ^11.7.6 version: 11.7.6 npm-check-updates: - specifier: ^22.2.3 - version: 22.2.3 + specifier: ^22.2.7 + version: 22.2.7 prettier: specifier: ^3.8.4 version: 3.8.4 @@ -151,8 +151,8 @@ importers: specifier: ^6.0.3 version: 6.0.3 typescript-eslint: - specifier: ^8.61.0 - version: 8.61.0(eslint@10.5.0)(typescript@6.0.3) + specifier: ^8.61.1 + version: 8.61.1(eslint@10.5.0)(typescript@6.0.3) packages: @@ -222,8 +222,8 @@ packages: '@codemirror/lint@6.9.7': resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} - '@codemirror/search@6.7.0': - resolution: {integrity: sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==} + '@codemirror/search@6.7.1': + resolution: {integrity: sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==} '@codemirror/state@6.6.0': resolution: {integrity: sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==} @@ -429,8 +429,8 @@ packages: resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@hpcc-js/wasm-graphviz@1.22.0': - resolution: {integrity: sha512-/l6m+ev4TzYwiSm2pklMALiKejuheJwFkOHKrTAvFei+US+MVv6Vgk06xMc4Mjn57WiqzheMVGnmNu1iqkcBdA==} + '@hpcc-js/wasm-graphviz@1.22.2': + resolution: {integrity: sha512-qofkC1bxiQKljs95A/7a0j3mvjEdTBiDPq2W6Eh3mJGOLJ+CEtLVe5pFtzf+FZhYW/V9p9hssS1TRl9PxoV8Sw==} '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} @@ -737,63 +737,63 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@typescript-eslint/eslint-plugin@8.61.0': - resolution: {integrity: sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==} + '@typescript-eslint/eslint-plugin@8.61.1': + resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.61.0 + '@typescript-eslint/parser': ^8.61.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.61.0': - resolution: {integrity: sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==} + '@typescript-eslint/parser@8.61.1': + resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.61.0': - resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==} + '@typescript-eslint/project-service@8.61.1': + resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.61.0': - resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==} + '@typescript-eslint/scope-manager@8.61.1': + resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.61.0': - resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==} + '@typescript-eslint/tsconfig-utils@8.61.1': + resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.61.0': - resolution: {integrity: sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==} + '@typescript-eslint/type-utils@8.61.1': + resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.61.0': - resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==} + '@typescript-eslint/types@8.61.1': + resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.61.0': - resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==} + '@typescript-eslint/typescript-estree@8.61.1': + resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.61.0': - resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==} + '@typescript-eslint/utils@8.61.1': + resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.61.0': - resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==} + '@typescript-eslint/visitor-keys@8.61.1': + resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@upsetjs/venn.js@2.0.0': @@ -1167,8 +1167,8 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} - dompurify@3.4.10: - resolution: {integrity: sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w==} + dompurify@3.4.11: + resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} @@ -1183,6 +1183,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + es-abstract@1.24.2: resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} @@ -1207,12 +1211,12 @@ packages: resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + es-to-primitive@1.3.1: + resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==} engines: {node: '>= 0.4'} - es-toolkit@1.47.1: - resolution: {integrity: sha512-5RAqEwf4P4E17p+W75KLOWw/nOvKZzSQpxM32IpI2KZLaVonjTrZ0Ai5ghMaVI9eKC2p8eoQgcBdkEDgzFk6+Q==} + es-toolkit@1.48.1: + resolution: {integrity: sha512-wfnXlwd5I75eXRtdD2vuEs50xHHESECDsGD7yiQnfFVNoa5522NwXEbmgo98LfiukSQHs+mBM7/YG3qKJB9/mQ==} esbuild@0.28.1: resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} @@ -1710,8 +1714,8 @@ packages: resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} engines: {node: '>= 0.4'} - npm-check-updates@22.2.3: - resolution: {integrity: sha512-n8HZ0ywZugYRgEqIm67hbZC5L/9S/gLX+MkGchJB43pP2xib1+u76OpVEJRZ4QP2BksPRi5M1CCStJVfc16+/A==} + npm-check-updates@22.2.7: + resolution: {integrity: sha512-Pzs/lTswEe23hT8JUXABl4vgrr0WRKOhw55wAldwngUflk8CwP+grXJt00yGjYiZf2wD1HIfdPa3xaOi1RCyZQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: '>=10.0.0'} hasBin: true @@ -1876,8 +1880,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -2033,8 +2037,8 @@ packages: resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} - typescript-eslint@8.61.0: - resolution: {integrity: sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==} + typescript-eslint@8.61.1: + resolution: {integrity: sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2055,8 +2059,8 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - uuid@14.0.0: - resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true w3c-keyname@2.2.8: @@ -2110,8 +2114,8 @@ packages: resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} engines: {node: '>=10'} - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} engines: {node: '>=12'} yocto-queue@0.1.0: @@ -2274,7 +2278,7 @@ snapshots: '@codemirror/view': 6.43.1 crelt: 1.0.6 - '@codemirror/search@6.7.0': + '@codemirror/search@6.7.1': dependencies: '@codemirror/state': 6.6.0 '@codemirror/view': 6.38.8 @@ -2410,7 +2414,7 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 - '@hpcc-js/wasm-graphviz@1.22.0': {} + '@hpcc-js/wasm-graphviz@1.22.2': {} '@humanfs/core@0.19.2': dependencies: @@ -2744,14 +2748,14 @@ snapshots: '@types/trusted-types@2.0.7': optional: true - '@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/type-utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/type-utils': 8.61.1(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.61.1 eslint: 10.5.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -2760,41 +2764,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3)': + '@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.61.1 debug: 4.4.3(supports-color@8.1.1) eslint: 10.5.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.0(typescript@6.0.3)': + '@typescript-eslint/project-service@8.61.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3) - '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) + '@typescript-eslint/types': 8.61.1 debug: 4.4.3(supports-color@8.1.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.61.0': + '@typescript-eslint/scope-manager@8.61.1': dependencies: - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 - '@typescript-eslint/tsconfig-utils@8.61.0(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.61.1(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.61.0(eslint@10.5.0)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.61.1(eslint@10.5.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0)(typescript@6.0.3) debug: 4.4.3(supports-color@8.1.1) eslint: 10.5.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -2802,37 +2806,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.61.0': {} + '@typescript-eslint/types@8.61.1': {} - '@typescript-eslint/typescript-estree@8.61.0(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.61.0(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3) - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/project-service': 8.61.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 debug: 4.4.3(supports-color@8.1.1) minimatch: 10.2.5 - semver: 7.8.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.0(eslint@10.5.0)(typescript@6.0.3)': + '@typescript-eslint/utils@8.61.1(eslint@10.5.0)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) eslint: 10.5.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.61.0': + '@typescript-eslint/visitor-keys@8.61.1': dependencies: - '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/types': 8.61.1 eslint-visitor-keys: 5.0.1 '@upsetjs/venn.js@2.0.0': @@ -2984,7 +2988,7 @@ snapshots: '@codemirror/commands': 6.10.3 '@codemirror/language': 6.12.3 '@codemirror/lint': 6.9.7 - '@codemirror/search': 6.7.0 + '@codemirror/search': 6.7.1 '@codemirror/state': 6.6.0 '@codemirror/view': 6.38.8 @@ -3256,7 +3260,7 @@ snapshots: dependencies: esutils: 2.0.3 - dompurify@3.4.10: + dompurify@3.4.11: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -3272,6 +3276,13 @@ snapshots: emoji-regex@9.2.2: {} + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + es-abstract@1.24.2: dependencies: array-buffer-byte-length: 1.0.2 @@ -3286,7 +3297,7 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 + es-to-primitive: 1.3.1 function.prototype.name: 1.2.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 @@ -3348,13 +3359,15 @@ snapshots: dependencies: hasown: 2.0.4 - es-to-primitive@1.3.0: + es-to-primitive@1.3.1: dependencies: + es-abstract-get: 1.0.0 + es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 - es-toolkit@1.47.1: {} + es-toolkit@1.48.1: {} esbuild@0.28.1: optionalDependencies: @@ -3401,17 +3414,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0): + eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0)(typescript@6.0.3) eslint: 10.5.0 eslint-import-resolver-node: 0.3.10 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -3422,7 +3435,7 @@ snapshots: doctrine: 2.1.0 eslint: 10.5.0 eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0) + eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0) hasown: 2.0.4 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -3434,7 +3447,7 @@ snapshots: string.prototype.trimend: 1.0.10 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0)(typescript@6.0.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -3862,15 +3875,15 @@ snapshots: d3-sankey: 0.12.3 dagre-d3-es: 7.0.14 dayjs: 1.11.21 - dompurify: 3.4.10 - es-toolkit: 1.47.1 + dompurify: 3.4.11 + es-toolkit: 1.48.1 katex: 0.16.47 khroma: 2.1.0 marked: 16.4.2 roughjs: 4.6.6 stylis: 4.4.0 ts-dedent: 2.3.0 - uuid: 14.0.0 + uuid: 14.0.1 minimatch@10.2.5: dependencies: @@ -3908,7 +3921,7 @@ snapshots: strip-json-comments: 3.1.1 supports-color: 8.1.1 workerpool: 9.3.4 - yargs: 17.7.2 + yargs: 17.7.3 yargs-parser: 21.1.1 yargs-unparser: 2.0.0 @@ -3923,7 +3936,7 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 - npm-check-updates@22.2.3: {} + npm-check-updates@22.2.7: {} object-inspect@1.13.4: {} @@ -4105,7 +4118,7 @@ snapshots: semver@6.3.1: {} - semver@7.8.4: {} + semver@7.8.5: {} serialize-javascript@6.0.2: dependencies: @@ -4301,12 +4314,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.61.0(eslint@10.5.0)(typescript@6.0.3): + typescript-eslint@8.61.1(eslint@10.5.0)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0)(typescript@6.0.3) eslint: 10.5.0 typescript: 6.0.3 transitivePeerDependencies: @@ -4327,7 +4340,7 @@ snapshots: dependencies: punycode: 2.3.1 - uuid@14.0.0: {} + uuid@14.0.1: {} w3c-keyname@2.2.8: {} @@ -4403,7 +4416,7 @@ snapshots: flat: 5.0.2 is-plain-obj: 2.1.0 - yargs@17.7.2: + yargs@17.7.3: dependencies: cliui: 8.0.1 escalade: 3.2.0 diff --git a/client/src/CodeChatEditor.mts b/client/src/CodeChatEditor.mts index 3aba7d8d..88b03151 100644 --- a/client/src/CodeChatEditor.mts +++ b/client/src/CodeChatEditor.mts @@ -62,8 +62,13 @@ import "./graphviz-webcomponent-setup.mjs"; // This must be imported *after* the previous setup import, so it's placed here, // instead of in the third-party category above. import "./third-party/graphviz-webcomponent/graph.js"; -import { init, tinymce } from "./tinymce-config.mjs"; -import { Editor, EditorEvent, Events } from "tinymce"; +import type { + Editor, + EditorEvent, + Events, + RawEditorOptions, + TinyMCE, +} from "tinymce"; import { CodeChatForWeb, CodeMirrorDiffable, @@ -137,8 +142,9 @@ let current_metadata: { const webSocketComm = () => parent.window.CodeChatEditorFramework.webSocketComm; -// This set when a TinyMCE `input` event occurs, which usually produces a duplicate `Dirty` event which should be ignored. -let ignoreDirty = false; +// This set when a TinyMCE `input` event occurs, which usually produces a +// duplicate `Dirty` event which should be ignored. +let ignoreTinyMceDirty = false; // True if the document is dirty (needs saving). let is_dirty = false; @@ -149,6 +155,20 @@ export const set_is_dirty = (value: boolean = true) => { export const get_is_dirty = () => is_dirty; +// ### TinyMCE dynamic import +// +// TinyMCE is dynamically imported when `init` is called. +export const init = async (options: RawEditorOptions) => { + const tinymce_config = await import("./tinymce-config.mjs"); + tinymce = tinymce_config.tinymce; + return await tinymce_config.init(options); +}; +// The imported module is stored in this variable. +export let tinymce: undefined | TinyMCE = undefined; +// A single TinyMCE instance is used for all doc blocks. Avoid accessing this +// through `tinymce.activeEditor`, which fails if the editor isn't active. +export const tinymce_instance = () => tinymce?.get(0); + // Page initialization // ------------------- @@ -205,7 +225,7 @@ let doc_content = ""; // the appropriate message. const do_debug = () => { if (DEBUG_ENABLED) { - tinymce.activeEditor?.save({ format: "raw" }); + tinymce_instance()?.save({ format: "raw" }); } }; @@ -222,17 +242,9 @@ const _open_lp = async ( scroll_line?: number, ) => { // Note that globals, such as `is_dirty` and document contents, may change - // between `await` calls. Therefore, try to perform processing which relies - // on these values between `await` calls. For example, evaluate this first: - // - // Before calling any MathJax, make sure it's fully loaded and the initial - // render is finished. - await window.MathJax.startup.promise; - - // The only call to `await` is based on TinyMCE init, which should only - // cause an async delay on its first execution. (Even then, I'm not sure it - // does, since all resources are statically imported). So, we should be OK - // for the rest of this function. + // between `await` calls. The only call to `await` is based on TinyMCE init, + // which should only cause an async delay on its first execution. So, we + // should be OK for the rest of this function. // // Now, make all decisions about `is_dirty`: if the text is dirty, do some // special processing; simply applying the update could cause either data @@ -251,7 +263,7 @@ const _open_lp = async ( // 2. In normal mode, we don't have a backup copy of the full text. // Report an `OutOfSync` error, which causes the IDE to send the full // text which will then overwrite changes made in the Client. - if (is_dirty && is_re_translation) { + if (get_is_dirty() && is_re_translation) { console_log(`Ignoring re-translation because Client is dirty.`); return; } @@ -264,10 +276,12 @@ const _open_lp = async ( // Get the mode from the page's query parameters. Default to edit using // the // [nullish coalescing operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator). - const mode = urlParams.get("mode") ?? EditorMode.edit; - const _editorMode = Object.values(EditorMode).includes(mode) - ? mode - : EditorMode.edit; + const mode = urlParams.get("mode") ?? EditorMode[EditorMode.edit]; + // `EditorMode` is a numeric enum, so indexing it by the mode name + // yields the matching enum value, or `undefined` for an unknown name. + // Fall back to `edit` in that case. + const _editorMode: EditorMode = + EditorMode[mode as keyof typeof EditorMode] ?? EditorMode.edit; // Get the [current_metadata](#current_metadata) from the // provided `code_chat_for_web` struct and store it as a global @@ -284,8 +298,10 @@ const _open_lp = async ( // mathematics, you will need to tell MathJax about that so that it // knows the typeset math that you are removing is no longer on the // page." - window.MathJax.typesetClear(codechat_body); - if (tinymce.activeEditor === null) { + window.MathJax?.typesetClear?.(codechat_body); + // Note that `==` is intentional: `null` (no editor instance) or + // `undefined` (TinyMCE not loaded). + if (tinymce_instance() == null) { // We shouldn't have a diff if the editor hasn't been // initialized. assert("Plain" in source); @@ -305,15 +321,16 @@ const _open_lp = async ( // this is how to create a TinyMCE event handler. setup: (editor: Editor) => { editor.on("Dirty", () => { - if (!ignoreDirty) { - is_dirty = true; + if (!ignoreTinyMceDirty) { + set_is_dirty(true); + startAutoUpdateTimer(); } - startAutoUpdateTimer(); }); editor.on("input", () => { - ignoreDirty = true; - is_dirty = true; + ignoreTinyMceDirty = true; + set_is_dirty(true); + startAutoUpdateTimer(); }); // Send updates on cursor movement. @@ -329,18 +346,18 @@ const _open_lp = async ( ); }, }); - tinymce.activeEditor!.focus(); + tinymce_instance()!.focus(); } else { // Save the cursor location before the update, then restore it // afterwards, if TinyMCE has focus. - const sel = tinymce.activeEditor!.hasFocus() + const sel = tinymce_instance()!.hasFocus() ? saveSelection() : undefined; doc_content = "Plain" in source ? source.Plain.doc : apply_diff_str(doc_content, source.Diff.doc); - tinymce.activeEditor!.setContent(doc_content); + tinymce_instance()!.setContent(doc_content); if (sel !== undefined) { restoreSelection(sel); } @@ -348,7 +365,7 @@ const _open_lp = async ( await mathJaxTypeset(codechat_body); scroll_to_line(cursor_position, scroll_line); } else { - if (is_dirty && "Diff" in source) { + if (get_is_dirty() && "Diff" in source) { // Send an `OutOfSync` response, so that the IDE will send the // full text to overwrite these changes with. webSocketComm().send_result( @@ -376,7 +393,7 @@ const _open_lp = async ( // contents have been overwritten by contents from the IDE. By the same // reasoning, restart the auto update timer. clearAutoUpdateTimer(); - is_dirty = false; + set_is_dirty(false); // If tests should be run, then the // [following global variable](CodeChatEditor-test.mts#CodeChatEditor_test) @@ -432,9 +449,10 @@ const save_lp = async ( // To save a document only, simply get the HTML from the only // Tiny MCE div. Update the `doc_contents` to stay in sync with // the Server. - doc_content = tinymce.activeEditor!.save({ format: "raw" }); - // The `save()` flushes any duplicate `Dirty` events. After this, following `Dirty` events are genuine. - ignoreDirty = false; + doc_content = tinymce_instance()!.save({ format: "raw" }); + // The `save()` flushes any duplicate `Dirty` events. After + // this, following `Dirty` events are genuine. + ignoreTinyMceDirty = false; ( code_mirror_diffable as { Plain: CodeMirror; @@ -520,10 +538,12 @@ export const restoreSelection = ({ // Copy the selection over to TinyMCE by indexing the selection path to find // the selected node. if (selection_path.length && typeof selection_offset === "number") { - let selection_node = tinymce.activeEditor!.getContentAreaContainer(); - while (selection_path.length) { + let selection_node = tinymce_instance()!.getContentAreaContainer(); + // Avoid mutating `selection_path` by making a copy of it. + const selection_path_copy = [...selection_path]; + while (selection_path_copy.length) { const new_selection_node = selection_node.childNodes[ - selection_path.shift()! + selection_path_copy.shift()! ] as HTMLElement; // If we get lost during the descent, then stop just before that. if (new_selection_node === undefined) { @@ -539,7 +559,7 @@ export const restoreSelection = ({ selection_node.nodeValue?.length ?? 0, ); // Use that to set the selection. - tinymce.activeEditor!.selection.setCursorLocation( + tinymce_instance()!.selection.setCursorLocation( selection_node, final_selection_offset, ); @@ -666,12 +686,19 @@ const on_click = (event: MouseEvent) => { // Save the current document, then navigate to the provided URL, which must be a // reference to another CodeChat Editor document. const save_then_navigate = (codeChatEditorUrl: URL) => { - send_update(true).then((_value) => { + const navigate = () => { // Avoid recursion! window.navigation.removeEventListener("navigate", on_navigate); parent.window.CodeChatEditorFramework.webSocketComm.current_file( codeChatEditorUrl, ); + }; + // Navigate after the save completes. If the save fails, still navigate -- + // otherwise the user is stranded on the current page with only a generic + // error toast -- but report the failure so the lost save isn't silent. + send_update(true).then(navigate, (reason) => { + show_toast(`Error saving before navigation: ${reason}`); + navigate(); }); }; @@ -694,11 +721,24 @@ export const on_error = (event: Event) => { if (event instanceof ErrorEvent) { err_str = `${event.filename}:${event.lineno}: ${event.message}`; } else if (event instanceof PromiseRejectionEvent) { - err_str = `${event.promise} rejected: ${event.reason}`; + const reason = event.reason; + let userMessage = "An unexpected error occurred. Please try again."; + console.log(reason, reason instanceof Error, typeof reason); + // A simple `reason instanceof Error` fails here. Better would be + // [Error.isError()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/isError), + // but this requires es2027. + if (typeof reason.message === "string") { + // Extracts the text from `reject(new Error('Your text'))`. + userMessage = reason.message; + } else if (typeof reason === "string") { + // Extracts the text from `reject('Your text')`. + userMessage = reason; + } + err_str = `Promise rejected: ${userMessage}`; } else { err_str = `Unexpected error ${typeof event}: ${event}`; } - show_toast(`Error: ${err_str}`); + show_toast(err_str); console.error(event); }; diff --git a/client/src/CodeChatEditorFramework.mts b/client/src/CodeChatEditorFramework.mts index 1c7f4c56..443bd2b0 100644 --- a/client/src/CodeChatEditorFramework.mts +++ b/client/src/CodeChatEditorFramework.mts @@ -326,8 +326,7 @@ class WebSocketComm { }; } - /*eslint-disable-next-line @typescript-eslint/no-explicit-any */ - send = (data: any) => this.ws.send(data); + send = (data: string | BufferSource | Blob) => this.ws.send(data); /*eslint-disable-next-line @typescript-eslint/no-explicit-any */ close = (...args: any) => this.ws.close(...args); diff --git a/client/src/CodeMirror-integration.mts b/client/src/CodeMirror-integration.mts index b1cdc3d8..51e7eb54 100644 --- a/client/src/CodeMirror-integration.mts +++ b/client/src/CodeMirror-integration.mts @@ -71,25 +71,9 @@ import { Annotation, TransactionSpec, } from "@codemirror/state"; -import { cpp } from "@codemirror/lang-cpp"; -import { css } from "@codemirror/lang-css"; -import { go } from "@codemirror/lang-go"; -import { html } from "@codemirror/lang-html"; -import { java } from "@codemirror/lang-java"; -import { javascript } from "@codemirror/lang-javascript"; -import { json } from "@codemirror/lang-json"; -import { python } from "@codemirror/lang-python"; -import { rust } from "@codemirror/lang-rust"; -import { sql } from "@codemirror/lang-sql"; -import { yaml } from "@codemirror/lang-yaml"; -import { StreamLanguage } from "@codemirror/language"; -import { shell } from "@codemirror/legacy-modes/mode/shell"; -import { swift } from "@codemirror/legacy-modes/mode/swift"; -import { toml } from "@codemirror/legacy-modes/mode/toml"; -import { verilog } from "@codemirror/legacy-modes/mode/verilog"; -import { vhdl } from "@codemirror/legacy-modes/mode/vhdl"; -import { tinymce, init } from "./tinymce-config.mjs"; -import { Editor, EditorEvent, Events } from "tinymce"; + +import type { StreamParser } from "@codemirror/language"; +import type { Editor, EditorEvent, Events } from "tinymce"; // ### Local import { @@ -97,6 +81,9 @@ import { startAutoUpdateTimer, saveSelection, restoreSelection, + tinymce_instance, + tinymce, + init, } from "./CodeChatEditor.mjs"; import { CodeChatForWeb, @@ -117,7 +104,7 @@ let current_view: EditorView; let on_dirty_scheduled = false; // This set when an `input` event occurs, which usually produces a duplicate // `Dirty` event which should be ignored. -let ignoreDirty = false; +let ignoreTinyMceDirty = false; // Options used when creating a `Decoration`. const decorationOptions = { @@ -127,7 +114,8 @@ const decorationOptions = { declare global { interface Window { - // Tye `#types/MathJax` definitions are out of date. + // The `@types/MathJax` definitions are out of date and I can't figure + // out how to import the v4 Typescript definitions. /*eslint-disable-next-line @typescript-eslint/no-explicit-any */ MathJax: any; } @@ -196,9 +184,7 @@ export const docBlockField = StateField.define({ .slice(effect.value.from, effect.value.to) .toString(); if (newlines !== "\n".repeat(newlines.length)) { - report_error(`Attempt to overwrite text: "${newlines}".`); - window.close(); - assert(false); + halt_on_error(`Attempt to overwrite text: "${newlines}".`); } // Perform an // [update](https://codemirror.net/docs/ref/#state.RangeSet.update) @@ -249,11 +235,9 @@ export const docBlockField = StateField.define({ // doc block. if (prev !== undefined) { console.error({ doc_blocks, effect }); - report_error( + halt_on_error( "More than one doc block at one location found.", ); - window.close(); - assert(false); } prev = value; to = to_found; @@ -267,8 +251,7 @@ export const docBlockField = StateField.define({ ); if (prev === undefined) { console.error({ doc_blocks, effect }); - report_error("No doc block found."); - window.close(); + halt_on_error("No doc block found."); assert(false); } // Determine the final from/to values. @@ -277,10 +260,9 @@ export const docBlockField = StateField.define({ // Check that we're not overwriting text. const newlines = tr.newDoc.slice(from, to).toString(); if (newlines !== "\n".repeat(newlines.length)) { - report_error(`Attempt to overwrite text: "${newlines}".`); - window.close(); - assert(false); + halt_on_error(`Attempt to overwrite text: "${newlines}".`); } + const prev_widget = prev.spec.widget as DocBlockWidget; doc_blocks = doc_blocks.update({ // Remove the old doc block. We assume there's only one // block in the provided from/to range. @@ -292,13 +274,12 @@ export const docBlockField = StateField.define({ add: [ Decoration.replace({ widget: new DocBlockWidget( - effect.value.indent ?? prev.spec.widget.indent, - effect.value.delimiter ?? - prev.spec.widget.delimiter, + effect.value.indent ?? prev_widget.indent, + effect.value.delimiter ?? prev_widget.delimiter, typeof effect.value.contents === "string" ? effect.value.contents : apply_diff_str( - prev.spec.widget.contents, + prev_widget.contents, effect.value.contents, ), // If autosave is allowed (meaning no autosave @@ -337,7 +318,7 @@ export const docBlockField = StateField.define({ toJSON: (value: DecorationSet, _state: EditorState) => { const json_result = []; for (const iter = value.iter(); iter.value !== null; iter.next()) { - const w = iter.value.spec.widget; + const w = iter.value.spec.widget as DocBlockWidget; json_result.push([ iter.from, iter.to, @@ -407,9 +388,6 @@ type updateDocBlockType = { indent?: string; delimiter?: string; contents: string | StringDiff[]; - // True if this update comes from a user change, as opposed to an update - // received from the IDE. - is_user_change?: boolean; }; // Define an update. @@ -464,7 +442,8 @@ class DocBlockWidget extends WidgetType { return ( other.indent === this.indent && other.delimiter === this.delimiter && - other.contents === this.contents + other.contents === this.contents && + other.is_user_change == this.is_user_change ); } @@ -497,7 +476,7 @@ class DocBlockWidget extends WidgetType { // different, non-eq content) to reflect this widget." updateDOM(dom: HTMLElement, _view: EditorView): boolean { // If this change was produced by a user edit, then the DOM was already - // updated. Stop here. + // updated, both for the indent and contents. Stop here. if (this.is_user_change) { return true; } @@ -508,14 +487,14 @@ class DocBlockWidget extends WidgetType { // The contents div could be a TinyMCE instance, or just a plain div. // Handle both cases. const [contents_div, is_tinymce] = get_contents(dom); - window.MathJax.typesetClear([contents_div]); + window.MathJax?.typesetClear?.([contents_div]); if (is_tinymce) { // Save the cursor location before the update, then restore it // afterwards, if TinyMCE has focus. - const sel = tinymce.activeEditor!.hasFocus() + const sel = tinymce_instance()!.hasFocus() ? saveSelection() : undefined; - tinymce.activeEditor!.setContent(this.contents); + tinymce_instance()!.setContent(this.contents); if (sel !== undefined) { restoreSelection(sel); } @@ -545,7 +524,7 @@ class DocBlockWidget extends WidgetType { destroy(dom: HTMLElement): void { const [contents_div, is_tinymce] = get_contents(dom); // Forget about any typeset math in this node. - window.MathJax.typesetClear([contents_div]); + window.MathJax?.typesetClear?.([contents_div]); // If this is the TinyMCE editor, save it. if (is_tinymce) { const codechat_body = document.getElementById("CodeChat-body")!; @@ -553,8 +532,8 @@ class DocBlockWidget extends WidgetType { codechat_body.insertBefore(tinymce_div, null); // Make TinyMCE invisible, since it's placed below the body of the // page. - tinymce.get(0)!.dom.addClass(tinymce_div, CODECHAT_DOC_HIDDEN); - tinymce.get(0)!.resetContent(); + tinymce_instance()!.dom.addClass(tinymce_div, CODECHAT_DOC_HIDDEN); + tinymce_instance()!.resetContent(); } } } @@ -572,17 +551,109 @@ export const mathJaxTypeset = async ( // The node to typeset. node: HTMLElement, ) => { - try { - await window.MathJax.typesetPromise([node]); - /*eslint-disable-next-line @typescript-eslint/no-explicit-any */ - } catch (err: any) { - report_error(`Typeset failed: ${err.message}`); + // If MathJax isn't loaded, look for math on the page. + if (window.MathJax === undefined) { + const mathDelimiters = [ + // See `replace_math_node` in `processing.rs` -- this is how Math is + // marked. + { start: "$$", end: "$$" }, + { start: "\\(", end: "\\)" }, + ]; + + // Check if Math tags or the text delimiters exist in the page body + const nodeContent = node.innerHTML; + const hasTeXMath = mathDelimiters.some((delimiter) => { + const startIdx = nodeContent.indexOf(delimiter.start); + return ( + startIdx !== -1 && + nodeContent.indexOf( + delimiter.end, + startIdx + delimiter.start.length, + ) !== -1 + ); + }); + + // If mathematical content is detected, load MathJax. + if (hasTeXMath) { + // Configure MathJax settings. + window.MathJax = { + // See the + // [docs](https://docs.mathjax.org/en/latest/options/output/chtml.html#option-descriptions), + // [postFilters](https://docs.mathjax.org/en/latest/options/output/index.html#output-postfilters); + // see also the + // [TinyMCE non-editable class](https://www.tiny.cloud/docs/tinymce/latest/non-editable-content-options/#noneditable_class). + // After some experimentation, I discovered: + // + // * Setting the `classList` had no effect. I still think it's a + // good idea for the future, though. + // * I can't use the `postFilter` to enclose this in a span with + // the appropriate class; MathJax disallows editing the + // `mjx-container` element. + // * Simply setting `contentEditable` is what actually works. + chtml: { + fontURL: "/static/mathjax-newcm-font/chtml/woff2", + }, + output: { + postFilters: [ + /*eslint-disable-next-line @typescript-eslint/no-explicit-any */ + (obj: { data: any }) => { + obj.data.classList.add("mceNonEditable"); + obj.data.contentEditable = false; + }, + ], + }, + }; + + // Load MathJax. There are several states for MathJax loading: + // + // 1. Not loaded: `window.MathJax === undefined`. + // 2. Load started: `windows.MathJax` is defined (see above -- this + // is required to configure MathJax properly, but doesn't + // guarantee that the library has finished loading and setup). + // 3. Load complete: `window.MathJax.typesetPromise/untypeset/etc.` + // is loaded. + // 4. Initial render complete. + // + // Unfortunately, since CodeMirror is synchronous, it will continue + // calling this function and related functions even during an await. + // To emulate a lock, put step 1-3 checks on all MathJax functions, + // skipping calling them until step 4. + await new Promise((resolve) => { + const script = document.createElement("script"); + script.src = "/static/mathjax/tex-chtml.js"; + script.async = true; + script.onload = resolve; + script.onerror = () => { + report_error(`Failed to load script: ${script.src}`); + // We've already reported the error; don't `reject()`, which + // would propagate this error up the call chain and further + // break things. + resolve(0); + }; + document.head.appendChild(script); + }); + // Wait until MathJax is fully loaded and the initial render is + // finished. Note that this also renders newly-added math. + await window.MathJax.startup.promise; + } + } else { + // MathJax is already loaded; just typeset the provided node. + try { + // MathJax may still be loading when this is called, since + // CodeMirror lacks async support. Use `?.` to skip typesetting in + // this case. + await window.MathJax.typesetPromise?.([node]); + } catch (err: unknown) { + report_error( + `Typeset failed: ${err instanceof Error ? err.message : "unknown"}`, + ); + } } }; // Transform a typeset node back to the original (untypeset) text. export const mathJaxUnTypeset = (node: HTMLElement) => { - window.MathJax.startup.document + window.MathJax?.startup?.document .getMathItemsWithin(node) /*eslint-disable-next-line @typescript-eslint/no-explicit-any */ .forEach((item: any) => { @@ -594,8 +665,10 @@ export const mathJaxUnTypeset = (node: HTMLElement) => { // attached to that div. const get_contents = (element: HTMLElement): [HTMLDivElement, boolean] => { const contents_div = element.childNodes[1] as HTMLDivElement; - const tinymce_inst = tinymce.get(contents_div.id); - return [contents_div, tinymce_inst !== null]; + const tinymce_inst = tinymce?.get(contents_div.id); + // Note the use of `!=` to check both `undefined` (TinyMCE not loaded) and + // `null`. + return [contents_div, tinymce_inst != null]; }; // Determine if the element which generated the provided event was in a doc @@ -646,8 +719,11 @@ const on_dirty = ( set_is_dirty(); on_dirty_scheduled = true; - // Only run this after typesetting is done. - window.MathJax.whenReady(async () => { + // Only run this after typesetting is done, if MathJax is loaded; otherwise, + // run this immediately. + const whenReady = + window.MathJax?.whenReady ?? (async (f: () => void) => f()); + whenReady(async () => { on_dirty_scheduled = false; // Find the doc block parent div. const target = (event_target as HTMLDivElement).closest( @@ -673,11 +749,11 @@ const on_dirty = ( mathJaxUnTypeset(contents_div); // Use the raw format; see the implementation notes. const contents = is_tinymce - ? tinymce.activeEditor!.save({ format: "raw" }) + ? tinymce_instance()!.save({ format: "raw" }) : contents_div.innerHTML; // The `save()` flushes any duplicate `Dirty` events. After this, // following `Dirty` events are genuine. - ignoreDirty = false; + ignoreTinyMceDirty = false; await mathJaxTypeset(contents_div); current_view.dispatch({ effects: [ @@ -809,10 +885,132 @@ export const DocBlockPlugin = ViewPlugin.fromClass( // cursor position (the selection) to be set in the // contenteditable div. Then, save that location. setTimeout(async () => { + // In case this node was modified during the timeout. + if (!contents_div.isConnected) { + return; + } + // Create the TinyMCE instance if necessary. Note the + // use of `==` here to check for `null` (TinyMCE is + // loaded, but no instance exists) and `undefined` + // (TinyMCE isn't loaded). + if (tinymce_instance() == null) { + await init({ + selector: "#TinyMCE-inst", + setup: (editor: Editor) => { + // See the + // [docs](https://www.tiny.cloud/docs/tinymce/latest/events/#editor-core-events). + // After much experimentation, using both an + // `input` event (which suppresses the + // redundant `Dirty` event which follows it) + // combined with a `Dirty` event (which + // catches GUI interactions, undo, etc. + // which doesn't produce an `input` event). + // Just using `Dirty` produces one failing + // case: insert a character (dirty event), + // delete the character (no dirty event), + // left arrow (delayed dirty event from + // backspace). + // + // Here's a demonstration of the bug and its + // fix: + // + // ```html + // + // + // + // + // TinyMCE Dirty Event Test + // + // + //

TinyMCE Dirty Event Test

+ // + // + // + // + // ``` + editor.on( + "Dirty", + ( + event: EditorEvent< + Events.EditorEventMap["dirty"] + >, + ) => { + // Sometimes, `tinymce.activeEditor` is + // null (perhaps when it's not focused). + // Use the `event` data instead. Get the + // div TinyMCE stores edits in. + const target = + event.target.bodyElement; + if (target == null) { + return; + } + if (!ignoreTinyMceDirty) { + on_dirty(target); + } + }, + ); + + editor.on("input", (event: InputEvent) => { + const target = + event.target as HTMLElement; + if (target == null) { + return; + } + ignoreTinyMceDirty = true; + on_dirty(target); + }); + + // Send updates on cursor movement. + editor.on( + "SelectionChange", + ( + _event: EditorEvent< + Events.EditorEventMap["SelectionChange"] + >, + ) => { + startAutoUpdateTimer(); + }, + ); + }, + }); + } + // Before untypesetting, make sure all other typesets // finish. - await new Promise((resolve) => - window.MathJax.whenReady(() => resolve()), + await new Promise( + (resolve) => + window.MathJax?.whenReady?.(() => resolve()) ?? + resolve(), ); // Untypeset math in the old doc block and the current // doc block before moving its contents around. @@ -844,7 +1042,7 @@ export const DocBlockPlugin = ViewPlugin.fromClass( // goes to a CodeMirror layer). old_contents_div.tabIndex = 0; old_contents_div.innerHTML = - tinymce.activeEditor!.save(); + tinymce_instance()!.save(); tinymce_div.parentNode!.insertBefore( old_contents_div, null, @@ -864,11 +1062,11 @@ export const DocBlockPlugin = ViewPlugin.fromClass( // edits to the previous doc block no longer apply here. // TODO: Eventually, we need a way to chain TinyMCE's // undo history with CodeMirror's undo history. - tinymce.activeEditor!.resetContent( + tinymce_instance()!.resetContent( contents_div.innerHTML, ); contents_div.remove(); - tinymce.activeEditor!.dom.removeClass( + tinymce_instance()!.dom.removeClass( tinymce_div, CODECHAT_DOC_HIDDEN, ); @@ -953,6 +1151,10 @@ const autosaveExtension = EditorView.updateListener.of( }, ); +// Wrap a stream language dynamic import. +const import_stream_language = async (lang: StreamParser) => + (await import("@codemirror/language")).StreamLanguage.define(lang); + // Given source code in a CodeMirror-friendly JSON format, load it into the // provided div. export const CodeMirror_load = async ( @@ -984,83 +1186,107 @@ export const CodeMirror_load = async ( scrollSnapshot = current_view.scrollSnapshot(); // For reloads, we need to remove previous instances; otherwise, Bad // Things happen. - tinymce.remove(); + tinymce?.remove(); } codechat_body.innerHTML = `
`; let parser; - // TODO: dynamically load the parser. + // Dynamically load the parser. switch (codechat_for_web.metadata.mode) { // Languages with a parser. case "sh": - parser = StreamLanguage.define(shell); + parser = await import_stream_language( + (await import("@codemirror/legacy-modes/mode/shell")).shell, + ); break; case "cpp": - parser = cpp(); + parser = (await import("@codemirror/lang-cpp")).cpp(); break; case "csharp": - parser = javascript(); + parser = ( + await import("@codemirror/lang-javascript") + ).javascript(); break; case "css": - parser = css(); + parser = (await import("@codemirror/lang-css")).css(); break; case "golang": - parser = go(); + parser = (await import("@codemirror/lang-go")).go(); break; case "html": - parser = html(); + parser = (await import("@codemirror/lang-html")).html(); break; case "java": - parser = java(); + parser = (await import("@codemirror/lang-java")).java(); break; case "javascript": - parser = javascript(); + parser = ( + await import("@codemirror/lang-javascript") + ).javascript(); + break; + // Octave is an open-source MATLAB-ish clone. + case "matlab": + parser = await import_stream_language( + (await import("@codemirror/legacy-modes/mode/octave")) + .octave, + ); break; case "python": - parser = python(); + parser = (await import("@codemirror/lang-python")).python(); break; case "rust": - parser = rust(); + parser = (await import("@codemirror/lang-rust")).rust(); break; case "sql": - parser = sql(); + parser = (await import("@codemirror/lang-sql")).sql(); break; case "swift": - parser = StreamLanguage.define(swift); + parser = await import_stream_language( + (await import("@codemirror/legacy-modes/mode/swift")).swift, + ); break; case "toml": - parser = StreamLanguage.define(toml); + parser = await import_stream_language( + (await import("@codemirror/legacy-modes/mode/toml")).toml, + ); break; case "typescript": - parser = javascript({ typescript: true }); + parser = ( + await import("@codemirror/lang-javascript") + ).javascript({ typescript: true }); break; case "vhdl": - parser = StreamLanguage.define(vhdl); + parser = await import_stream_language( + (await import("@codemirror/legacy-modes/mode/vhdl")).vhdl, + ); break; case "verilog": - parser = StreamLanguage.define(verilog); + parser = await import_stream_language( + (await import("@codemirror/legacy-modes/mode/verilog")) + .verilog, + ); break; case "yaml": - parser = yaml(); + parser = (await import("@codemirror/lang-yaml")).yaml(); break; // Languages without a parser. // // JSON5 allows comments, but JSON doesn't. case "json5": - parser = json(); - break; - // Nothing available. Python isn't even close. - case "matlab": - parser = python(); + parser = (await import("@codemirror/lang-json")).json(); break; // An approximation for Vlang. case "v": - parser = javascript(); + parser = ( + await import("@codemirror/lang-javascript") + ).javascript(); break; default: - parser = javascript(); + parser = ( + await import("@codemirror/lang-javascript") + ).javascript(); report_error( `Unknown lexer name ${codechat_for_web.metadata.mode}`, ); @@ -1105,104 +1331,6 @@ export const CodeMirror_load = async ( state, scrollTo: scrollSnapshot, }); - - await init({ - selector: "#TinyMCE-inst", - setup: (editor: Editor) => { - // See the - // [docs](https://www.tiny.cloud/docs/tinymce/latest/events/#editor-core-events). - // After much experimentation, using both an `input` event - // (which suppresses the redundant `Dirty` event which follows - // it) combined with a `Dirty` event (which catches GUI - // interactions, undo, etc. which doesn't produce an `input` - // event). Just using `Dirty` produces one failing case: insert - // a character (dirty event), delete the character (no dirty - // event), left arrow (delayed dirty event from backspace). - // - // Here's a demonstration of the bug and its fix: - // - // ```html - // - // - // - // - // TinyMCE Dirty Event Test - // - // - //

TinyMCE Dirty Event Test

- // - // - // - // - // ``` - editor.on( - "Dirty", - (event: EditorEvent) => { - // Sometimes, `tinymce.activeEditor` is null (perhaps - // when it's not focused). Use the `event` data instead. - // Get the div TinyMCE stores edits in. - const target = event.target.bodyElement; - if (target == null) { - return; - } - if (!ignoreDirty) { - on_dirty(target); - } - }, - ); - - editor.on("input", (event: InputEvent) => { - const target = event.target as HTMLElement; - if (target == null) { - return; - } - ignoreDirty = true; - on_dirty(target); - }); - - // Send updates on cursor movement. - editor.on( - "SelectionChange", - ( - _event: EditorEvent< - Events.EditorEventMap["SelectionChange"] - >, - ) => { - startAutoUpdateTimer(); - }, - ); - }, - }); } else { // This contains a diff, instead of plain text. Apply the text diff. // @@ -1333,7 +1461,7 @@ export const set_CodeMirror_positions = ( // If a doc block has focus, then the CodeMirror selection reports line 1. // Use the starting line number of the doc block instead. const doc_block = document.activeElement?.closest(".CodeChat-doc"); - let cursor_position; + let cursor_position: CursorPosition; if (doc_block) { const from = current_view.posAtDOM(doc_block); const location = saveSelection(); @@ -1378,3 +1506,10 @@ const report_error = (text: string) => { console.error(text); show_toast(text); }; + +const halt_on_error = (text: string): never => { + document.getElementById("error-overlay")!.style.display = "block"; + console.error(text); + // The error handler will make this a toast. + throw new Error(text); +}; diff --git a/client/src/css/CodeChatEditor.css b/client/src/css/CodeChatEditor.css index 08ae2b12..e68dccd2 100644 --- a/client/src/css/CodeChatEditor.css +++ b/client/src/css/CodeChatEditor.css @@ -95,6 +95,29 @@ body { white-space: nowrap; } +/* Error overlay. When visible, obscure the screen. */ +#error-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + /* The toast z-index is 10000. */ + z-index: 9999; + display: none; + opacity: 0.8; + background-color: gray; +} + +.centered-text { + position: absolute; + top: 50%; + left: 50%; + /* Pulls element back exactly to its own middle */ + transform: translate(-50%, -50%); + text-align: center; +} + /* Doc block styling ----------------- */ .CodeChat-doc { diff --git a/client/src/show_toast.mts b/client/src/show_toast.mts index e1b48e0c..070bdd1b 100644 --- a/client/src/show_toast.mts +++ b/client/src/show_toast.mts @@ -22,7 +22,7 @@ import "toastify-js/src/toastify.css"; export const show_toast = (text: string) => Toastify({ text, - duration: 10000, + duration: 20000, newWindow: true, close: true, escapeMarkup: true, @@ -31,5 +31,6 @@ export const show_toast = (text: string) => stopOnFocus: true, style: { background: "linear-gradient(to right, #b00049ff, #e76d8bff)", + zIndex: "10000", }, }).showToast(); diff --git a/client/src/tinymce-config.mts b/client/src/tinymce-config.mts index d6c7a89b..7cc72221 100644 --- a/client/src/tinymce-config.mts +++ b/client/src/tinymce-config.mts @@ -19,18 +19,11 @@ // ========================================================================= // // Import TinyMCE. -import { - default as tinymce_, - Editor, - RawEditorOptions, - TinyMCE, -} from "tinymce"; +import { default as tinymce, Editor, RawEditorOptions } from "tinymce"; // This is taken from the // [TinyMCE example code](https://github.com/tinymce/tinymce/blob/main/modules/tinymce/src/core/demo/ts/demo/TinyMceDemo.ts). -// However, esbuild doesn't accept it. -//export declare const tinymce: TinyMCE; -// Here's a workaround. -export const tinymce = tinymce_ as unknown as TinyMCE; +// However, esbuild doesn't accept it. This is the workaround: +export { tinymce }; // Default icons are required for TinyMCE 5.3 or above. import "tinymce/icons/default/index.js"; @@ -38,8 +31,7 @@ import "tinymce/icons/default/index.js"; // A theme is also required. import "tinymce/themes/silver/index.js"; -// Import the skin to use; use're using an inline editor, so load the inline -// CSS. +// Import the skin to use; we're using an inline editor, so load the inline CSS. import "tinymce/skins/ui/oxide/skin.css"; import "tinymce/skins/ui/oxide/content.inline.css"; @@ -51,11 +43,13 @@ import "tinymce/plugins/advlist/index.js"; import "tinymce/plugins/anchor/index.js"; import "tinymce/plugins/charmap/index.js"; import "tinymce/plugins/directionality/index.js"; -import "tinymce/plugins/emoticons/index.js"; -import "tinymce/plugins/emoticons/js/emojis.js"; -import "tinymce/plugins/emoticons/js/emojiimages.js"; +// Omit these, since they're large and infrequently used. TODO: dynamically load +// these on first use. +/// import "tinymce/plugins/emoticons/index.js"; +/// import "tinymce/plugins/emoticons/js/emojis.js"; +/// import "tinymce/plugins/emoticons/js/emojiimages.js"; import "tinymce/plugins/help/index.js"; -// TODO: this should be a dynamic import. +// TODO: this should be a dynamic import based on the current language. import "tinymce/plugins/help/js/i18n/keynav/en.js"; import "tinymce/plugins/image/index.js"; import "tinymce/plugins/link/index.js"; @@ -75,80 +69,84 @@ import "tinymce/plugins/visualchars/index.js"; // Initialize TinyMCE. export const init = async ( - // Provide editor options; don't set `plugins` or `skin`, since these must - // be accompanied by the correct imports. - options: RawEditorOptions, + // Provide additional editor options; note that the basic setup provided + // below can't be overridden by these options. The only exception is a value + // for `setup`, which will be combined with the setup defined in this + // function. + addiitionalOptions: RawEditorOptions, ) => { - // Merge the provided options with these default options. - const combinedOptions = Object.assign({}, options, { - // See the list of - // [plugins](https://www.tiny.cloud/docs/tinymce/6/plugins/). These must - // be accompanied by the corresponding import above. - plugins: - "advlist anchor charmap directionality emoticons help image link lists media quickbars searchreplace table visualblocks visualchars", - // The imports above apply the skins; don't try to dynamically load the - // skin's CSS. - skin: false, - // Enable the - // [browser-supplied spellchecker](https://www.tiny.cloud/docs/tinymce/6/spelling/#browser_spellcheck), - // since TinyMCE's spellchecker is a premium feature. - browser_spellcheck: true, - // Place the Tiny MCE menu bar at the top of the screen; otherwise, it - // floats in front of text, sometimes obscuring what the user wants to - // edit. See the - // [docs](https://www.tiny.cloud/docs/configure/editor-appearance/#fixed_toolbar_container). - fixed_toolbar_container: "#CodeChat-menu", - inline: true, - // When true, this still prevents hyperlinks to anchors on the current - // page from working correctly. There's an onClick handler that prevents - // links in the current page from working -- need to look into this. See - // also - // [a related GitHub issue](https://github.com/tinymce/tinymce/issues/3836). - //readonly: true // Per the comment above, this is commented out. - // Use relative URLs in hyperlinks. - relative_urls: true, - // Disable the - // [TinyMCE toolbar buttons](https://www.tiny.cloud/blog/tinymce-toolbar/) - // to provide more real estate on the screen. - toolbar: false, - // Don't show the file option on the - // [menu](https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#menubar), - // which is useless. - menubar: "edit insert view format table tools help", - // See - // [License key](https://www.tiny.cloud/docs/tinymce/latest/license-key). - license_key: "gpl", - // Block drag-and-drop of unsupported images and files. See the - // [docs](https://www.tiny.cloud/docs/tinymce/latest/file-image-upload/#block_unsupported_drop). - block_unsupported_drop: true, - // Prevent drag-and-dropping images; this create a mess. See the - // [docs](https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_data_images). - paste_data_images: false, + // Merge the provided options with this basic setup, giving priority to the + // settings below. + const combinedOptions: RawEditorOptions = Object.assign( + {}, + addiitionalOptions, + { + // See the list of + // [plugins](https://www.tiny.cloud/docs/tinymce/latest/plugins/). + // These must be accompanied by the corresponding import above. + plugins: + "advlist anchor charmap directionality help image link lists media quickbars searchreplace table visualblocks visualchars", + // The imports above apply the skins; don't try to dynamically load + // the skin's CSS. + skin: false, + // Enable the + // [browser-supplied spellchecker](https://www.tiny.cloud/docs/tinymce/latest/spelling/#browser_spellcheck), + // since TinyMCE's spellchecker is a premium feature. + browser_spellcheck: true, + // Place the Tiny MCE menu bar at the top of the screen; otherwise, + // it floats in front of text, sometimes obscuring what the user + // wants to edit. See the + // [docs](https://www.tiny.cloud/docs/configure/editor-appearance/#fixed_toolbar_container). + fixed_toolbar_container: "#CodeChat-menu", + inline: true, + // When true, this still prevents hyperlinks to anchors on the + // current page from working correctly. There's an onClick handler + // that prevents links in the current page from working -- need to + // look into this. See also + // [a related GitHub issue](https://github.com/tinymce/tinymce/issues/3836). + //readonly: true // Per the comment above, this is commented out. + // Use relative URLs in hyperlinks. + relative_urls: true, + // Disable the + // [TinyMCE toolbar buttons](https://www.tiny.cloud/blog/tinymce-toolbar/) + // to provide more real estate on the screen. + toolbar: false, + // Don't show the file option on the + // [menu](https://www.tiny.cloud/docs/tinymce/latest/menus-configuration-options/#menubar), + // which is useless. + menubar: "edit insert view format table tools help", + // See + // [License key](https://www.tiny.cloud/docs/tinymce/latest/license-key). + license_key: "gpl", + // Block drag-and-drop of unsupported images and files. See the + // [docs](https://www.tiny.cloud/docs/tinymce/latest/file-image-upload/#block_unsupported_drop). + block_unsupported_drop: true, + // Prevent drag-and-dropping images; this create a mess. See the + // [docs](https://www.tiny.cloud/docs/tinymce/latest/copy-and-paste/#paste_data_images). + paste_data_images: false, - // ### Settings for plugins - // - // [Image](https://www.tiny.cloud/docs/plugins/opensource/image/) - image_caption: true, - image_advtab: true, - image_title: true, + // ### Settings for plugins + // + // [Image](https://www.tiny.cloud/docs/plugins/opensource/image/) + image_caption: true, + image_advtab: true, + image_title: true, - // Quickbar config: disable the insert toolbar (which doesn't seem - // useful, and also has the image insert, which is problematic - // currently). - quickbars_insert_toolbar: false, - // Put more buttons on the - // [quick toolbar](https://www.tiny.cloud/docs/tinymce/6/quickbars/) - // that appears when text is selected. TODO: add a button for code - // format (can't find this one -- it's only on the - // [list of menu items](https://www.tiny.cloud/docs/tinymce/6/available-menu-items/#the-core-menu-items) - // as `codeformat`). - quickbars_selection_toolbar: - "bold italic underline codeformat | quicklink h2 h3", + // Quickbar config: disable the insert toolbar (which doesn't seem + // useful, and also has the image insert, which is problematic + // currently). + quickbars_insert_toolbar: false, + // Put more buttons on the + // [quick toolbar](https://www.tiny.cloud/docs/tinymce/latest/quickbars/) + // that appears when text is selected. + quickbars_selection_toolbar: + "bold italic underline codeformat | quicklink h2 h3", - // Needed to allow custom elements. - extended_valid_elements: "graphviz-graph[scale],wc-mermaid", - custom_elements: "graphviz-graph,wc-mermaid", - }); + // Needed to allow custom elements. + extended_valid_elements: "graphviz-graph[scale],wc-mermaid", + custom_elements: "graphviz-graph,wc-mermaid", + }, + ); // Merge in additional setup code. const oldSetup = combinedOptions.setup; @@ -172,7 +170,7 @@ export const init = async ( }; // Use these combined options to - // [init](https://www.tiny.cloud/docs/tinymce/6/apis/tinymce.root/#init) + // [init](https://www.tiny.cloud/docs/tinymce/latest/apis/tinymce.root/#init) // TinyMCE. return await tinymce.init(combinedOptions); }; diff --git a/extensions/VSCode/Cargo.lock b/extensions/VSCode/Cargo.lock index 44675c36..08137cc7 100644 --- a/extensions/VSCode/Cargo.lock +++ b/extensions/VSCode/Cargo.lock @@ -44,9 +44,9 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.12.1" +version = "3.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93acb4a42f64936f9b8cae4a433b237599dd6eb6ed06124eb67132ef8cc90662" +checksum = "48e2faa3e7418ed780cca54829d32782a4008a077230f67457caa063415e99c2" dependencies = [ "actix-codec", "actix-rt", @@ -60,7 +60,7 @@ dependencies = [ "derive_more", "encoding_rs", "flate2", - "foldhash", + "foldhash 0.2.0", "futures-core", "h2", "http", @@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.13.0" +version = "4.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf" +checksum = "df09e2d9239703dd64056359c920c7f3fba6535ec61a0059e0f44e095ffe02b4" dependencies = [ "actix-codec", "actix-http", @@ -175,7 +175,7 @@ dependencies = [ "cookie", "derive_more", "encoding_rs", - "foldhash", + "foldhash 0.2.0", "futures-core", "futures-util", "impl-more", @@ -206,7 +206,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -406,7 +406,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -448,9 +448,9 @@ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -546,9 +546,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" dependencies = [ "serde", ] @@ -573,9 +573,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.64" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "jobserver", @@ -644,7 +644,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -670,7 +670,7 @@ dependencies = [ [[package]] name = "codechat-editor-server" -version = "0.1.57" +version = "0.1.58" dependencies = [ "actix-files", "actix-http", @@ -705,7 +705,7 @@ dependencies = [ "path-slash", "pest", "pest_derive", - "phf 0.13.1", + "phf 0.14.0", "pulldown-cmark 0.13.4", "rand 0.10.1", "regex", @@ -727,7 +727,7 @@ dependencies = [ [[package]] name = "codechat-editor-vscode-extension" -version = "0.1.57" +version = "0.1.58" dependencies = [ "codechat-editor-server", "log", @@ -953,7 +953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1024,7 +1024,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.118", "unicode-xid", ] @@ -1070,7 +1070,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1231,6 +1231,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1311,7 +1317,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1378,16 +1384,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "wasip2", - "wasip3", ] [[package]] @@ -1456,7 +1460,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1663,12 +1667,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -1718,9 +1716,9 @@ dependencies = [ [[package]] name = "impl-more" -version = "0.1.9" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" +checksum = "35a84fd5aa25fae5c0f4a33d9cac2ca017fc622cbd089be2229993514990f870" [[package]] name = "indexmap" @@ -1820,7 +1818,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1839,7 +1837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1901,12 +1899,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" version = "0.2.186" @@ -2012,9 +2004,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" dependencies = [ "serde_core", ] @@ -2168,9 +2160,9 @@ dependencies = [ [[package]] name = "minreq" -version = "2.14.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05015102dad0f7d61691ca347e9d9d9006685a64aefb3d79eecf62665de2153d" +checksum = "659579df697b372ef9e36f02fcbb41f6d6f157dcec7db9c9618fa0f23cf0fc20" [[package]] name = "mio" @@ -2192,9 +2184,9 @@ checksum = "9bb517913cfcfb9eeda59f36020269075a152701a01606c612f547e4890be399" [[package]] name = "napi" -version = "3.9.2" +version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d3c7dd60231116a47854321c9ac8df6f13435d11aa3a59d8533a76e07a3730" +checksum = "fbd9f9295f3ff5921e78a71222c3361a8216f7760b1a99a6ad4e8441de18bbb9" dependencies = [ "bitflags", "ctor", @@ -2223,7 +2215,7 @@ dependencies = [ "napi-derive-backend", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2236,7 +2228,7 @@ dependencies = [ "proc-macro2", "quote", "semver", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2445,9 +2437,9 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "oxc-browserslist" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209091efbb07de356beae15cf5fd8bd2b3ab94a17f19cc19a7f8b094aec2559c" +checksum = "2b87743c2f6963036971ca587539cd9235693d953dc018a734014241afb20d5b" dependencies = [ "flate2", "postcard", @@ -2479,7 +2471,7 @@ checksum = "b237422b014f8f8fff75bb9379e697d13f8d57551a22c88bebb39f073c1bf696" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2521,7 +2513,7 @@ dependencies = [ "phf 0.13.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2911,7 +2903,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2945,6 +2937,17 @@ dependencies = [ "serde", ] +[[package]] +name = "phf" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6" +dependencies = [ + "phf_macros 0.14.0", + "phf_shared 0.14.0", + "serde", +] + [[package]] name = "phf_codegen" version = "0.11.3" @@ -2985,6 +2988,16 @@ dependencies = [ "phf_shared 0.13.1", ] +[[package]] +name = "phf_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100" +dependencies = [ + "fastrand", + "phf_shared 0.14.0", +] + [[package]] name = "phf_macros" version = "0.11.3" @@ -2995,7 +3008,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3008,7 +3021,20 @@ dependencies = [ "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", +] + +[[package]] +name = "phf_macros" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085" +dependencies = [ + "phf_generator 0.14.0", + "phf_shared 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.118", ] [[package]] @@ -3029,6 +3055,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -3140,16 +3175,6 @@ dependencies = [ "termtree", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -3210,9 +3235,9 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -3261,7 +3286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", - "getrandom 0.4.2", + "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -3523,7 +3548,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3673,9 +3698,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smawk" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" [[package]] name = "socket2" @@ -3764,9 +3789,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -3781,7 +3806,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3864,7 +3889,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3888,9 +3913,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.49" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" dependencies = [ "deranged", "num-conv", @@ -3908,9 +3933,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.29" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" dependencies = [ "num-conv", "time-core", @@ -3966,7 +3991,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4029,7 +4054,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4089,7 +4114,7 @@ checksum = "38d90eea51bc7988ef9e674bf80a85ba6804739e535e9cab48e4bb34a8b652aa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "termcolor", ] @@ -4299,16 +4324,7 @@ version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] @@ -4352,7 +4368,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] @@ -4365,40 +4381,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" version = "0.3.102" @@ -4411,9 +4393,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" dependencies = [ "phf 0.13.1", "phf_codegen 0.13.1", @@ -4534,7 +4516,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4545,7 +4527,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4747,100 +4729,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "writeable" version = "0.6.3" @@ -4885,7 +4779,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] @@ -4906,7 +4800,7 @@ checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4926,7 +4820,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] @@ -4960,7 +4854,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] diff --git a/extensions/VSCode/Cargo.toml b/extensions/VSCode/Cargo.toml index ee101bbe..607fb0b0 100644 --- a/extensions/VSCode/Cargo.toml +++ b/extensions/VSCode/Cargo.toml @@ -32,7 +32,7 @@ license = "GPL-3.0-only" name = "codechat-editor-vscode-extension" readme = "../README.md" repository = "https://github.com/bjones1/CodeChat_Editor" -version = "0.1.57" +version = "0.1.58" [lib] crate-type = ["cdylib"] diff --git a/extensions/VSCode/package.json b/extensions/VSCode/package.json index 342953cb..18edc835 100644 --- a/extensions/VSCode/package.json +++ b/extensions/VSCode/package.json @@ -41,7 +41,7 @@ "type": "git", "url": "https://github.com/bjones1/CodeChat_Editor" }, - "version": "0.1.57", + "version": "0.1.58", "activationEvents": [ "onCommand:extension.codeChatEditorActivate", "onCommand:extension.codeChatEditorDeactivate" @@ -88,8 +88,8 @@ "@types/escape-html": "^1.0.4", "@types/node": "^24.13.2", "@types/vscode": "1.61.0", - "@typescript-eslint/eslint-plugin": "^8.61.0", - "@typescript-eslint/parser": "^8.61.0", + "@typescript-eslint/eslint-plugin": "^8.61.1", + "@typescript-eslint/parser": "^8.61.1", "@vscode/vsce": "^3.9.2", "chalk": "^5.6.2", "esbuild": "^0.28.1", @@ -101,7 +101,7 @@ "npm-run-all2": "^9.0.2", "prettier": "^3.8.4", "typescript": "^6.0.3", - "typescript-eslint": "^8.61.0" + "typescript-eslint": "^8.61.1" }, "optionalDependencies": { "bufferutil": "^4.1.0" diff --git a/extensions/VSCode/pnpm-lock.yaml b/extensions/VSCode/pnpm-lock.yaml index d52c8e64..357d0cf5 100644 --- a/extensions/VSCode/pnpm-lock.yaml +++ b/extensions/VSCode/pnpm-lock.yaml @@ -37,11 +37,11 @@ importers: specifier: 1.61.0 version: 1.61.0 '@typescript-eslint/eslint-plugin': - specifier: ^8.61.0 - version: 8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3) + specifier: ^8.61.1 + version: 8.61.1(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3) '@typescript-eslint/parser': - specifier: ^8.61.0 - version: 8.61.0(eslint@10.5.0)(typescript@6.0.3) + specifier: ^8.61.1 + version: 8.61.1(eslint@10.5.0)(typescript@6.0.3) '@vscode/vsce': specifier: ^3.9.2 version: 3.9.2 @@ -59,7 +59,7 @@ importers: version: 10.1.8(eslint@10.5.0) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0) + version: 2.32.0(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0) eslint-plugin-node: specifier: ^11.1.0 version: 11.1.0(eslint@10.5.0) @@ -76,8 +76,8 @@ importers: specifier: ^6.0.3 version: 6.0.3 typescript-eslint: - specifier: ^8.61.0 - version: 8.61.0(eslint@10.5.0)(typescript@6.0.3) + specifier: ^8.61.1 + version: 8.61.1(eslint@10.5.0)(typescript@6.0.3) optionalDependencies: bufferutil: specifier: ^4.1.0 @@ -123,16 +123,16 @@ packages: resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} engines: {node: '>=20.0.0'} - '@azure/msal-browser@5.13.0': - resolution: {integrity: sha512-Ea23x0U8XNFY+qJ9T44zO2BbY+AHdb+WdjmYnx36OhJ/KO+PGU5pmsNHf1DCElYX+6wyVRJz1HFeCPC/cHbRug==} + '@azure/msal-browser@5.14.0': + resolution: {integrity: sha512-Dfl7hPZe9/JJwRhFFXHq2z1oHYBuGubmff3kWXOsd1AGgyXlqjNYAWuN/1JL/ZrcZBs8TKMjGSil6Rcc7E8VPQ==} engines: {node: '>=0.8.0'} - '@azure/msal-common@16.8.0': - resolution: {integrity: sha512-5S4RHOcInL2Nu2U217tDZbWGI6StMfcWCrA7TWvWdJmXQ+cYrrIqr84AsN62fGh2MDBysiBJPt6CfWceJfloEA==} + '@azure/msal-common@16.9.0': + resolution: {integrity: sha512-1MWGjqgUCRAYgLmVFZKp7fs3Rg1TFvIMgywY8ze2olNVvLlJoRThuoziWSDJuwwyJI5L4rnLb9Tyt5D9GvSLPw==} engines: {node: '>=0.8.0'} - '@azure/msal-node@5.2.4': - resolution: {integrity: sha512-rpBUg9dA8UpC2WiFt3KeDKVQmmmVrfxdRnW+F1ebgou/jX/0tAvYuonaq5RUo8OaqzOrj4x/HaI8DmY56RXZ2Q==} + '@azure/msal-node@5.2.5': + resolution: {integrity: sha512-RUuewWk9JvWJS5Yiy8/74Lm1rQAWlrU/qg/Bgtk1jIauVRtnb9XKwS5Xg0J+Whwjesq9EVrBIFgQEP8vHxgezA==} engines: {node: '>=20'} '@babel/code-frame@7.29.7': @@ -1019,63 +1019,63 @@ packages: '@types/vscode@1.61.0': resolution: {integrity: sha512-9k5Nwq45hkRwdfCFY+eKXeQQSbPoA114mF7U/4uJXRBJeGIO7MuJdhF1PnaDN+lllL9iKGQtd6FFXShBXMNaFg==} - '@typescript-eslint/eslint-plugin@8.61.0': - resolution: {integrity: sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==} + '@typescript-eslint/eslint-plugin@8.61.1': + resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.61.0 + '@typescript-eslint/parser': ^8.61.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.61.0': - resolution: {integrity: sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==} + '@typescript-eslint/parser@8.61.1': + resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.61.0': - resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==} + '@typescript-eslint/project-service@8.61.1': + resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.61.0': - resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==} + '@typescript-eslint/scope-manager@8.61.1': + resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.61.0': - resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==} + '@typescript-eslint/tsconfig-utils@8.61.1': + resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.61.0': - resolution: {integrity: sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==} + '@typescript-eslint/type-utils@8.61.1': + resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.61.0': - resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==} + '@typescript-eslint/types@8.61.1': + resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.61.0': - resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==} + '@typescript-eslint/typescript-estree@8.61.1': + resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.61.0': - resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==} + '@typescript-eslint/utils@8.61.1': + resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.61.0': - resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==} + '@typescript-eslint/visitor-keys@8.61.1': + resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typespec/ts-http-runtime@0.3.6': @@ -1294,8 +1294,8 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -1485,6 +1485,10 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + es-abstract@1.24.2: resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} @@ -1509,12 +1513,12 @@ packages: resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + es-to-primitive@1.3.1: + resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==} engines: {node: '>= 0.4'} - es-toolkit@1.47.1: - resolution: {integrity: sha512-5RAqEwf4P4E17p+W75KLOWw/nOvKZzSQpxM32IpI2KZLaVonjTrZ0Ai5ghMaVI9eKC2p8eoQgcBdkEDgzFk6+Q==} + es-toolkit@1.48.1: + resolution: {integrity: sha512-wfnXlwd5I75eXRtdD2vuEs50xHHESECDsGD7yiQnfFVNoa5522NwXEbmgo98LfiukSQHs+mBM7/YG3qKJB9/mQ==} esbuild@0.28.1: resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} @@ -2471,8 +2475,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -2687,8 +2691,8 @@ packages: typed-rest-client@1.8.11: resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} - typescript-eslint@8.61.0: - resolution: {integrity: sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==} + typescript-eslint@8.61.1: + resolution: {integrity: sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2712,8 +2716,8 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici@7.27.2: - resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} + undici@7.28.0: + resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} unicorn-magic@0.1.0: @@ -2880,8 +2884,8 @@ snapshots: '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 - '@azure/msal-browser': 5.13.0 - '@azure/msal-node': 5.2.4 + '@azure/msal-browser': 5.14.0 + '@azure/msal-node': 5.2.5 open: 10.2.0 tslib: 2.8.1 transitivePeerDependencies: @@ -2894,15 +2898,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/msal-browser@5.13.0': + '@azure/msal-browser@5.14.0': dependencies: - '@azure/msal-common': 16.8.0 + '@azure/msal-common': 16.9.0 - '@azure/msal-common@16.8.0': {} + '@azure/msal-common@16.9.0': {} - '@azure/msal-node@5.2.4': + '@azure/msal-node@5.2.5': dependencies: - '@azure/msal-common': 16.8.0 + '@azure/msal-common': 16.9.0 jsonwebtoken: 9.0.3 '@babel/code-frame@7.29.7': @@ -3101,7 +3105,7 @@ snapshots: '@inquirer/external-editor@3.0.3(@types/node@24.13.2)': dependencies: - chardet: 2.1.1 + chardet: 2.2.0 iconv-lite: 0.7.2 optionalDependencies: '@types/node': 24.13.2 @@ -3182,10 +3186,10 @@ snapshots: clipanion: 4.0.0-rc.4(typanion@3.14.0) colorette: 2.0.20 emnapi: 1.11.1 - es-toolkit: 1.47.1 + es-toolkit: 1.48.1 js-yaml: 4.2.0 obug: 2.1.3 - semver: 7.8.4 + semver: 7.8.5 typanion: 3.14.0 optionalDependencies: '@emnapi/runtime': 1.11.1 @@ -3647,14 +3651,14 @@ snapshots: '@types/vscode@1.61.0': {} - '@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/type-utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/type-utils': 8.61.1(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.61.1 eslint: 10.5.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -3663,41 +3667,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3)': + '@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.61.1 debug: 4.4.3 eslint: 10.5.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.0(typescript@6.0.3)': + '@typescript-eslint/project-service@8.61.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3) - '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) + '@typescript-eslint/types': 8.61.1 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.61.0': + '@typescript-eslint/scope-manager@8.61.1': dependencies: - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 - '@typescript-eslint/tsconfig-utils@8.61.0(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.61.1(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.61.0(eslint@10.5.0)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.61.1(eslint@10.5.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0)(typescript@6.0.3) debug: 4.4.3 eslint: 10.5.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -3705,37 +3709,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.61.0': {} + '@typescript-eslint/types@8.61.1': {} - '@typescript-eslint/typescript-estree@8.61.0(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.61.0(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3) - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/project-service': 8.61.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.0(eslint@10.5.0)(typescript@6.0.3)': + '@typescript-eslint/utils@8.61.1(eslint@10.5.0)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) eslint: 10.5.0 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.61.0': + '@typescript-eslint/visitor-keys@8.61.1': dependencies: - '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/types': 8.61.1 eslint-visitor-keys: 5.0.1 '@typespec/ts-http-runtime@0.3.6': @@ -3809,7 +3813,7 @@ snapshots: parse-semver: 1.1.1 read: 1.0.7 secretlint: 10.2.2 - semver: 7.8.4 + semver: 7.8.5 tmp: 0.2.7 typed-rest-client: 1.8.11 url-join: 4.0.1 @@ -4004,7 +4008,7 @@ snapshots: chalk@5.6.2: {} - chardet@2.1.1: {} + chardet@2.2.0: {} cheerio-select@2.1.0: dependencies: @@ -4026,7 +4030,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.27.2 + undici: 7.28.0 whatwg-mimetype: 4.0.0 chownr@1.1.4: @@ -4194,6 +4198,13 @@ snapshots: environment@1.1.0: {} + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + es-abstract@1.24.2: dependencies: array-buffer-byte-length: 1.0.2 @@ -4208,7 +4219,7 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 + es-to-primitive: 1.3.1 function.prototype.name: 1.2.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 @@ -4270,13 +4281,15 @@ snapshots: dependencies: hasown: 2.0.4 - es-to-primitive@1.3.0: + es-to-primitive@1.3.1: dependencies: + es-abstract-get: 1.0.0 + es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 - es-toolkit@1.47.1: {} + es-toolkit@1.48.1: {} esbuild@0.28.1: optionalDependencies: @@ -4323,11 +4336,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0): + eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0)(typescript@6.0.3) eslint: 10.5.0 eslint-import-resolver-node: 0.3.10 transitivePeerDependencies: @@ -4339,7 +4352,7 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -4350,7 +4363,7 @@ snapshots: doctrine: 2.1.0 eslint: 10.5.0 eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0) + eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.5.0) hasown: 2.0.4 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -4362,7 +4375,7 @@ snapshots: string.prototype.trimend: 1.0.10 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0)(typescript@6.0.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -4875,7 +4888,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.8.4 + semver: 7.8.5 jwa@2.0.1: dependencies: @@ -5000,7 +5013,7 @@ snapshots: node-abi@3.92.0: dependencies: - semver: 7.8.4 + semver: 7.8.5 optional: true node-addon-api@4.3.0: @@ -5024,7 +5037,7 @@ snapshots: normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.8.4 + semver: 7.8.5 validate-npm-package-license: 3.0.4 npm-normalize-package-bin@6.0.0: {} @@ -5345,7 +5358,7 @@ snapshots: semver@6.3.1: {} - semver@7.8.4: {} + semver@7.8.5: {} set-function-length@1.2.2: dependencies: @@ -5625,12 +5638,12 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.8 - typescript-eslint@8.61.0(eslint@10.5.0)(typescript@6.0.3): + typescript-eslint@8.61.1(eslint@10.5.0)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/parser': 8.61.0(eslint@10.5.0)(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.0(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.61.1(eslint@10.5.0)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.61.1(eslint@10.5.0)(typescript@6.0.3) eslint: 10.5.0 typescript: 6.0.3 transitivePeerDependencies: @@ -5651,7 +5664,7 @@ snapshots: undici-types@7.18.2: {} - undici@7.27.2: {} + undici@7.28.0: {} unicorn-magic@0.1.0: {} diff --git a/server/Cargo.lock b/server/Cargo.lock index 90eeba97..c8724fef 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -44,9 +44,9 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.12.1" +version = "3.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93acb4a42f64936f9b8cae4a433b237599dd6eb6ed06124eb67132ef8cc90662" +checksum = "48e2faa3e7418ed780cca54829d32782a4008a077230f67457caa063415e99c2" dependencies = [ "actix-codec", "actix-rt", @@ -60,7 +60,7 @@ dependencies = [ "derive_more", "encoding_rs", "flate2", - "foldhash", + "foldhash 0.2.0", "futures-core", "h2", "http 0.2.12", @@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.13.0" +version = "4.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf" +checksum = "df09e2d9239703dd64056359c920c7f3fba6535ec61a0059e0f44e095ffe02b4" dependencies = [ "actix-codec", "actix-http", @@ -175,7 +175,7 @@ dependencies = [ "cookie", "derive_more", "encoding_rs", - "foldhash", + "foldhash 0.2.0", "futures-core", "futures-util", "impl-more", @@ -206,7 +206,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -431,7 +431,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -501,9 +501,9 @@ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -600,9 +600,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" dependencies = [ "serde", ] @@ -627,9 +627,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.64" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "jobserver", @@ -704,7 +704,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -739,7 +739,7 @@ dependencies = [ [[package]] name = "codechat-editor-server" -version = "0.1.57" +version = "0.1.58" dependencies = [ "actix-files", "actix-http", @@ -778,7 +778,7 @@ dependencies = [ "path-slash", "pest", "pest_derive", - "phf 0.13.1", + "phf 0.14.0", "predicates", "pretty_assertions", "pulldown-cmark 0.13.4", @@ -1030,7 +1030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1095,7 +1095,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.118", "unicode-xid", ] @@ -1168,7 +1168,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1349,6 +1349,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1446,7 +1452,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1517,16 +1523,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "wasip2", - "wasip3", ] [[package]] @@ -1595,7 +1599,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1893,12 +1897,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -1948,9 +1946,9 @@ dependencies = [ [[package]] name = "impl-more" -version = "0.1.9" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" +checksum = "35a84fd5aa25fae5c0f4a33d9cac2ca017fc622cbd089be2229993514990f870" [[package]] name = "indexmap" @@ -2056,7 +2054,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2075,7 +2073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2152,12 +2150,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" version = "0.2.186" @@ -2253,9 +2245,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" dependencies = [ "serde_core", ] @@ -2415,9 +2407,9 @@ dependencies = [ [[package]] name = "minreq" -version = "2.14.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05015102dad0f7d61691ca347e9d9d9006685a64aefb3d79eecf62665de2153d" +checksum = "659579df697b372ef9e36f02fcbb41f6d6f157dcec7db9c9618fa0f23cf0fc20" [[package]] name = "mio" @@ -2640,9 +2632,9 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "oxc-browserslist" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209091efbb07de356beae15cf5fd8bd2b3ab94a17f19cc19a7f8b094aec2559c" +checksum = "2b87743c2f6963036971ca587539cd9235693d953dc018a734014241afb20d5b" dependencies = [ "flate2", "postcard", @@ -2674,7 +2666,7 @@ checksum = "b237422b014f8f8fff75bb9379e697d13f8d57551a22c88bebb39f073c1bf696" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2716,7 +2708,7 @@ dependencies = [ "phf 0.13.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3112,7 +3104,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3146,6 +3138,17 @@ dependencies = [ "serde", ] +[[package]] +name = "phf" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6" +dependencies = [ + "phf_macros 0.14.0", + "phf_shared 0.14.0", + "serde", +] + [[package]] name = "phf_codegen" version = "0.11.3" @@ -3186,6 +3189,16 @@ dependencies = [ "phf_shared 0.13.1", ] +[[package]] +name = "phf_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100" +dependencies = [ + "fastrand", + "phf_shared 0.14.0", +] + [[package]] name = "phf_macros" version = "0.11.3" @@ -3196,7 +3209,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3209,7 +3222,20 @@ dependencies = [ "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", +] + +[[package]] +name = "phf_macros" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085" +dependencies = [ + "phf_generator 0.14.0", + "phf_shared 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.118", ] [[package]] @@ -3230,6 +3256,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -3354,16 +3389,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -3424,9 +3449,9 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", @@ -3444,9 +3469,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" dependencies = [ "aws-lc-rs", "bytes", @@ -3480,9 +3505,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -3531,7 +3556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", - "getrandom 0.4.2", + "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -3965,7 +3990,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3990,7 +4015,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4138,9 +4163,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smawk" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" [[package]] name = "socket2" @@ -4244,9 +4269,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -4270,7 +4295,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4389,7 +4414,7 @@ checksum = "5cf0ffc3ba4368e99597bd6afd83f4ff6febad66d9ae541ab46e697d32285fc0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4409,7 +4434,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4433,9 +4458,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.49" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" dependencies = [ "deranged", "num-conv", @@ -4453,9 +4478,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.29" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" dependencies = [ "num-conv", "time-core", @@ -4511,7 +4536,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4653,7 +4678,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4719,7 +4744,7 @@ checksum = "38d90eea51bc7988ef9e674bf80a85ba6804739e535e9cab48e4bb34a8b652aa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "termcolor", ] @@ -4975,16 +5000,7 @@ version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] @@ -5038,7 +5054,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] @@ -5051,28 +5067,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.5.0" @@ -5086,18 +5080,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" version = "0.3.102" @@ -5120,9 +5102,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" dependencies = [ "phf 0.13.1", "phf_codegen 0.13.1", @@ -5148,9 +5130,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" dependencies = [ "rustls-pki-types", ] @@ -5252,7 +5234,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5263,7 +5245,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5465,100 +5447,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "writeable" version = "0.6.3" @@ -5619,7 +5513,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] @@ -5640,7 +5534,7 @@ checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5660,7 +5554,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] @@ -5700,7 +5594,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5719,9 +5613,9 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" +checksum = "977347db8caa080403f6b6b7c1cda9479a8e869316f7e13a59b19076a40f94e3" [[package]] name = "zmij" diff --git a/server/Cargo.toml b/server/Cargo.toml index 363cd8f3..aa7879e6 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -32,7 +32,7 @@ license = "GPL-3.0-only" name = "codechat-editor-server" readme = "../README.md" repository = "https://github.com/bjones1/CodeChat_Editor" -version = "0.1.57" +version = "0.1.58" # This library allows other packages to use core CodeChat Editor features. [lib] @@ -71,13 +71,13 @@ markup5ever_rcdom = "0.39" mime = "0.3.17" mime_guess = "2.0.5" minify-html = { git = "https://github.com/bjones1/minify-html.git", branch = "dev", version = "0.18.1" } -minreq = "2.12.0" +minreq = "3" normalize-line-endings = "0.3.0" notify-debouncer-full = "0.7" path-slash = "0.2.1" pest = "2.7.14" pest_derive = "2.7.14" -phf = "0.13.1" +phf = { version = "0.14", features = ["macros"] } # Per the [docs](https://docs.rs/crate/pulldown-cmark/latest), skip building the # binary. pulldown-cmark = { version = "0.13", default-features = false, features = [ diff --git a/server/run_until_fail.ps1 b/server/run_until_fail.ps1 index 1605c568..1b71d99c 100644 --- a/server/run_until_fail.ps1 +++ b/server/run_until_fail.ps1 @@ -14,4 +14,9 @@ while ($true) { Write-Host "Test overall_2 failed on iteration $iteration. Exiting." exit $LASTEXITCODE } + cargo test --test overall_3 + if ($LASTEXITCODE -ne 0) { + Write-Host "Test overall_3 failed on iteration $iteration. Exiting." + exit $LASTEXITCODE + } } diff --git a/server/src/processing.rs b/server/src/processing.rs index a58115e6..150c0026 100644 --- a/server/src/processing.rs +++ b/server/src/processing.rs @@ -1249,10 +1249,14 @@ fn replace_math_node(child: &Rc, is_hydrate: bool) -> Option> { attrs: ref_child_attrs, .. } = &child.data && let child_attrs = ref_child_attrs.borrow() - && child_attrs.len() == 1 - && let Some(attr) = child_attrs.iter().next() - && *attr.name.local == *"class" - && let attr_value = &attr.value + && let child_attrs_len = child_attrs.len() + && child_attrs_len >= 1 + // Look up the `class` attribute by name, so it may appear in any + // order relative to other attributes. + && let Some(class_attr) = child_attrs + .iter() + .find(|attr| *attr.name.local == *"class") + && let attr_value = &class_attr.value // with only one Text child && let text_children = &child.children.borrow() && text_children.len() == 1 @@ -1266,16 +1270,33 @@ fn replace_math_node(child: &Rc, is_hydrate: bool) -> Option> { // this span. let attr_value_str: &str = attr_value; let delim = if is_hydrate { - match attr_value_str { - "math math-inline" => Some(("\\(", "\\)", "math math-inline mceNonEditable")), - "math math-display" => Some(("$$", "$$", "math math-display mceNonEditable")), - _ => None, + // When hydrating, there should only be a `class` attribute. + if child_attrs_len == 1 { + match attr_value_str { + "math math-inline" => Some(("\\(", "\\)", "math math-inline mceNonEditable")), + "math math-display" => Some(("$$", "$$", "math math-display mceNonEditable")), + _ => None, + } + } else { + None } } else { - match attr_value_str { - "math math-inline mceNonEditable" => Some(("\\(", "\\)", "math math-inline")), - "math math-display mceNonEditable" => Some(("$$", "$$", "math math-display")), - _ => None, + // When dehydrating, there should also be a `contenteditable=false` + // attribute. It may appear in either order relative to `class`, so + // look it up by name. + if child_attrs_len == 2 + && let Some(contenteditable_attr) = child_attrs + .iter() + .find(|attr| *attr.name.local == *"contenteditable") + && contenteditable_attr.value == *"false" + { + match attr_value_str { + "math math-inline mceNonEditable" => Some(("\\(", "\\)", "math math-inline")), + "math math-display mceNonEditable" => Some(("$$", "$$", "math math-display")), + _ => None, + } + } else { + None } }; @@ -1286,7 +1307,7 @@ fn replace_math_node(child: &Rc, is_hydrate: bool) -> Option> { let delimited_text_str = if is_hydrate { format!("{}{}{}", delim.0, contents_str, delim.1) } else { - // Only apply the dehydration is the delimiters are correct. + // Only apply the dehydration if the delimiters are correct. if !contents_str.starts_with(delim.0) || !contents_str.ends_with(delim.1) { return None; } @@ -1297,12 +1318,23 @@ fn replace_math_node(child: &Rc, is_hydrate: bool) -> Option> { let delimited_text_node = Node::new(NodeData::Text { contents: RefCell::new(delimited_text_str.into()), }); + let mut attrs_vec = vec![Attribute { + name: QualName::new(None, Namespace::from(""), LocalName::from("class")), + value: delim.2.into(), + }]; + if is_hydrate { + attrs_vec.push(Attribute { + name: QualName::new( + None, + Namespace::from(""), + LocalName::from("contenteditable"), + ), + value: "false".into(), + }); + } let span = Node::new(NodeData::Element { name: QualName::new(None, Namespace::from(""), LocalName::from("span")), - attrs: RefCell::new(vec![Attribute { - name: QualName::new(None, Namespace::from(""), LocalName::from("class")), - value: delim.2.into(), - }]), + attrs: RefCell::new(attrs_vec), template_contents: RefCell::new(None), mathml_annotation_xml_integration_point: false, }); @@ -1506,14 +1538,40 @@ fn dehydrating_walk_node(node: &Rc) { Some(children[index].clone()) } }; - // `borrow_mut` is now dropped; safe to recurse. + // `borrow_mut` is now dropped; safe to recurse. Recurse first so that + // TinyMCE attributes on descendants (e.g. `data-mce-bogus` on a `
`) + // are removed before the `


` check below. if let Some(child) = child_to_walk { dehydrating_walk_node(&child); + // If this child is `


`, change it to `

 

`. An + // empty paragraph is created by TinyMCE as `


`; the `
` + // alone is dropped by HTML-to-Markdown conversion, so replace it + // with a non-breaking space to preserve the empty paragraph. + if is_empty_p_with_br(&child) { + let nbsp = Node::new(NodeData::Text { + contents: RefCell::new("\u{a0}".into()), + }); + nbsp.parent.set(Some(Rc::downgrade(&child))); + *child.children.borrow_mut() = vec![nbsp]; + } } index += 1; } } +/// Returns true if `node` is `


`: a `

` element with no attributes +/// whose only child is a `
` element with no attributes. +fn is_empty_p_with_br(node: &Rc) -> bool { + get_node_tag_name(node) == Some("p") + && matches!(&node.data, NodeData::Element { attrs, .. } if attrs.borrow().is_empty()) + // ...with exactly one child, a `
` element with no attributes. + && node.children.borrow().len() == 1 + && node.children.borrow().first().is_some_and(|br| { + get_node_tag_name(br) == Some("br") + && matches!(&br.data, NodeData::Element { attrs, .. } if attrs.borrow().is_empty()) + }) +} + fn get_node_tag_name(node: &Rc) -> Option<&str> { match &node.data { NodeData::Document => Some("html"), diff --git a/server/src/processing/tests.rs b/server/src/processing/tests.rs index ddb298d2..dd1992c1 100644 --- a/server/src/processing/tests.rs +++ b/server/src/processing/tests.rs @@ -1338,12 +1338,12 @@ fn test_hydrate_html_1() { .unwrap(), indoc!( r#" -

\({a}_1, b_{2}\) - \(a*1, b*2\) - \([a](b)\) - \(3 <a> b\) - \(a \; b\)

-

$${a}_1, b_{2}, a*1, b*2, [a](b), 3 <a> b, a \; b$$

+

\({a}_1, b_{2}\) + \(a*1, b*2\) + \([a](b)\) + \(3 <a> b\) + \(a \; b\)

+

$${a}_1, b_{2}, a*1, b*2, [a](b), 3 <a> b, a \; b$$

"# ) ); @@ -1424,12 +1424,12 @@ fn test_dehydrate_html_1() { .convert( &dehydrate_html(indoc!( r#" -

\({a}_1, b_{2}\) - \(a*1, b*2\) - \([a](b)\) - \(3 <a> b\) - \(a \; b\)

-

$${a}_1, b_{2}, a*1, b*2, [a](b), 3 <a> b, a \; b$$

+

\({a}_1, b_{2}\) + \(a*1, b*2\) + \([a](b)\) + \(3 <a> b\) + \(a \; b\)

+

$${a}_1, b_{2}, a*1, b*2, [a](b), 3 <a> b, a \; b$$

"# )) .unwrap() @@ -1501,4 +1501,26 @@ fn test_dehydrate_html_1() { "# ) ); + + // A trailing empty paragraph (`


`) is converted to `

 

` + // by `dehydrating_walk_node`, preserving it as a non-breaking space. + assert_eq!( + converter + .convert( + &dehydrate_html(indoc!( + " +

1


+ " + )) + .unwrap() + ) + .unwrap(), + indoc!( + " + 1 + + \u{a0} + " + ) + ); } diff --git a/server/src/webserver.rs b/server/src/webserver.rs index 59df0f54..3034d583 100644 --- a/server/src/webserver.rs +++ b/server/src/webserver.rs @@ -53,7 +53,7 @@ use bytes::Bytes; use dunce::simplified; use futures_util::StreamExt; use htmlize::{escape_attribute, escape_text}; -use indoc::{concatdoc, formatdoc}; +use indoc::formatdoc; use lazy_static::lazy_static; use log::{LevelFilter, error, info, warn}; use log4rs::{self, config::load_config_file}; @@ -473,42 +473,6 @@ pub const INITIAL_IDE_MESSAGE_ID: f64 = INITIAL_CLIENT_MESSAGE_ID + 1.0; /// assuming an average of 1 message/second.) pub const MESSAGE_ID_INCREMENT: f64 = 3.0; -const MATHJAX_TAGS: &str = concatdoc!( - r#" - "#, - // Per the - // [MathJax docs](https://docs.mathjax.org/en/latest/web/components/combined.html#tex-chtml), - // enable tex input and HTML output. - r#" - "# -); - lazy_static! { pub static ref ROOT_PATH: Arc> = Arc::new(Mutex::new(PathBuf::new())); @@ -987,7 +951,6 @@ pub async fn file_to_response( {name} - The CodeChat Editor - {MATHJAX_TAGS} @@ -1030,12 +993,12 @@ pub async fn file_to_response( {name} - The CodeChat Editor - {MATHJAX_TAGS} {sidebar_css} +

Fatal error

{sidebar_iframe}
diff --git a/server/tests/fixtures/overall_3/test_7/test.py b/server/tests/fixtures/overall_3/test_7/test.py new file mode 100644 index 00000000..23103ab8 --- /dev/null +++ b/server/tests/fixtures/overall_3/test_7/test.py @@ -0,0 +1,3 @@ +# The contents of this file don't matter -- tests will supply the content, +# instead of loading it from disk. However, it does need to exist for +# `canonicalize` to find the correct path to this file. diff --git a/server/tests/fixtures/overall_3/test_8/test.py b/server/tests/fixtures/overall_3/test_8/test.py new file mode 100644 index 00000000..23103ab8 --- /dev/null +++ b/server/tests/fixtures/overall_3/test_8/test.py @@ -0,0 +1,3 @@ +# The contents of this file don't matter -- tests will supply the content, +# instead of loading it from disk. However, it does need to exist for +# `canonicalize` to find the correct path to this file. diff --git a/server/tests/overall_1.rs b/server/tests/overall_1.rs index 9695679f..601d5f17 100644 --- a/server/tests/overall_1.rs +++ b/server/tests/overall_1.rs @@ -27,7 +27,7 @@ mod overall_common; // ------- // // ### Standard library -use std::{error::Error, path::PathBuf, time::Duration}; +use std::{path::PathBuf, time::Duration}; // ### Third-party use dunce::canonicalize; @@ -137,7 +137,7 @@ async fn test_server_core( EditorMessageContents::Update(UpdateMessageContents { file_path: path_str.clone(), cursor_position: Some(CursorPosition::Line(1)), - scroll_position: None, + scroll_position: Some(1.0), is_re_translation: false, contents: None, }), @@ -237,10 +237,7 @@ async fn test_server_core( client_id += MESSAGE_ID_INCREMENT; // Moving left should move us back to the doc block. - code_line - .send_keys("" + Key::Home + Key::Left) - .await - .unwrap(); + code_line.send_keys(Key::Home + Key::Left).await.unwrap(); assert_eq!( codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), EditorMessage { @@ -267,7 +264,7 @@ async fn test_server_core( EditorMessageContents::Update(UpdateMessageContents { file_path: path_str.clone(), cursor_position: Some(CursorPosition::Line(1)), - scroll_position: None, + scroll_position: Some(1.0), is_re_translation: false, contents: None, }), @@ -789,7 +786,7 @@ async fn test_client_updates_core( let doc_block_contents = driver.find(By::Css(contents_css)).await.unwrap(); doc_block_contents - .send_keys("" + Key::End + " testing") + .send_keys(Key::End + " testing") .await .unwrap(); @@ -815,7 +812,6 @@ async fn test_client_updates_core( } ); client_id += MESSAGE_ID_INCREMENT; - assert_eq!(client_id, 7.0); msg = codechat_server.get_message_timeout(TIMEOUT).await.unwrap(); } @@ -850,7 +846,6 @@ async fn test_client_updates_core( ); codechat_server.send_result(client_id, None).await.unwrap(); client_id += MESSAGE_ID_INCREMENT; - assert!(client_id == 10.0 || client_id == 7.0); // The Server sends the Client a wrapped version of the text; the Client // replies with a Result(Ok). @@ -880,7 +875,7 @@ async fn test_client_updates_core( EditorMessageContents::Update(UpdateMessageContents { file_path: path_str.clone(), cursor_position: Some(CursorPosition::Line(1)), - scroll_position: None, + scroll_position: Some(1.0), is_re_translation: false, contents: None, }), @@ -944,7 +939,8 @@ async fn test_client_updates_core( codechat_server.send_result(client_id, None).await.unwrap(); client_id += MESSAGE_ID_INCREMENT; - // Send the original text back, to ensure the re-translation correctly updated the Client. + // Send the original text back, to ensure the re-translation correctly + // updated the Client. let ide_version = 1.0; let ide_id = codechat_server .send_message_update_plain( @@ -972,7 +968,7 @@ async fn test_client_updates_core( EditorMessageContents::Update(UpdateMessageContents { file_path: path_str.clone(), cursor_position: Some(CursorPosition::Line(1)), - scroll_position: None, + scroll_position: Some(1.0), is_re_translation: false, contents: None, }), diff --git a/server/tests/overall_2.rs b/server/tests/overall_2.rs index b48f3c38..810481b9 100644 --- a/server/tests/overall_2.rs +++ b/server/tests/overall_2.rs @@ -27,7 +27,7 @@ mod overall_common; // ------- // // ### Standard library -use std::{error::Error, path::PathBuf}; +use std::path::PathBuf; // ### Third-party use dunce::canonicalize; @@ -37,8 +37,8 @@ use thirtyfour::{By, Key, WebDriver, error::WebDriverError}; // ### Local use crate::overall_common::{ - TIMEOUT, assert_no_more_messages, get_version, optional_message, perform_loadfile, - select_codechat_iframe, + TIMEOUT, assert_no_more_messages, click_element_top_left, get_version, optional_message, + perform_loadfile, select_codechat_iframe, }; use code_chat_editor::{ ide::CodeChatEditorServer, @@ -151,7 +151,8 @@ async fn test_4_core( make_test!(test_5, test_5_core); -// Verify that newlines in Mermaid and Graphviz diagrams aren't removed. +// Verify that newlines in Mermaid and Graphviz diagrams aren't removed, and +// that equations aren't munged. async fn test_5_core( codechat_server: CodeChatEditorServer, driver: WebDriver, @@ -174,6 +175,8 @@ async fn test_5_core( # graph TD # A --> B # ``` + # + # $x$ " ) .to_string(); @@ -191,8 +194,7 @@ async fn test_5_core( select_codechat_iframe(&driver).await; // Focus it. - let contents_css = ".CodeChat-CodeMirror .CodeChat-doc-contents"; - let doc_block_contents = driver.find(By::Css(contents_css)).await.unwrap(); + let doc_block_contents = driver.find(By::Css(".CodeChat-doc")).await.unwrap(); doc_block_contents.click().await.unwrap(); // The click produces an updated cursor/scroll location after an autosave // delay. @@ -229,7 +231,7 @@ async fn test_5_core( EditorMessageContents::Update(UpdateMessageContents { file_path: path_str.clone(), cursor_position: Some(CursorPosition::Line(1)), - scroll_position: None, + scroll_position: Some(1.0), is_re_translation: false, contents: None, }), @@ -266,7 +268,6 @@ async fn test_5_core( let version = client_version; codechat_server.send_result(client_id, None).await.unwrap(); client_id += MESSAGE_ID_INCREMENT; - assert_eq!(client_id, 10.0); // Send new text, which turns into a diff. let ide_id = codechat_server @@ -293,7 +294,7 @@ async fn test_5_core( EditorMessageContents::Update(UpdateMessageContents { file_path: path_str.clone(), cursor_position: Some(CursorPosition::Line(1)), - scroll_position: None, + scroll_position: Some(1.0), is_re_translation: false, contents: None, }), @@ -371,30 +372,17 @@ async fn test_6_core( // Check the content. let body_css = "#CodeChat-body .CodeChat-doc-contents"; let body_content = driver.find(By::Css(body_css)).await.unwrap(); - body_content.click().await.unwrap(); - let body_content = driver.find(By::Css(body_css)).await.unwrap(); + click_element_top_left(&driver, &body_content) + .await + .unwrap(); let mut client_id = INITIAL_CLIENT_MESSAGE_ID; - // Sometimes, the cursor starts at the beginning of the doc block before it - // moves to the end. - let msg = optional_message( - &codechat_server, - &mut client_id, - EditorMessageContents::Update(UpdateMessageContents { - file_path: path_str.clone(), - cursor_position: Some(CursorPosition::Line(1)), - scroll_position: None, - is_re_translation: false, - contents: None, - }), - ) - .await; assert_eq!( - msg, + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), EditorMessage { id: client_id, message: EditorMessageContents::Update(UpdateMessageContents { file_path: path_str.clone(), - cursor_position: Some(CursorPosition::Line(3)), + cursor_position: Some(CursorPosition::Line(1)), scroll_position: None, is_re_translation: false, contents: None, @@ -444,7 +432,7 @@ async fn test_6_core( &mut client_id, EditorMessageContents::Update(UpdateMessageContents { file_path: path_str.clone(), - cursor_position: Some(CursorPosition::Line(3)), + cursor_position: Some(CursorPosition::Line(1)), scroll_position: None, is_re_translation: false, contents: None, diff --git a/server/tests/overall_3.rs b/server/tests/overall_3.rs new file mode 100644 index 00000000..7b9dfdd2 --- /dev/null +++ b/server/tests/overall_3.rs @@ -0,0 +1,471 @@ +// Copyright (C) 2025 Bryan A. Jones. +// +// This file is part of the CodeChat Editor. The CodeChat Editor is free +// software: you can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, either +// version 3 of the License, or (at your option) any later version. +// +// The CodeChat Editor is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// the CodeChat Editor. If not, see +// [http://www.gnu.org/licenses](http://www.gnu.org/licenses). +/// `overall_3.rs` - test the overall system +/// ======================================== +/// +/// These are functional tests of the overall system, performed by attaching a +/// testing IDE to generate commands then observe results, along with a browser +/// tester. +// Modules +// ------- +mod overall_common; + +// Imports +// ------- +// +// ### Standard library +use std::path::PathBuf; + +// ### Third-party +use dunce::canonicalize; +use indoc::indoc; +use pretty_assertions::assert_eq; +use thirtyfour::{By, Key, WebDriver, error::WebDriverError}; + +// ### Local +use crate::overall_common::{ + TIMEOUT, assert_no_more_messages, click_element_top_left, get_version, optional_message, + perform_loadfile, select_codechat_iframe, +}; +use code_chat_editor::{ + ide::CodeChatEditorServer, + processing::{ + CodeChatForWeb, CodeMirrorDiff, CodeMirrorDiffable, SourceFileMetadata, StringDiff, + }, + webserver::{ + CursorPosition, EditorMessage, EditorMessageContents, INITIAL_CLIENT_MESSAGE_ID, + MESSAGE_ID_INCREMENT, ResultOkTypes, UpdateMessageContents, + }, +}; +use test_utils::prep_test_dir; + +// Tests +// ----- +make_test!(test_7, test_7_core); + +// Test that Client to IDE cursor sync in doc blocks works. +async fn test_7_core( + codechat_server: CodeChatEditorServer, + driver: WebDriver, + test_dir: PathBuf, +) -> Result<(), WebDriverError> { + let path = canonicalize(test_dir.join("test.py")).unwrap(); + let path_str = path.to_str().unwrap().to_string(); + let ide_version = 0.0; + perform_loadfile( + &codechat_server, + &test_dir, + "test.py", + Some(( + indoc!( + " + # 1
+ # 2 + # + # 4 + # + # 6 + " + ) + .to_string(), + ide_version, + )), + false, + 6.0, + ) + .await; + + // Target the iframe containing the Client. + select_codechat_iframe(&driver).await; + + // Focus the doc block. It should produce an update with only cursor/scroll + // info (no contents). + let mut client_id = INITIAL_CLIENT_MESSAGE_ID; + let doc_block = driver.find(By::Css(".CodeChat-doc")).await.unwrap(); + click_element_top_left(&driver, &doc_block).await.unwrap(); + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }) + } + ); + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + + // Refind it, since it's now switched with a TinyMCE editor. + let tinymce_contents = driver.find(By::Id("TinyMCE-inst")).await.unwrap(); + + // Move to the next lines. + for expected_line in [2, 4, 6] { + tinymce_contents.send_keys(Key::Down).await.unwrap(); + + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(expected_line)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }) + } + ); + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + } + + assert_no_more_messages(&codechat_server).await; + + Ok(()) +} + +make_test!(test_8, test_8_core); + +// Test that Clients can insert a new paragraph. +async fn test_8_core( + codechat_server: CodeChatEditorServer, + driver: WebDriver, + test_dir: PathBuf, +) -> Result<(), WebDriverError> { + let path = canonicalize(test_dir.join("test.py")).unwrap(); + let path_str = path.to_str().unwrap().to_string(); + let ide_version = 0.0; + let mut server_id = perform_loadfile( + &codechat_server, + &test_dir, + "test.py", + Some(( + indoc!( + " + # 2 + # + # 4 + " + ) + .to_string(), + ide_version, + )), + false, + 6.0, + ) + .await; + + // Target the iframe containing the Client. + select_codechat_iframe(&driver).await; + + // Focus the doc block. It should produce an update with only cursor/scroll + // info (no contents). + let mut client_id = INITIAL_CLIENT_MESSAGE_ID; + let doc_block = driver.find(By::Css(".CodeChat-doc")).await.unwrap(); + click_element_top_left(&driver, &doc_block).await.unwrap(); + + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }) + } + ); + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + + // Refind it, since it's now switched with a TinyMCE editor. + let tinymce_contents = driver.find(By::Id("TinyMCE-inst")).await.unwrap(); + + // Move to the beginning of this line. Due to MacOS fun, avoid option+left + // arrow. TODO: the cursor movement doesn't seem to change the actual + // insertion point. Not sure why. + tinymce_contents + .send_keys(Key::Left + Key::Left) + .await + .unwrap(); + + // Uncomment for debug. + //use std::time::Duration; + //use tokio::time::sleep; + //sleep(Duration::from_hours(1)).await; + + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }) + } + ); + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + + // Start a new paragraph. Wait for a re-translation as the line changes. + tinymce_contents.send_keys(Key::Enter).await.unwrap(); + + let msg = optional_message( + &codechat_server, + &mut client_id, + EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }), + ) + .await; + let mut version = 0.0; + let client_version = get_version(&msg); + assert_eq!( + msg, + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: Some(CodeChatForWeb { + metadata: SourceFileMetadata { + mode: "python".to_string(), + }, + source: CodeMirrorDiffable::Diff(CodeMirrorDiff { + doc: vec![StringDiff { + from: 10, + to: None, + insert: "#\n# \u{a0}\n".to_string(), + },], + doc_blocks: vec![], + version, + }), + version: client_version, + }), + }) + } + ); + version = client_version; + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + + // There's a re-translation sent to the client, whose response comes back to + // the IDE. + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: server_id, + message: EditorMessageContents::Result(Ok(ResultOkTypes::Void)) + } + ); + server_id += MESSAGE_ID_INCREMENT; + + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }) + } + ); + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + + // ### Insert a newline between two existing paragraphs + // + // After the previous edit, the doc block contains three paragraphs. Move up + // to the first paragraph (producing a cursor-only update), then start a new + // paragraph between the first and second ones. Wait for a re-translation as + // the lines change. + tinymce_contents.send_keys(Key::Up + Key::Up).await.unwrap(); + tinymce_contents.send_keys(Key::Enter).await.unwrap(); + + // The cursor move produces an optional cursor-only update before the + // re-translation arrives. + let msg = optional_message( + &codechat_server, + &mut client_id, + EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }), + ) + .await; + let client_version = get_version(&msg); + assert_eq!( + msg, + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(3)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: Some(CodeChatForWeb { + metadata: SourceFileMetadata { + mode: "python".to_string(), + }, + source: CodeMirrorDiffable::Diff(CodeMirrorDiff { + doc: vec![StringDiff { + from: 0, + to: None, + insert: "# \u{a0}\n#\n".to_string(), + },], + doc_blocks: vec![], + version, + }), + version: client_version, + }), + }) + } + ); + version = client_version; + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + + // There's a re-translation sent to the client, whose response comes back to + // the IDE. + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: server_id, + message: EditorMessageContents::Result(Ok(ResultOkTypes::Void)) + } + ); + server_id += MESSAGE_ID_INCREMENT; + + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(3)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }) + } + ); + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + + // ### Insert a newline at the end of the document + // + // Move to the end of the last paragraph, then start a new paragraph there. + // Wait for a re-translation as the lines change. + tinymce_contents + .send_keys(Key::Down + Key::Down + Key::Down + Key::Down + Key::Down + Key::End) + .await + .unwrap(); + tinymce_contents.send_keys(Key::Enter).await.unwrap(); + + let msg = optional_message( + &codechat_server, + &mut client_id, + EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }), + ) + .await; + let client_version = get_version(&msg); + assert_eq!( + msg, + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: Some(CodeChatForWeb { + metadata: SourceFileMetadata { + mode: "python".to_string(), + }, + source: CodeMirrorDiffable::Diff(CodeMirrorDiff { + doc: vec![StringDiff { + from: 22, + to: None, + insert: "#\n# \u{a0}\n".to_string(), + },], + doc_blocks: vec![], + version, + }), + version: client_version, + }), + }) + } + ); + codechat_server.send_result(client_id, None).await.unwrap(); + client_id += MESSAGE_ID_INCREMENT; + + // There's a re-translation sent to the client, whose response comes back to + // the IDE. + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: server_id, + message: EditorMessageContents::Result(Ok(ResultOkTypes::Void)) + } + ); + + assert_eq!( + codechat_server.get_message_timeout(TIMEOUT).await.unwrap(), + EditorMessage { + id: client_id, + message: EditorMessageContents::Update(UpdateMessageContents { + file_path: path_str.clone(), + cursor_position: Some(CursorPosition::Line(1)), + scroll_position: Some(1.0), + is_re_translation: false, + contents: None, + }) + } + ); + codechat_server.send_result(client_id, None).await.unwrap(); + //client_id += MESSAGE_ID_INCREMENT; + + assert_no_more_messages(&codechat_server).await; + + Ok(()) +} diff --git a/server/tests/overall_common/mod.rs b/server/tests/overall_common/mod.rs index df15255e..4cdafdbe 100644 --- a/server/tests/overall_common/mod.rs +++ b/server/tests/overall_common/mod.rs @@ -250,7 +250,7 @@ macro_rules! make_test { ($test_name: ident, $test_core_name: ident) => { #[tokio::test] #[tracing::instrument] - async fn $test_name() -> Result<(), Box> { + async fn $test_name() -> Result<(), Box> { $crate::overall_common::harness($test_core_name, prep_test_dir!()).await } }; @@ -299,10 +299,12 @@ pub async fn goto_line( .await .unwrap(); // The cursor movement produces a cursor/scroll position update after an - // autosave delay. Sometimes, we get an update just before the movement; - // ignore that. + // autosave delay. Sometimes, we get an update or two just before the + // movement; ignore those (up to 2 of them). let mut msg = codechat_server.get_message_timeout(TIMEOUT).await.unwrap(); - if msg.id == *client_id + let mut ignored = 0; + while ignored < 2 + && msg.id == *client_id && let EditorMessageContents::Update(update) = &msg.message && update.file_path == path_str && update.cursor_position != Some(CursorPosition::Line(line)) @@ -314,6 +316,7 @@ pub async fn goto_line( codechat_server.send_result(*client_id, None).await.unwrap(); *client_id += MESSAGE_ID_INCREMENT; msg = codechat_server.get_message_timeout(TIMEOUT).await.unwrap(); + ignored += 1; } assert_eq!( msg, @@ -412,6 +415,32 @@ pub async fn perform_loadfile( } } +/// Click near the top-left corner of `element`. By default, `click()` selects +/// the middle of an element; we want to start at the first line, so use an +/// action chain to offset from the middle (the origin used by +/// `move_to_element_with_offset`) toward the top left. +/// +/// Note that the offset must be computed from the element's `width`/`height`. A +/// few pixels of inset is also added so the click lands just inside the element +/// rather than on its border or in any surrounding padding. +#[allow(dead_code)] +pub async fn click_element_top_left( + driver_ref: &WebDriver, + element: &WebElement, +) -> Result<(), WebDriverError> { + let element_size = element.rect().await?; + driver_ref + .action_chain() + .move_to_element_with_offset( + element, + (-element_size.width / 2.0 + 8.0) as i64, + (-element_size.height / 2.0 + 8.0) as i64, + ) + .click() + .perform() + .await +} + #[allow(deprecated)] pub async fn select_codechat_iframe(driver_ref: &WebDriver) -> WebElement { // Target the iframe containing the Client. @@ -433,6 +462,7 @@ pub async fn assert_no_more_messages(codechat_server: &CodeChatEditorServer) { /// Wait for a message. If it matches the provided optional message, acknowledge /// it and update the client ID, then wait for another message. Return the most /// recently received message. +#[allow(dead_code)] #[tracing::instrument(skip_all)] pub async fn optional_message( codechat_server: &CodeChatEditorServer,