diff --git a/.github/workflows/ci-ui-test.yml b/.github/workflows/ci-ui-test.yml index 434aa1015..cf07d71c9 100644 --- a/.github/workflows/ci-ui-test.yml +++ b/.github/workflows/ci-ui-test.yml @@ -16,7 +16,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: "20.x" + node-version: "22.x" cache: npm - name: Install dependencies diff --git a/package-lock.json b/package-lock.json index b3cc1da5f..f1729ff41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,10 @@ "src/ui", "tests/e2e" ], + "dependencies": { + "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@^24.2.0", + "vscode": "npm:@codingame/monaco-vscode-extension-api@^24.2.0" + }, "devDependencies": { "playwright": "^1.57.0" }, @@ -141,6 +145,427 @@ "integrity": "sha512-Ys9LkOzDtjsUY9bbwFU0SbPqpXAjHCmDGZ7N90hEZdncRomgXGUi/BUvzVNsyPk97EpH9JtlrYHcIZhtGPDaXQ==", "license": "Apache-2.0" }, + "node_modules/@codingame/monaco-vscode-api": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-api/-/monaco-vscode-api-24.3.0.tgz", + "integrity": "sha512-9FWZsr5HDh/hLQ3E1KtsfCiURzf5k5q3xWwOGojryal+qDmkGtuqkDP14Nf4OykAYki5ZuTwZ5TyxIfrI2bfqQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-base-service-override": "24.3.0", + "@codingame/monaco-vscode-environment-service-override": "24.3.0", + "@codingame/monaco-vscode-extensions-service-override": "24.3.0", + "@codingame/monaco-vscode-files-service-override": "24.3.0", + "@codingame/monaco-vscode-host-service-override": "24.3.0", + "@codingame/monaco-vscode-layout-service-override": "24.3.0", + "@codingame/monaco-vscode-quickaccess-service-override": "24.3.0", + "@vscode/iconv-lite-umd": "0.7.1", + "dompurify": "3.3.1", + "jschardet": "3.1.4", + "marked": "14.0.0" + } + }, + "node_modules/@codingame/monaco-vscode-api/node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@codingame/monaco-vscode-base-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-base-service-override/-/monaco-vscode-base-service-override-24.3.0.tgz", + "integrity": "sha512-xBPslzTyOq+wRurZhRwzhK3VMpv/0swqEKFGgC9N4qDgH57T/5EHko96ZCg9i7A6DIBNXZ+RxM0c9U2YWES0JA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-bulk-edit-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-bulk-edit-service-override/-/monaco-vscode-bulk-edit-service-override-24.3.0.tgz", + "integrity": "sha512-0NZH2vZDQC0csPQtoLDf6VUErBMEYnr4tepM1fvSq4KDLGGsMo5D0RdY3C4jG5qHk+Bm5yZtB4KwwkTjPF3oQw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-configuration-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-configuration-service-override/-/monaco-vscode-configuration-service-override-24.3.0.tgz", + "integrity": "sha512-AB5FcygqtjEOPzBRbvxrYnIKzKLAy1F2Fo/iF7wV7n4+bgCv8Qtx3IJuJR5zdkgQ6vVo0hW5FKHebcpMqBV+OQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-files-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-editor-api": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-editor-api/-/monaco-vscode-editor-api-24.3.0.tgz", + "integrity": "sha512-W+jguLUI4exsIRe8iQ3i+SNhHsCgO9NvY0zFsxOhTiMhAADK7hEehd0CLnKbfoZt8XEbr/XQ0S3wISRFlQQlOQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-editor-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-editor-service-override/-/monaco-vscode-editor-service-override-24.3.0.tgz", + "integrity": "sha512-loBoJMUqjKUv0PTMGFsINYfu01PeulUq00MKy6Gu7+Jdox3FrUNzQm25VvbWPFLLyQI6/v0VDkjgzoz39YfTQQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-environment-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-environment-service-override/-/monaco-vscode-environment-service-override-24.3.0.tgz", + "integrity": "sha512-wyfzk/ciIpFQw87TCLYe6grTRFTtK8YNJC/8vrQ40sJ5/hz3R36hr9wBFhA7V/DVnAiUhASJQeEWwJFmUzl9nw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-extension-api": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-extension-api/-/monaco-vscode-extension-api-24.3.0.tgz", + "integrity": "sha512-RykrxXwwngF7xR0dtiUhqRTKqLOnDwCKvcLvh7lLIYT1CertvzhRojoyAX/klR1z3klInLL6ycrQJ88Ykd3w+A==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-extensions-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-extensions-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-extensions-service-override/-/monaco-vscode-extensions-service-override-24.3.0.tgz", + "integrity": "sha512-kzrpvI1cOEKRZLjAuG9unmoqZGEKwTSFn7S1AIbnAXt20wdOPNCSgSYwKQ5zMHAXVmr0+AMymUnNh6bdD+HOuA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-files-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-files-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-files-service-override/-/monaco-vscode-files-service-override-24.3.0.tgz", + "integrity": "sha512-+tSghOY4HSJBdR6S/xlO4bcavzjSOSyqlzBVq8dzVbheOhA6dGs7OP9DKpb+BaKHUlDtOx2t2LBvWpMg9QA5LA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-host-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-host-service-override/-/monaco-vscode-host-service-override-24.3.0.tgz", + "integrity": "sha512-kOzAGwtkPAUAkTT02fUnkeaXspsTqoRqJFAHSBkgVN7aJk0+DMpmpQWntb1GgxlHKff0qaGz3Fla2WDmmYnWTw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-keybindings-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-keybindings-service-override/-/monaco-vscode-keybindings-service-override-24.3.0.tgz", + "integrity": "sha512-QPBn92XQCMnRw8R9WZrOSbp6y4IGT34J26mZjPO8EscehBXKfr6joxUphHFTcjOVnY8bXBuF38BcKCu/QbK/mg==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-files-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-cs": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-cs/-/monaco-vscode-language-pack-cs-24.3.0.tgz", + "integrity": "sha512-l1xSq6qKVVXVRzw1i5d99O1W7c5mW6pApIJXWnliXlFSDPcWoCeWtQTiFzbbJBZJut1bE0YmArjtrXCeXcnYyA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-de": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-de/-/monaco-vscode-language-pack-de-24.3.0.tgz", + "integrity": "sha512-ZXBK3byQyIdJu9iXjeMBhA+neMuEwd4ineQ+Gxsx1EYib/P3jC38SxUNuuzZAXWnUwBD/7pkLVVIgd25r3a84g==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-es": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-es/-/monaco-vscode-language-pack-es-24.3.0.tgz", + "integrity": "sha512-5m70g/VwIOSr0BZhpyW8JTtIdD5AZZoEjUvE8FPImMR/VlN2/p5+nxnYnXnIQQgDCOM9OWczP/jGa0B9vNBL9g==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-fr": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-fr/-/monaco-vscode-language-pack-fr-24.3.0.tgz", + "integrity": "sha512-bgFE9dxiD6Gqd2i+nUw6LudWZk25th9PtmS3WkSeSrGNm7Gtc6vXgKQkQUXx+tUiQFjqpAQg5pBRLBcnKh6UNA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-it": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-it/-/monaco-vscode-language-pack-it-24.3.0.tgz", + "integrity": "sha512-rafY2uwHXRgMHP0Rje7eubJzi2DPwTGI1JeP5GTRUCENWSjQ/O+UdglZBjqEEr2/crEBAcMZcprGMnOHd2T8AA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-ja": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-ja/-/monaco-vscode-language-pack-ja-24.3.0.tgz", + "integrity": "sha512-84kB22nV+EwiLBadPumQpg1F6QlRH2JVtuVzFPVdxx4ibp9968CUChu5SqWYpYxdLUTqBKtWs13lEczjyxoXzw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-ko": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-ko/-/monaco-vscode-language-pack-ko-24.3.0.tgz", + "integrity": "sha512-5dKA4yanQKY1d1xkdjfF8t322VRLDXJ/3Pw+5BVyVPlYFIcU8qCDo/52Bu3kf6Rby5B6VWZz1d/aabQCnlz9ZA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-pl": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-pl/-/monaco-vscode-language-pack-pl-24.3.0.tgz", + "integrity": "sha512-r70/eBKdxvBjxtmM3U+Y2u/SgMubUfw7t5ys0Tt88moJLeVrAjwghc14wp0OWoIkjhal7o5xbZzIh+ZizDRlKg==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-pt-br": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-pt-br/-/monaco-vscode-language-pack-pt-br-24.3.0.tgz", + "integrity": "sha512-A/P44wWdafWoTiVkttJslrrdugBwRZRaemykKhEznXGbL0yyRv9vkjVcpS7YdQOSjOMe4bMG+GTRJ+1gFs2aeQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-qps-ploc": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-qps-ploc/-/monaco-vscode-language-pack-qps-ploc-24.3.0.tgz", + "integrity": "sha512-KwLZqXdM+gu4HF3wWltAHdQ+jeYOhf7GkFqJ5mnEMWya3HTEMkOiG0b8xzBTPi9s9ZP+pbCaO5eNC7iwbGCLhw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-ru": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-ru/-/monaco-vscode-language-pack-ru-24.3.0.tgz", + "integrity": "sha512-A5khhV/JgHGRWcKXkLLdSWIcD+8sEnbaG97yIc4FUff9XBUVYv6D+IDmocgudiX6jCCC/AhintMnJjh3Ifc7nw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-tr": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-tr/-/monaco-vscode-language-pack-tr-24.3.0.tgz", + "integrity": "sha512-ZeZgy3BDOWDU319mj8g42YfG6L08rM1INSo0VzAS+3x+tHQXDaZC9BKjSg0nSQJapr0FNiVRIBAqFP/eYWaX2g==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-zh-hans": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-zh-hans/-/monaco-vscode-language-pack-zh-hans-24.3.0.tgz", + "integrity": "sha512-NZZJE0pOUQaii0FS+jRlxfB4tXp0aQWxdZRd1e+jLmLf6O0bex1FxE8V2QqoAM4iMa2/6ynIiN7bAP2K5965tQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-language-pack-zh-hant": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-language-pack-zh-hant/-/monaco-vscode-language-pack-zh-hant-24.3.0.tgz", + "integrity": "sha512-lAWrrymQyih/iln+rWPwx4QMt87XKE3Qz9xzmAfJR/ysXAbboJXmIRKhkWaG3ZEXdrYtO844J8IDiq/nsFGibg==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-languages-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-languages-service-override/-/monaco-vscode-languages-service-override-24.3.0.tgz", + "integrity": "sha512-yixGNCRPJwACiBCfDlcHIlzF6/XypsZhuJ0Bs7zTEt5ksq4TJIe8HAT79246me5xqhfuing5vYuIKYwCKrayGQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-files-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-layout-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-layout-service-override/-/monaco-vscode-layout-service-override-24.3.0.tgz", + "integrity": "sha512-fTapVvu7tgxcfS/Fou1ukVpPDtrLM+DtH1smDU+KZmq3hsyw7eupvetmgYREqjHuEpni+KVEHtFotj31PqJwfg==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-localization-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-localization-service-override/-/monaco-vscode-localization-service-override-24.3.0.tgz", + "integrity": "sha512-oxKdItcYZTgBTKkp0C1NkXNClQ5NZtAedxprbgxDDO814kc66kjXClOEfK978IRpPCn65pkK1RWwhbwopjeTPQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-log-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-log-service-override/-/monaco-vscode-log-service-override-24.3.0.tgz", + "integrity": "sha512-KkALZd3gjvDuvs9O+YDIxAdbwgVCIqB+UTioQ9gnhjGFXer/+I3z2KHWYG97BrRqYTaEhSqCZnXTM2RyOZK3lA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-environment-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-model-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-model-service-override/-/monaco-vscode-model-service-override-24.3.0.tgz", + "integrity": "sha512-pauLEX5zgBZxaQk0308zf0KwOEWUGfr6bGzmzSvlKIhqA2tKiyrf01mhyuzHVQ860peEPZSUFoUgiwjEqu4hnQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-monarch-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-monarch-service-override/-/monaco-vscode-monarch-service-override-24.3.0.tgz", + "integrity": "sha512-5XQI+3AJgyDfTGzj09ACO5lOE+mBAhwuthCdhX3O9v2egWGP784l6z2Y+eWFMett5dpe3+mbLTd4ZlbJq/fhaw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-quickaccess-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-quickaccess-service-override/-/monaco-vscode-quickaccess-service-override-24.3.0.tgz", + "integrity": "sha512-TbgF3gmoW+Rp0UENVUBvNMKrnIH+7nKqVdJrNaredHkF/3jul2pSZLRYoXgRfeyxQNWgzeb1jTNbPmwGLS0dvA==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-textmate-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-textmate-service-override/-/monaco-vscode-textmate-service-override-24.3.0.tgz", + "integrity": "sha512-wIi1LxjCy9JFrKeHmgYbpPg9zg9ySAiQhSPLE9dKeHjcRICQkCKcHj1jCD+McAsJYrVMC+w9FqFEBQQrDeYESQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-files-service-override": "24.3.0", + "vscode-oniguruma": "1.7.0", + "vscode-textmate": "9.2.1" + } + }, + "node_modules/@codingame/monaco-vscode-theme-defaults-default-extension": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-theme-defaults-default-extension/-/monaco-vscode-theme-defaults-default-extension-24.3.0.tgz", + "integrity": "sha512-L9zFIpBLvUEKMdr7ze0u6oodNFlKy6sHnvhBGqDisSsrII2Uq8OAhkof6bi8a9rZh/2Ytre3iV7wiWS/mVjjKw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-theme-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-theme-service-override/-/monaco-vscode-theme-service-override-24.3.0.tgz", + "integrity": "sha512-jmKWOha76dqi5xkbSwfM6zlhz9gprGolm8FfstpUBOXmBJhmlH/0SEs9Eh9bX/Iwy7g4QO9Kv29eFUDZATi5Tw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-files-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-view-banner-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-view-banner-service-override/-/monaco-vscode-view-banner-service-override-24.3.0.tgz", + "integrity": "sha512-8kWRqaghmSWGrddZAQ1u90KLriThOxL+e1w5GG/H4MlIuMdEShDzD/VK5smPkmbSRgPVZaaDRAakiYSUTkjIAw==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-view-common-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-view-common-service-override/-/monaco-vscode-view-common-service-override-24.3.0.tgz", + "integrity": "sha512-pVdqTBTajkXcB1xw+KciILfeRkKDQsEiiZV7abA1ND6awbQMcrmlDE+csEcyO6pHcPalWZw3AD9scNqWab5OQg==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-bulk-edit-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-view-status-bar-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-view-status-bar-service-override/-/monaco-vscode-view-status-bar-service-override-24.3.0.tgz", + "integrity": "sha512-fr9xOSHMjza01rT7vDpARq5iWA2gMdQwIUFZNsEytnVHOvbgNsLeLzb5cdlIw2swyA6aM4CMGnn6qm5+/eZ3lQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-view-title-bar-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-view-title-bar-service-override/-/monaco-vscode-view-title-bar-service-override-24.3.0.tgz", + "integrity": "sha512-6ldfOX516hGG/mNjqXYXpa/0eWW9XZh0aytQDKyqa/7FSfVE/yCsrH4fycZmRwOkA3sbYGP4agX9IbNi7bel0Q==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-views-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-views-service-override/-/monaco-vscode-views-service-override-24.3.0.tgz", + "integrity": "sha512-PlJGhq5BL4KG4NrQ4KdqawicdfQS7Ne3blK8mptYOZ9j7RDV71EE4La5OSYVxT0r2+vZMRCLgzKm0eYgTG2E8g==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-keybindings-service-override": "24.3.0", + "@codingame/monaco-vscode-layout-service-override": "24.3.0", + "@codingame/monaco-vscode-quickaccess-service-override": "24.3.0", + "@codingame/monaco-vscode-view-common-service-override": "24.3.0" + } + }, + "node_modules/@codingame/monaco-vscode-workbench-service-override": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-workbench-service-override/-/monaco-vscode-workbench-service-override-24.3.0.tgz", + "integrity": "sha512-azz4CUtYy5indpkSp/tzrhCST2/UYPwnYE1A32i5EwppTbyR3Y11etAqirzcZUKBBTVkIpzby9tBFqxAE8ILVg==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-keybindings-service-override": "24.3.0", + "@codingame/monaco-vscode-quickaccess-service-override": "24.3.0", + "@codingame/monaco-vscode-view-banner-service-override": "24.3.0", + "@codingame/monaco-vscode-view-common-service-override": "24.3.0", + "@codingame/monaco-vscode-view-status-bar-service-override": "24.3.0", + "@codingame/monaco-vscode-view-title-bar-service-override": "24.3.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -803,68 +1228,11 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.24", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@launchdarkly/js-client-sdk": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@launchdarkly/js-client-sdk/-/js-client-sdk-0.6.0.tgz", @@ -918,73 +1286,6 @@ "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-3.0.0.tgz", "integrity": "sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg==" }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@mapbox/point-geometry": { "version": "0.1.0", "license": "ISC" @@ -1021,6 +1322,12 @@ "monaco-editor": ">= 0.21.0 < 1" } }, + "node_modules/@monacopilot/core": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@monacopilot/core/-/core-1.2.9.tgz", + "integrity": "sha512-S6zQ1lTF7saOsENaVisovHYluSxMRQWHx6XCmlatDeRWuHknXvq/+X0Fg2ibAVqBTkJYYNMnTBrzntpE63QcnA==", + "license": "MIT" + }, "node_modules/@napi-rs/canvas": { "version": "0.1.70", "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.70.tgz", @@ -1843,6 +2150,13 @@ "@types/geojson": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/web-bluetooth": { "version": "0.0.21", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", @@ -2158,6 +2472,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@vscode/iconv-lite-umd": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", + "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", + "license": "MIT" + }, "node_modules/@vscode/l10n": { "version": "0.0.18", "dev": true, @@ -2360,20 +2680,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/ajv": { "version": "6.12.6", "dev": true, @@ -2442,29 +2748,6 @@ "version": "20.3.0", "license": "MIT" }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/argparse": { "version": "2.0.1", "license": "Python-2.0" @@ -2514,7 +2797,6 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -2588,7 +2870,6 @@ }, "node_modules/brace-expansion": { "version": "2.0.1", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -2605,13 +2886,6 @@ "node": ">=8" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/builtin-modules": { "version": "1.1.1", "dev": true, @@ -2676,23 +2950,6 @@ "node": ">=6" } }, - "node_modules/canvas": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", - "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/chalk": { "version": "2.4.2", "dev": true, @@ -2825,16 +3082,6 @@ "node": ">= 6" } }, - "node_modules/chownr": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" - } - }, "node_modules/chroma-js": { "version": "2.4.2", "license": "(BSD-3-Clause AND Apache-2.0)" @@ -2864,17 +3111,6 @@ "dev": true, "license": "MIT" }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "color-support": "bin.js" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2947,14 +3183,6 @@ "proto-list": "~1.2.1" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/content-disposition": { "version": "0.5.4", "license": "MIT", @@ -3379,20 +3607,6 @@ "dev": true, "license": "MIT" }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -3431,14 +3645,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/depd": { "version": "2.0.0", "license": "MIT", @@ -3454,17 +3660,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/diff": { "version": "4.0.2", "dev": true, @@ -3496,8 +3691,13 @@ } }, "node_modules/dompurify": { - "version": "3.0.9", - "license": "(MPL-2.0 OR Apache-2.0)" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, "node_modules/dunder-proto": { "version": "1.0.1", @@ -4125,9 +4325,19 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "BSD-3-Clause" }, "node_modules/fastq": { @@ -4298,42 +4508,16 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "minipass": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">= 0.6" } }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, "node_modules/fs.realpath": { @@ -4360,28 +4544,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/geojson-vt": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", @@ -4584,14 +4746,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4677,21 +4831,6 @@ "node": ">= 14" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -5024,6 +5163,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jschardet": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-3.1.4.tgz", + "integrity": "sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==", + "license": "LGPL-2.1+", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/jsdom": { "version": "25.0.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", @@ -5432,20 +5580,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -5477,33 +5611,6 @@ "node": ">=8" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/mkdirp": { "version": "0.5.6", "dev": true, @@ -5516,9 +5623,72 @@ } }, "node_modules/monaco-editor": { - "version": "0.47.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.47.0.tgz", - "integrity": "sha512-VabVvHvQ9QmMwXu4du008ZDuyLnHs9j7ThVFsiJoXSOQk18+LF89N4ADzPbFenm0W4V2bGHnFBztIRQTgBfxzw==" + "name": "@codingame/monaco-vscode-editor-api", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-editor-api/-/monaco-vscode-editor-api-24.3.0.tgz", + "integrity": "sha512-W+jguLUI4exsIRe8iQ3i+SNhHsCgO9NvY0zFsxOhTiMhAADK7hEehd0CLnKbfoZt8XEbr/XQ0S3wISRFlQQlOQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0" + } + }, + "node_modules/monaco-languageclient": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/monaco-languageclient/-/monaco-languageclient-10.5.0.tgz", + "integrity": "sha512-stKCyDc/Nw4aZ2oP1M+7fye+rFi8PB0rciWzqJq5qFaDJnj9GQMCyE2oVeo1BqGy5v2p2G/gYkt8v00kuXvZkQ==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "^24.2.0", + "@codingame/monaco-vscode-configuration-service-override": "^24.2.0", + "@codingame/monaco-vscode-editor-api": "^24.2.0", + "@codingame/monaco-vscode-editor-service-override": "^24.2.0", + "@codingame/monaco-vscode-extension-api": "^24.2.0", + "@codingame/monaco-vscode-extensions-service-override": "^24.2.0", + "@codingame/monaco-vscode-language-pack-cs": "^24.2.0", + "@codingame/monaco-vscode-language-pack-de": "^24.2.0", + "@codingame/monaco-vscode-language-pack-es": "^24.2.0", + "@codingame/monaco-vscode-language-pack-fr": "^24.2.0", + "@codingame/monaco-vscode-language-pack-it": "^24.2.0", + "@codingame/monaco-vscode-language-pack-ja": "^24.2.0", + "@codingame/monaco-vscode-language-pack-ko": "^24.2.0", + "@codingame/monaco-vscode-language-pack-pl": "^24.2.0", + "@codingame/monaco-vscode-language-pack-pt-br": "^24.2.0", + "@codingame/monaco-vscode-language-pack-qps-ploc": "^24.2.0", + "@codingame/monaco-vscode-language-pack-ru": "^24.2.0", + "@codingame/monaco-vscode-language-pack-tr": "^24.2.0", + "@codingame/monaco-vscode-language-pack-zh-hans": "^24.2.0", + "@codingame/monaco-vscode-language-pack-zh-hant": "^24.2.0", + "@codingame/monaco-vscode-languages-service-override": "^24.2.0", + "@codingame/monaco-vscode-localization-service-override": "^24.2.0", + "@codingame/monaco-vscode-log-service-override": "^24.2.0", + "@codingame/monaco-vscode-model-service-override": "^24.2.0", + "@codingame/monaco-vscode-monarch-service-override": "^24.2.0", + "@codingame/monaco-vscode-textmate-service-override": "^24.2.0", + "@codingame/monaco-vscode-theme-defaults-default-extension": "^24.2.0", + "@codingame/monaco-vscode-theme-service-override": "^24.2.0", + "@codingame/monaco-vscode-views-service-override": "^24.2.0", + "@codingame/monaco-vscode-workbench-service-override": "^24.2.0", + "vscode": "npm:@codingame/monaco-vscode-extension-api@^24.2.0", + "vscode-languageclient": "~9.0.1", + "vscode-languageserver-protocol": "~3.17.5", + "vscode-ws-jsonrpc": "~3.5.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/monacopilot": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/monacopilot/-/monacopilot-1.2.9.tgz", + "integrity": "sha512-8dEBfiDR0wK4t7uDkHa7USfddhijWDBHA3vo7+NT2RfpPtcXlOYDHcTQrd2KdtwCzVzgxhnNAmkdIEvSXOrPwA==", + "license": "MIT", + "dependencies": { + "@monacopilot/core": "1.2.9" + }, + "peerDependencies": { + "monaco-editor": ">=0.41.0" + } }, "node_modules/ms": { "version": "2.1.3", @@ -5529,14 +5699,6 @@ "version": "1.0.0", "license": "MIT" }, - "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/nanoid": { "version": "3.3.7", "funding": [ @@ -5649,20 +5811,6 @@ "node": ">=0.10.0" } }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "dev": true, @@ -6419,21 +6567,6 @@ "node": ">= 0.8" } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { "version": "3.6.0", "dev": true, @@ -6705,14 +6838,6 @@ "node": ">= 0.8.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/set-function-length": { "version": "1.2.1", "license": "MIT", @@ -6773,48 +6898,6 @@ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true }, - "node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "peer": true - }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/simple-update-notifier": { "version": "2.0.0", "dev": true, @@ -6834,16 +6917,6 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6853,17 +6926,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "dev": true, @@ -6900,16 +6962,6 @@ "node": ">=10" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-width": { "version": "4.2.3", "license": "MIT", @@ -7053,56 +7105,6 @@ "node": ">=12.17" } }, - "node_modules/tar": { - "version": "6.2.1", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser": { - "version": "5.28.1", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/text-table": { "version": "0.2.0", "dev": true, @@ -8280,6 +8282,17 @@ } } }, + "node_modules/vscode": { + "name": "@codingame/monaco-vscode-extension-api", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-extension-api/-/monaco-vscode-extension-api-24.3.0.tgz", + "integrity": "sha512-RykrxXwwngF7xR0dtiUhqRTKqLOnDwCKvcLvh7lLIYT1CertvzhRojoyAX/klR1z3klInLL6ycrQJ88Ykd3w+A==", + "license": "MIT", + "dependencies": { + "@codingame/monaco-vscode-api": "24.3.0", + "@codingame/monaco-vscode-extensions-service-override": "24.3.0" + } + }, "node_modules/vscode-html-languageservice": { "version": "5.1.2", "dev": true, @@ -8291,6 +8304,51 @@ "vscode-uri": "^3.0.8" } }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "license": "MIT", + "dependencies": { + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "engines": { + "vscode": "^1.82.0" + } + }, + "node_modules/vscode-languageclient/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.11", "dev": true, @@ -8298,7 +8356,18 @@ }, "node_modules/vscode-languageserver-types": { "version": "3.17.5", - "dev": true, + "license": "MIT" + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "license": "MIT" + }, + "node_modules/vscode-textmate": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.2.1.tgz", + "integrity": "sha512-eXiUi2yYFv9bdvgrYtJynA7UemCEkpVNE50S9iBBA08LYG5t9+/TB+8IRS/YoYOubCez2OkSyZ1Q12eQMwzbrw==", "license": "MIT" }, "node_modules/vscode-uri": { @@ -8306,6 +8375,28 @@ "dev": true, "license": "MIT" }, + "node_modules/vscode-ws-jsonrpc": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/vscode-ws-jsonrpc/-/vscode-ws-jsonrpc-3.5.0.tgz", + "integrity": "sha512-13ZDy7Od4AfEPK2HIfY3DtyRi4FVsvFql1yobVJrpIoHOKGGJpIjVvIJpMxkrHzCZzWlYlg+WEu2hrYkCTvM0Q==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "~8.2.1" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/vscode-ws-jsonrpc/node_modules/vscode-jsonrpc": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", + "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/vt-pbf": { "version": "3.1.3", "license": "MIT", @@ -8484,17 +8575,6 @@ "node": ">=8" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/wordwrapjs": { "version": "5.1.0", "license": "MIT", @@ -8648,13 +8728,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true - }, "node_modules/yargs": { "version": "17.7.2", "license": "MIT", @@ -8695,6 +8768,9 @@ "dependencies": { "@apache-arrow/ts": "^15.0.2", "@chamaeleonidae/chmln": "^1.0.1", + "@codingame/monaco-vscode-api": "^24.2.0", + "@codingame/monaco-vscode-languages-service-override": "^24.2.0", + "@codingame/monaco-vscode-textmate-service-override": "^24.2.0", "@floating-ui/vue": "^1.1.5", "@fontsource/poppins": "^5.0.14", "@fullstory/browser": "^2.0.6", @@ -8715,17 +8791,22 @@ "arquero": "^5.2.0", "attr-accept": "^2.2.5", "chroma-js": "^2.4.2", + "fast-uri": "^3.1.0", "launchdarkly-js-client-sdk": "^3.8.1", "lucide": "^0.525.0", "mapbox-gl": "^3.2.0", "marked": "^12.0.1", - "monaco-editor": "^0.47.0", + "monaco-editor": "^0.52.0", + "monaco-languageclient": "^10.5.0", + "monacopilot": "^1.2.9", "plotly.js-dist-min": "^2.35.2", "pretty-bytes": "^6.1.1", "typescript": "^5.4.3", "vega": "^5.22.1", "vega-embed": "^6.22.1", "vega-lite": "^5.7.1", + "vscode-languageclient": "^9.0.1", + "vscode-ws-jsonrpc": "^3.5.0", "vue": "^3.5.0", "vue-dompurify-html": "^5.0.1" }, @@ -8813,6 +8894,12 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "src/ui/node_modules/monaco-editor": { + "version": "0.52.2", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz", + "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==", + "license": "MIT" + }, "tests/e2e": { "name": "writer-e2e", "version": "1.0.0", diff --git a/package.json b/package.json index 6ce5b760f..c0c0d3a9a 100644 --- a/package.json +++ b/package.json @@ -6,22 +6,20 @@ "src/ui", "tests/e2e" ], - "engines": { - "node": "22.x.x", - "npm": "10.x.x" - }, + "engines": { + "node": "22.x.x", + "npm": "10.x.x" + }, "scripts": { "build": "npm run ui:build && npm run apps:build && npm run ui:codegen", "test": "npm run --if-present -ws test", "lint": "npm run --if-present -ws lint", "dev": "npm run -w writer-ui dev", "custom.dev": "npm run -w writer-ui custom.dev", - "cli:test": "pytest tests -o log_cli=true ", "cli:lint": "mypy ./src/writer --exclude app_templates/* && ruff check", "cli:build": "npm run ui:codegen", - - "ui:codegen": "npm run -w writer-ui codegen", + "ui:codegen": "npm run -w writer-ui codegen", "ui:dev": "npm run -w writer-ui dev", "ui:build": "npm run -w writer-ui build", "ui:preview": "npm run -w writer-ui preview", @@ -29,7 +27,6 @@ "ui:custom.check": "npm run -w writer-ui custom.check", "ui:lint": "npm run -w writer-ui lint", "ui:lint.ci": "npm run -w writer-ui lint.ci", - "e2e": "npm run -w writer-e2e e2e", "e2e:setup": "npm run -w writer-e2e e2e:setup", "e2e:ui": "npm run -w writer-e2e e2e:ui", @@ -37,7 +34,6 @@ "e2e:firefox": "npm run -w writer-e2e e2e:firefox", "e2e:chromium": "npm run -w writer-e2e e2e:chromium", "e2e:webkit": "npm run -w writer-e2e e2e:webkit", - "apps:build": "cp -R ./apps/hello ./src/writer/app_templates/ && cp -R ./apps/default ./src/writer/app_templates/", "codegen": "npm run ui:codegen", "ld:upload-sourcemaps": "./scripts/upload-sourcemaps.sh", @@ -45,5 +41,9 @@ }, "devDependencies": { "playwright": "^1.57.0" + }, + "dependencies": { + "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@^24.2.0", + "vscode": "npm:@codingame/monaco-vscode-extension-api@^24.2.0" } } diff --git a/poetry.lock b/poetry.lock index 76ff2007f..285f41f35 100644 --- a/poetry.lock +++ b/poetry.lock @@ -90,6 +90,38 @@ doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] trio = ["trio (>=0.26.1)"] +[[package]] +name = "astroid" +version = "3.3.11" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.9.0" +groups = ["main"] +markers = "python_version < \"3.10\"" +files = [ + {file = "astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec"}, + {file = "astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} + +[[package]] +name = "astroid" +version = "4.0.3" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.10.0" +groups = ["main"] +markers = "python_version >= \"3.10\"" +files = [ + {file = "astroid-4.0.3-py3-none-any.whl", hash = "sha256:864a0a34af1bd70e1049ba1e61cee843a7252c826d97825fcee9b2fcbd9e1b14"}, + {file = "astroid-4.0.3.tar.gz", hash = "sha256:08d1de40d251cc3dc4a7a12726721d475ac189e4e583d596ece7422bc176bda3"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} + [[package]] name = "async-timeout" version = "5.0.1" @@ -138,6 +170,129 @@ files = [ [package.dependencies] cryptography = "*" +[[package]] +name = "autopep8" +version = "2.0.4" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "autopep8-2.0.4-py2.py3-none-any.whl", hash = "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb"}, + {file = "autopep8-2.0.4.tar.gz", hash = "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"}, +] + +[package.dependencies] +pycodestyle = ">=2.10.0" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[[package]] +name = "black" +version = "25.11.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version < \"3.10\"" +files = [ + {file = "black-25.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec311e22458eec32a807f029b2646f661e6859c3f61bc6d9ffb67958779f392e"}, + {file = "black-25.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1032639c90208c15711334d681de2e24821af0575573db2810b0763bcd62e0f0"}, + {file = "black-25.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0f7c461df55cf32929b002335883946a4893d759f2df343389c4396f3b6b37"}, + {file = "black-25.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:f9786c24d8e9bd5f20dc7a7f0cdd742644656987f6ea6947629306f937726c03"}, + {file = "black-25.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:895571922a35434a9d8ca67ef926da6bc9ad464522a5fe0db99b394ef1c0675a"}, + {file = "black-25.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb4f4b65d717062191bdec8e4a442539a8ea065e6af1c4f4d36f0cdb5f71e170"}, + {file = "black-25.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d81a44cbc7e4f73a9d6ae449ec2317ad81512d1e7dce7d57f6333fd6259737bc"}, + {file = "black-25.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:7eebd4744dfe92ef1ee349dc532defbf012a88b087bb7ddd688ff59a447b080e"}, + {file = "black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac"}, + {file = "black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96"}, + {file = "black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd"}, + {file = "black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409"}, + {file = "black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b"}, + {file = "black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd"}, + {file = "black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993"}, + {file = "black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c"}, + {file = "black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170"}, + {file = "black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545"}, + {file = "black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda"}, + {file = "black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664"}, + {file = "black-25.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3bb5ce32daa9ff0605d73b6f19da0b0e6c1f8f2d75594db539fdfed722f2b06"}, + {file = "black-25.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9815ccee1e55717fe9a4b924cae1646ef7f54e0f990da39a34fc7b264fcf80a2"}, + {file = "black-25.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92285c37b93a1698dcbc34581867b480f1ba3a7b92acf1fe0467b04d7a4da0dc"}, + {file = "black-25.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:43945853a31099c7c0ff8dface53b4de56c41294fa6783c0441a8b1d9bf668bc"}, + {file = "black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b"}, + {file = "black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +pytokens = ">=0.3.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "black" +version = "25.12.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version >= \"3.10\"" +files = [ + {file = "black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8"}, + {file = "black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a"}, + {file = "black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea"}, + {file = "black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f"}, + {file = "black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da"}, + {file = "black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a"}, + {file = "black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be"}, + {file = "black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b"}, + {file = "black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5"}, + {file = "black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655"}, + {file = "black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a"}, + {file = "black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783"}, + {file = "black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59"}, + {file = "black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892"}, + {file = "black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43"}, + {file = "black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5"}, + {file = "black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f"}, + {file = "black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf"}, + {file = "black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d"}, + {file = "black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce"}, + {file = "black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5"}, + {file = "black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f"}, + {file = "black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f"}, + {file = "black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83"}, + {file = "black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b"}, + {file = "black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828"}, + {file = "black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +pytokens = ">=0.3.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "certifi" version = "2025.1.31" @@ -355,11 +510,11 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["main", "build"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "platform_system == \"Windows\"", build = "platform_system == \"Windows\" or sys_platform == \"win32\""} [[package]] name = "cryptography" @@ -419,6 +574,22 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "dill" +version = "0.4.0" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049"}, + {file = "dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + [[package]] name = "distro" version = "1.9.0" @@ -431,6 +602,22 @@ files = [ {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, ] +[[package]] +name = "docstring-to-markdown" +version = "0.17" +description = "On the fly conversion of Python docstrings to markdown" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "docstring_to_markdown-0.17-py3-none-any.whl", hash = "sha256:fd7d5094aa83943bf5f9e1a13701866b7c452eac19765380dead666e36d3711c"}, + {file = "docstring_to_markdown-0.17.tar.gz", hash = "sha256:df72a112294c7492487c9da2451cae0faeee06e86008245c188c5761c9590ca3"}, +] + +[package.dependencies] +importlib-metadata = ">=3.6" +typing_extensions = ">=4.6" + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -485,6 +672,23 @@ all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (> standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] +[[package]] +name = "flake8" +version = "7.1.2" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +groups = ["main"] +files = [ + {file = "flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a"}, + {file = "flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.12.0,<2.13.0" +pyflakes = ">=3.2.0,<3.3.0" + [[package]] name = "gitignore-parser" version = "0.1.12" @@ -570,6 +774,30 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "importlib-metadata" +version = "8.7.1" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"}, + {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=3.4)"] +perf = ["ipython"] +test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["mypy (<1.19) ; platform_python_implementation == \"PyPy\"", "pytest-mypy (>=1.0.1)"] + [[package]] name = "iniconfig" version = "2.1.0" @@ -582,6 +810,63 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +[[package]] +name = "isort" +version = "6.1.0" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.9.0" +groups = ["main"] +markers = "python_version < \"3.10\"" +files = [ + {file = "isort-6.1.0-py3-none-any.whl", hash = "sha256:58d8927ecce74e5087aef019f778d4081a3b6c98f15a80ba35782ca8a2097784"}, + {file = "isort-6.1.0.tar.gz", hash = "sha256:9b8f96a14cfee0677e78e941ff62f03769a06d412aabb9e2a90487b3b7e8d481"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.6.0", markers = "python_version < \"3.10\""} + +[package.extras] +colors = ["colorama"] +plugins = ["setuptools"] + +[[package]] +name = "isort" +version = "7.0.0" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.10.0" +groups = ["main"] +markers = "python_version >= \"3.10\"" +files = [ + {file = "isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1"}, + {file = "isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187"}, +] + +[package.extras] +colors = ["colorama"] +plugins = ["setuptools"] + +[[package]] +name = "jedi" +version = "0.19.2" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, +] + +[package.dependencies] +parso = ">=0.8.4,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] + [[package]] name = "jinja2" version = "3.1.6" @@ -835,6 +1120,18 @@ files = [ {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + [[package]] name = "mypy" version = "1.15.0" @@ -895,7 +1192,7 @@ version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.8" -groups = ["build"] +groups = ["main", "build"] files = [ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, @@ -1262,6 +1559,76 @@ files = [ numpy = ">=1.23.5" types-pytz = ">=2022.1.1" +[[package]] +name = "parso" +version = "0.8.5" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"}, + {file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + +[[package]] +name = "pathspec" +version = "1.0.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pathspec-1.0.1-py3-none-any.whl", hash = "sha256:8870061f22c58e6d83463cfce9a7dd6eca0512c772c1001fb09ac64091816721"}, + {file = "pathspec-1.0.1.tar.gz", hash = "sha256:e2769b508d0dd47b09af6ee2c75b2744a2cb1f474ae4b1494fd6a1b7a841613c"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] +tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] + +[[package]] +name = "platformdirs" +version = "4.4.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "python_version < \"3.10\"" +files = [ + {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"}, + {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] + +[[package]] +name = "platformdirs" +version = "4.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version >= \"3.10\"" +files = [ + {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"}, + {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"}, +] + +[package.extras] +docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"] +type = ["mypy (>=1.18.2)"] + [[package]] name = "plotly" version = "5.24.1" @@ -1284,7 +1651,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" -groups = ["build"] +groups = ["main", "build"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1404,6 +1771,18 @@ files = [ [package.extras] test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] +[[package]] +name = "pycodestyle" +version = "2.12.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, +] + [[package]] name = "pycparser" version = "2.22" @@ -1551,6 +1930,95 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3) ; python_version < \"3.11\""] + +[[package]] +name = "pyflakes" +version = "3.2.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, +] + +[[package]] +name = "pylint" +version = "3.3.9" +description = "python code static checker" +optional = false +python-versions = ">=3.9.0" +groups = ["main"] +markers = "python_version < \"3.10\"" +files = [ + {file = "pylint-3.3.9-py3-none-any.whl", hash = "sha256:01f9b0462c7730f94786c283f3e52a1fbdf0494bbe0971a78d7277ef46a751e7"}, + {file = "pylint-3.3.9.tar.gz", hash = "sha256:d312737d7b25ccf6b01cc4ac629b5dcd14a0fcf3ec392735ac70f137a9d5f83a"}, +] + +[package.dependencies] +astroid = ">=3.3.8,<=3.4.0.dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = {version = ">=0.2", markers = "python_version < \"3.11\""} +isort = ">=4.2.5,<5.13 || >5.13,<7" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2" +tomli = {version = ">=1.1", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pylint" +version = "4.0.4" +description = "python code static checker" +optional = false +python-versions = ">=3.10.0" +groups = ["main"] +markers = "python_version >= \"3.10\"" +files = [ + {file = "pylint-4.0.4-py3-none-any.whl", hash = "sha256:63e06a37d5922555ee2c20963eb42559918c20bd2b21244e4ef426e7c43b92e0"}, + {file = "pylint-4.0.4.tar.gz", hash = "sha256:d9b71674e19b1c36d79265b5887bf8e55278cbe236c9e95d22dc82cf044fdbd2"}, +] + +[package.dependencies] +astroid = ">=4.0.2,<=4.1.dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, +] +isort = ">=5,<5.13 || >5.13,<8" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2" +tomli = {version = ">=1.1", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + [[package]] name = "pyrfc3339" version = "2.1.0" @@ -1620,6 +2088,69 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-lsp-jsonrpc" +version = "1.1.2" +description = "JSON RPC 2.0 server library" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "python-lsp-jsonrpc-1.1.2.tar.gz", hash = "sha256:4688e453eef55cd952bff762c705cedefa12055c0aec17a06f595bcc002cc912"}, + {file = "python_lsp_jsonrpc-1.1.2-py3-none-any.whl", hash = "sha256:7339c2e9630ae98903fdaea1ace8c47fba0484983794d6aafd0bd8989be2b03c"}, +] + +[package.dependencies] +ujson = ">=3.0.0" + +[package.extras] +test = ["coverage", "pycodestyle", "pyflakes", "pylint", "pytest", "pytest-cov"] + +[[package]] +name = "python-lsp-server" +version = "1.14.0" +description = "Python Language Server for the Language Server Protocol" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "python_lsp_server-1.14.0-py3-none-any.whl", hash = "sha256:a71a917464effc48f4c70363f90b8520e5e3ba8201428da80b97a7ceb259e32a"}, + {file = "python_lsp_server-1.14.0.tar.gz", hash = "sha256:509c445fc667f41ffd3191cb7512a497bf7dd76c14ceb1ee2f6c13ebe71f9a6b"}, +] + +[package.dependencies] +autopep8 = {version = ">=2.0.4,<2.1.0", optional = true, markers = "extra == \"all\""} +black = "*" +docstring-to-markdown = "*" +flake8 = {version = ">=7.1,<8", optional = true, markers = "extra == \"all\""} +importlib_metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} +jedi = ">=0.17.2,<0.20.0" +mccabe = {version = ">=0.7.0,<0.8.0", optional = true, markers = "extra == \"all\""} +pluggy = ">=1.0.0" +pycodestyle = {version = ">=2.12.0,<2.13.0", optional = true, markers = "extra == \"all\""} +pydocstyle = {version = ">=6.3.0,<6.4.0", optional = true, markers = "extra == \"all\""} +pyflakes = {version = ">=3.2.0,<3.3.0", optional = true, markers = "extra == \"all\""} +pylint = {version = ">=3.1,<4.1.0", optional = true, markers = "extra == \"all\""} +python-lsp-jsonrpc = ">=1.1.0,<2.0.0" +rope = {version = ">=1.11.0", optional = true, markers = "extra == \"all\""} +ujson = ">=3.0.0" +whatthepatch = {version = ">=1.0.2,<2.0.0", optional = true, markers = "extra == \"all\""} +yapf = {version = ">=0.33.0", optional = true, markers = "extra == \"all\""} + +[package.extras] +all = ["autopep8 (>=2.0.4,<2.1.0)", "flake8 (>=7.1,<8)", "mccabe (>=0.7.0,<0.8.0)", "pycodestyle (>=2.12.0,<2.13.0)", "pydocstyle (>=6.3.0,<6.4.0)", "pyflakes (>=3.2.0,<3.3.0)", "pylint (>=3.1,<4.1.0)", "rope (>=1.11.0)", "whatthepatch (>=1.0.2,<2.0.0)", "yapf (>=0.33.0)"] +autopep8 = ["autopep8 (>=2.0.4,<2.1.0)"] +flake8 = ["flake8 (>=7.1,<8)"] +mccabe = ["mccabe (>=0.7.0,<0.8.0)"] +pycodestyle = ["pycodestyle (>=2.12.0,<2.13.0)"] +pydocstyle = ["pydocstyle (>=6.3.0,<6.4.0)"] +pyflakes = ["pyflakes (>=3.2.0,<3.3.0)"] +pylint = ["pylint (>=3.1,<4.1.0)"] +rope = ["rope (>=1.11.0)"] +test = ["coverage", "flaky", "matplotlib", "numpy", "pandas", "pylint (>=3.1,<4.1.0)", "pyqt6", "pytest", "pytest-cov", "websockets (>=10.3)"] +websockets = ["websockets (>=10.3)"] +yapf = ["whatthepatch (>=1.0.2,<2.0.0)", "yapf (>=0.33.0)"] + [[package]] name = "python-multipart" version = "0.0.20" @@ -1632,6 +2163,44 @@ files = [ {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, ] +[[package]] +name = "pytokens" +version = "0.3.0" +description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3"}, + {file = "pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a"}, +] + +[package.extras] +dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] + +[[package]] +name = "pytoolconfig" +version = "1.3.1" +description = "Python tool configuration" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pytoolconfig-1.3.1-py3-none-any.whl", hash = "sha256:5d8cea8ae1996938ec3eaf44567bbc5ef1bc900742190c439a44a704d6e1b62b"}, + {file = "pytoolconfig-1.3.1.tar.gz", hash = "sha256:51e6bd1a6f108238ae6aab6a65e5eed5e75d456be1c2bf29b04e5c1e7d7adbae"}, +] + +[package.dependencies] +packaging = ">=23.2" +platformdirs = {version = ">=3.11.0", optional = true, markers = "extra == \"global\""} +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["sphinx (>=7.1.2)", "tabulate (>=0.9.0)"] +gendocs = ["pytoolconfig[doc]", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-rtd-theme (>=2.0.0)"] +global = ["platformdirs (>=3.11.0)"] +validation = ["pydantic (>=2.5.3)"] + [[package]] name = "pytz" version = "2024.2" @@ -1702,6 +2271,26 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "rope" +version = "1.14.0" +description = "a python refactoring library..." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "rope-1.14.0-py3-none-any.whl", hash = "sha256:00a7ea8c0c376fc0b053b2f2f8ef3bfb8b50fecf1ebf3eb80e4f8bd7f1941918"}, + {file = "rope-1.14.0.tar.gz", hash = "sha256:8803e3b667315044f6270b0c69a10c0679f9f322ed8efe6245a93ceb7658da69"}, +] + +[package.dependencies] +pytoolconfig = {version = ">=1.2.2", extras = ["global"]} + +[package.extras] +dev = ["build (>=0.7.0)", "pre-commit (>=2.20.0)", "pytest (>=7.0.1)", "pytest-cov (>=4.1.0)", "pytest-timeout (>=2.1.0)"] +doc = ["pytoolconfig[doc]", "sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx-rtd-theme (>=1.0.0)"] +release = ["pip-tools (>=6.12.1)", "toml (>=0.10.2)", "twine (>=4.0.2)"] + [[package]] name = "rpds-py" version = "0.24.0" @@ -1901,6 +2490,18 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "snowballstemmer" +version = "3.0.1" +description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" +groups = ["main"] +files = [ + {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, + {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, +] + [[package]] name = "starlette" version = "0.46.2" @@ -1954,7 +2555,7 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["build"] +groups = ["main", "build"] markers = "python_version <= \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, @@ -1991,6 +2592,18 @@ files = [ {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] +[[package]] +name = "tomlkit" +version = "0.13.3" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, + {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, +] + [[package]] name = "types-python-dateutil" version = "2.9.0.20241206" @@ -2069,6 +2682,100 @@ files = [ {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, ] +[[package]] +name = "ujson" +version = "5.11.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "ujson-5.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:446e8c11c06048611c9d29ef1237065de0af07cabdd97e6b5b527b957692ec25"}, + {file = "ujson-5.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16ccb973b7ada0455201808ff11d48fe9c3f034a6ab5bd93b944443c88299f89"}, + {file = "ujson-5.11.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3134b783ab314d2298d58cda7e47e7a0f7f71fc6ade6ac86d5dbeaf4b9770fa6"}, + {file = "ujson-5.11.0-cp310-cp310-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:185f93ebccffebc8baf8302c869fac70dd5dd78694f3b875d03a31b03b062cdb"}, + {file = "ujson-5.11.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d06e87eded62ff0e5f5178c916337d2262fdbc03b31688142a3433eabb6511db"}, + {file = "ujson-5.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:181fb5b15703a8b9370b25345d2a1fd1359f0f18776b3643d24e13ed9c036d4c"}, + {file = "ujson-5.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a4df61a6df0a4a8eb5b9b1ffd673429811f50b235539dac586bb7e9e91994138"}, + {file = "ujson-5.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6eff24e1abd79e0ec6d7eae651dd675ddbc41f9e43e29ef81e16b421da896915"}, + {file = "ujson-5.11.0-cp310-cp310-win32.whl", hash = "sha256:30f607c70091483550fbd669a0b37471e5165b317d6c16e75dba2aa967608723"}, + {file = "ujson-5.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:3d2720e9785f84312b8e2cb0c2b87f1a0b1c53aaab3b2af3ab817d54409012e0"}, + {file = "ujson-5.11.0-cp310-cp310-win_arm64.whl", hash = "sha256:85e6796631165f719084a9af00c79195d3ebf108151452fefdcb1c8bb50f0105"}, + {file = "ujson-5.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d7c46cb0fe5e7056b9acb748a4c35aa1b428025853032540bb7e41f46767321f"}, + {file = "ujson-5.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8951bb7a505ab2a700e26f691bdfacf395bc7e3111e3416d325b513eea03a58"}, + {file = "ujson-5.11.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952c0be400229940248c0f5356514123d428cba1946af6fa2bbd7503395fef26"}, + {file = "ujson-5.11.0-cp311-cp311-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:94fcae844f1e302f6f8095c5d1c45a2f0bfb928cccf9f1b99e3ace634b980a2a"}, + {file = "ujson-5.11.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e0ec1646db172beb8d3df4c32a9d78015e671d2000af548252769e33079d9a6"}, + {file = "ujson-5.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:da473b23e3a54448b008d33f742bcd6d5fb2a897e42d1fc6e7bf306ea5d18b1b"}, + {file = "ujson-5.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:aa6b3d4f1c0d3f82930f4cbd7fe46d905a4a9205a7c13279789c1263faf06dba"}, + {file = "ujson-5.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4843f3ab4fe1cc596bb7e02228ef4c25d35b4bb0809d6a260852a4bfcab37ba3"}, + {file = "ujson-5.11.0-cp311-cp311-win32.whl", hash = "sha256:e979fbc469a7f77f04ec2f4e853ba00c441bf2b06720aa259f0f720561335e34"}, + {file = "ujson-5.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:683f57f0dd3acdd7d9aff1de0528d603aafcb0e6d126e3dc7ce8b020a28f5d01"}, + {file = "ujson-5.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:7855ccea3f8dad5e66d8445d754fc1cf80265a4272b5f8059ebc7ec29b8d0835"}, + {file = "ujson-5.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7895f0d2d53bd6aea11743bd56e3cb82d729980636cd0ed9b89418bf66591702"}, + {file = "ujson-5.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12b5e7e22a1fe01058000d1b317d3b65cc3daf61bd2ea7a2b76721fe160fa74d"}, + {file = "ujson-5.11.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0180a480a7d099082501cad1fe85252e4d4bf926b40960fb3d9e87a3a6fbbc80"}, + {file = "ujson-5.11.0-cp312-cp312-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:fa79fdb47701942c2132a9dd2297a1a85941d966d8c87bfd9e29b0cf423f26cc"}, + {file = "ujson-5.11.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8254e858437c00f17cb72e7a644fc42dad0ebb21ea981b71df6e84b1072aaa7c"}, + {file = "ujson-5.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1aa8a2ab482f09f6c10fba37112af5f957689a79ea598399c85009f2f29898b5"}, + {file = "ujson-5.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a638425d3c6eed0318df663df44480f4a40dc87cc7c6da44d221418312f6413b"}, + {file = "ujson-5.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7e3cff632c1d78023b15f7e3a81c3745cd3f94c044d1e8fa8efbd6b161997bbc"}, + {file = "ujson-5.11.0-cp312-cp312-win32.whl", hash = "sha256:be6b0eaf92cae8cdee4d4c9e074bde43ef1c590ed5ba037ea26c9632fb479c88"}, + {file = "ujson-5.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:b7b136cc6abc7619124fd897ef75f8e63105298b5ca9bdf43ebd0e1fa0ee105f"}, + {file = "ujson-5.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:6cd2df62f24c506a0ba322d5e4fe4466d47a9467b57e881ee15a31f7ecf68ff6"}, + {file = "ujson-5.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:109f59885041b14ee9569bf0bb3f98579c3fa0652317b355669939e5fc5ede53"}, + {file = "ujson-5.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a31c6b8004438e8c20fc55ac1c0e07dad42941db24176fe9acf2815971f8e752"}, + {file = "ujson-5.11.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78c684fb21255b9b90320ba7e199780f653e03f6c2528663768965f4126a5b50"}, + {file = "ujson-5.11.0-cp313-cp313-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:4c9f5d6a27d035dd90a146f7761c2272cf7103de5127c9ab9c4cd39ea61e878a"}, + {file = "ujson-5.11.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:837da4d27fed5fdc1b630bd18f519744b23a0b5ada1bbde1a36ba463f2900c03"}, + {file = "ujson-5.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:787aff4a84da301b7f3bac09bc696e2e5670df829c6f8ecf39916b4e7e24e701"}, + {file = "ujson-5.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6dd703c3e86dc6f7044c5ac0b3ae079ed96bf297974598116aa5fb7f655c3a60"}, + {file = "ujson-5.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3772e4fe6b0c1e025ba3c50841a0ca4786825a4894c8411bf8d3afe3a8061328"}, + {file = "ujson-5.11.0-cp313-cp313-win32.whl", hash = "sha256:8fa2af7c1459204b7a42e98263b069bd535ea0cd978b4d6982f35af5a04a4241"}, + {file = "ujson-5.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:34032aeca4510a7c7102bd5933f59a37f63891f30a0706fb46487ab6f0edf8f0"}, + {file = "ujson-5.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:ce076f2df2e1aa62b685086fbad67f2b1d3048369664b4cdccc50707325401f9"}, + {file = "ujson-5.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:65724738c73645db88f70ba1f2e6fb678f913281804d5da2fd02c8c5839af302"}, + {file = "ujson-5.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29113c003ca33ab71b1b480bde952fbab2a0b6b03a4ee4c3d71687cdcbd1a29d"}, + {file = "ujson-5.11.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c44c703842024d796b4c78542a6fcd5c3cb948b9fc2a73ee65b9c86a22ee3638"}, + {file = "ujson-5.11.0-cp314-cp314-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:e750c436fb90edf85585f5c62a35b35082502383840962c6983403d1bd96a02c"}, + {file = "ujson-5.11.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f278b31a7c52eb0947b2db55a5133fbc46b6f0ef49972cd1a80843b72e135aba"}, + {file = "ujson-5.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ab2cb8351d976e788669c8281465d44d4e94413718af497b4e7342d7b2f78018"}, + {file = "ujson-5.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:090b4d11b380ae25453100b722d0609d5051ffe98f80ec52853ccf8249dfd840"}, + {file = "ujson-5.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:80017e870d882d5517d28995b62e4e518a894f932f1e242cbc802a2fd64d365c"}, + {file = "ujson-5.11.0-cp314-cp314-win32.whl", hash = "sha256:1d663b96eb34c93392e9caae19c099ec4133ba21654b081956613327f0e973ac"}, + {file = "ujson-5.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:849e65b696f0d242833f1df4182096cedc50d414215d1371fca85c541fbff629"}, + {file = "ujson-5.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:e73df8648c9470af2b6a6bf5250d4744ad2cf3d774dcf8c6e31f018bdd04d764"}, + {file = "ujson-5.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:de6e88f62796372fba1de973c11138f197d3e0e1d80bcb2b8aae1e826096d433"}, + {file = "ujson-5.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e56ef8066f11b80d620985ae36869a3ff7e4b74c3b6129182ec5d1df0255f3"}, + {file = "ujson-5.11.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a325fd2c3a056cf6c8e023f74a0c478dd282a93141356ae7f16d5309f5ff823"}, + {file = "ujson-5.11.0-cp314-cp314t-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:a0af6574fc1d9d53f4ff371f58c96673e6d988ed2b5bf666a6143c782fa007e9"}, + {file = "ujson-5.11.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10f29e71ecf4ecd93a6610bd8efa8e7b6467454a363c3d6416db65de883eb076"}, + {file = "ujson-5.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1a0a9b76a89827a592656fe12e000cf4f12da9692f51a841a4a07aa4c7ecc41c"}, + {file = "ujson-5.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b16930f6a0753cdc7d637b33b4e8f10d5e351e1fb83872ba6375f1e87be39746"}, + {file = "ujson-5.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04c41afc195fd477a59db3a84d5b83a871bd648ef371cf8c6f43072d89144eef"}, + {file = "ujson-5.11.0-cp314-cp314t-win32.whl", hash = "sha256:aa6d7a5e09217ff93234e050e3e380da62b084e26b9f2e277d2606406a2fc2e5"}, + {file = "ujson-5.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:48055e1061c1bb1f79e75b4ac39e821f3f35a9b82de17fce92c3140149009bec"}, + {file = "ujson-5.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1194b943e951092db611011cb8dbdb6cf94a3b816ed07906e14d3bc6ce0e90ab"}, + {file = "ujson-5.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65f3c279f4ed4bf9131b11972040200c66ae040368abdbb21596bf1564899694"}, + {file = "ujson-5.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:99c49400572cd77050894e16864a335225191fd72a818ea6423ae1a06467beac"}, + {file = "ujson-5.11.0-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0654a2691fc252c3c525e3d034bb27b8a7546c9d3eb33cd29ce6c9feda361a6a"}, + {file = "ujson-5.11.0-cp39-cp39-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:6b6ec7e7321d7fc19abdda3ad809baef935f49673951a8bab486aea975007e02"}, + {file = "ujson-5.11.0-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f62b9976fabbcde3ab6e413f4ec2ff017749819a0786d84d7510171109f2d53c"}, + {file = "ujson-5.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7f1a27ab91083b4770e160d17f61b407f587548f2c2b5fbf19f94794c495594a"}, + {file = "ujson-5.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ecd6ff8a3b5a90c292c2396c2d63c687fd0ecdf17de390d852524393cd9ed052"}, + {file = "ujson-5.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9aacbeb23fdbc4b256a7d12e0beb9063a1ba5d9e0dbb2cfe16357c98b4334596"}, + {file = "ujson-5.11.0-cp39-cp39-win32.whl", hash = "sha256:674f306e3e6089f92b126eb2fe41bcb65e42a15432c143365c729fdb50518547"}, + {file = "ujson-5.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:c6618f480f7c9ded05e78a1938873fde68baf96cdd74e6d23c7e0a8441175c4b"}, + {file = "ujson-5.11.0-cp39-cp39-win_arm64.whl", hash = "sha256:5600202a731af24a25e2d7b6eb3f648e4ecd4bb67c4d5cf12f8fab31677469c9"}, + {file = "ujson-5.11.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:abae0fb58cc820092a0e9e8ba0051ac4583958495bfa5262a12f628249e3b362"}, + {file = "ujson-5.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fac6c0649d6b7c3682a0a6e18d3de6857977378dce8d419f57a0b20e3d775b39"}, + {file = "ujson-5.11.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b42c115c7c6012506e8168315150d1e3f76e7ba0f4f95616f4ee599a1372bbc"}, + {file = "ujson-5.11.0-pp311-pypy311_pp73-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:86baf341d90b566d61a394869ce77188cc8668f76d7bb2c311d77a00f4bdf844"}, + {file = "ujson-5.11.0-pp311-pypy311_pp73-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4598bf3965fc1a936bd84034312bcbe00ba87880ef1ee33e33c1e88f2c398b49"}, + {file = "ujson-5.11.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:416389ec19ef5f2013592f791486bef712ebce0cd59299bf9df1ba40bb2f6e04"}, + {file = "ujson-5.11.0.tar.gz", hash = "sha256:e204ae6f909f099ba6b6b942131cee359ddda2b6e4ea39c12eb8b991fe2010e0"}, +] + [[package]] name = "urllib3" version = "2.4.0" @@ -2238,6 +2945,18 @@ files = [ {file = "websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee"}, ] +[[package]] +name = "whatthepatch" +version = "1.0.7" +description = "A patch parsing and application library." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "whatthepatch-1.0.7-py3-none-any.whl", hash = "sha256:1b6f655fd31091c001c209529dfaabbabdbad438f5de14e3951266ea0fc6e7ed"}, + {file = "whatthepatch-1.0.7.tar.gz", hash = "sha256:9eefb4ebea5200408e02d413d2b4bc28daea6b78bb4b4d53431af7245f7d7edf"}, +] + [[package]] name = "writer-sdk" version = "2.3.1" @@ -2262,10 +2981,46 @@ typing-extensions = ">=4.10,<5" [package.extras] aiohttp = ["aiohttp", "httpx-aiohttp (>=0.1.8)"] +[[package]] +name = "yapf" +version = "0.43.0" +description = "A formatter for Python code" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca"}, + {file = "yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e"}, +] + +[package.dependencies] +platformdirs = ">=3.5.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[[package]] +name = "zipp" +version = "3.23.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [extras] redis = [] [metadata] lock-version = "2.1" python-versions = ">=3.9.2, <4.0" -content-hash = "053af32476a69862c8ce697b461c56588f1df7245904c2497fb39561cdd04988" +content-hash = "47f3ac7aea021ea1a7ccbff76e7ef80f9757547ed205ccb7eb6c8ccf108a6a5b" diff --git a/pyproject.toml b/pyproject.toml index bbdef41b3..5b9f2dd01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ writer-sdk = ">= 2.3.1, < 3" python-multipart = ">=0.0.7, < 1" orjson = "^3.11.0, <4" launchdarkly-server-sdk = "^9.12.0" +python-lsp-server = {extras = ["all"], version = "^1.12.0"} [tool.poetry.group.build] optional = true diff --git a/src/ui/package.json b/src/ui/package.json index f72908a0d..13ab47f66 100644 --- a/src/ui/package.json +++ b/src/ui/package.json @@ -4,7 +4,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build", + "build": "NODE_OPTIONS='--max-old-space-size=8192' vite build", "preview": "vite preview --port 5050", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path ../../.gitignore --ignore-path .gitignore", "lint.ci": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --ignore-path ../../.gitignore --ignore-path .gitignore", @@ -18,10 +18,15 @@ "dependencies": { "@apache-arrow/ts": "^15.0.2", "@chamaeleonidae/chmln": "^1.0.1", + "@codingame/monaco-vscode-api": "^24.2.0", + "@codingame/monaco-vscode-languages-service-override": "^24.2.0", + "@codingame/monaco-vscode-textmate-service-override": "^24.2.0", "@floating-ui/vue": "^1.1.5", "@fontsource/poppins": "^5.0.14", "@fullstory/browser": "^2.0.6", "@googlemaps/js-api-loader": "^1.16.6", + "@launchdarkly/observability": "^0.4.0", + "@launchdarkly/session-replay": "^0.4.5", "@monaco-editor/loader": "^1.3.3", "@tato30/vue-pdf": "^1.11.3", "@tiptap/extension-document": "^3.0.7", @@ -36,19 +41,22 @@ "arquero": "^5.2.0", "attr-accept": "^2.2.5", "chroma-js": "^2.4.2", - "@launchdarkly/observability": "^0.4.0", - "@launchdarkly/session-replay": "^0.4.5", + "fast-uri": "^3.1.0", "launchdarkly-js-client-sdk": "^3.8.1", "lucide": "^0.525.0", "mapbox-gl": "^3.2.0", "marked": "^12.0.1", - "monaco-editor": "^0.47.0", + "monaco-editor": "^0.52.0", + "monaco-languageclient": "^10.5.0", + "monacopilot": "^1.2.9", "plotly.js-dist-min": "^2.35.2", "pretty-bytes": "^6.1.1", "typescript": "^5.4.3", "vega": "^5.22.1", "vega-embed": "^6.22.1", "vega-lite": "^5.7.1", + "vscode-languageclient": "^9.0.1", + "vscode-ws-jsonrpc": "^3.5.0", "vue": "^3.5.0", "vue-dompurify-html": "^5.0.1" }, diff --git a/src/ui/src/builder/BuilderEmbeddedCodeEditor.vue b/src/ui/src/builder/BuilderEmbeddedCodeEditor.vue index d419b450a..4425d831b 100644 --- a/src/ui/src/builder/BuilderEmbeddedCodeEditor.vue +++ b/src/ui/src/builder/BuilderEmbeddedCodeEditor.vue @@ -3,9 +3,11 @@ ref="rootEl" class="BuilderEmbeddedCodeEditor" :class="{ - 'BuilderEmbeddedCodeEditor--full': variant === 'full', - 'BuilderEmbeddedCodeEditor--halfScreen': variant === 'half-screen', - 'BuilderEmbeddedCodeEditor--singleLine': variant === 'single-line', + 'BuilderEmbeddedCodeEditor--full': props.variant === 'full', + 'BuilderEmbeddedCodeEditor--halfScreen': + props.variant === 'half-screen', + 'BuilderEmbeddedCodeEditor--singleLine': + props.variant === 'single-line', }" >
@@ -18,16 +20,26 @@ import "./builderEditorWorker"; import { onMounted, onUnmounted, - PropType, + type PropType, toRefs, useTemplateRef, watch, } from "vue"; +import { syncModelWithLSP } from "./lsp/lspModelSync"; +import { setModelDiagnostics, setupLSPDiagnostics } from "./lsp/lspDiagnostics"; +import { useMonacopilot } from "../composables/useMonacopilot"; +import { useLogger } from "@/composables/useLogger"; +import { useCodeEditorSettings } from "@/composables/useCodeEditorSettings"; const rootEl = useTemplateRef("rootEl"); const editorContainerEl = useTemplateRef("editorContainerEl"); const resizeObserver = new ResizeObserver(updateDimensions); let editor: monaco.editor.IStandaloneCodeEditor = null; +let lspSyncDisposable: monaco.IDisposable | null = null; +let diagnosticsDisposable: monaco.IDisposable | null = null; +let monacopilotCleanup: (() => void) | null = null; + +const { diagnosticsEnabled, aiCompletionEnabled } = useCodeEditorSettings(); type EditorVariant = "full" | "minimal" | "half-screen" | "single-line"; @@ -44,6 +56,8 @@ const props = defineProps({ const { modelValue, disabled, language } = toRefs(props); const emit = defineEmits(["update:modelValue"]); +const logger = useLogger(); + const VARIANTS_SETTINGS: Partial< Record< EditorVariant, @@ -54,6 +68,7 @@ const VARIANTS_SETTINGS: Partial< minimap: { enabled: false, }, + tabCompletion: "on", }, minimal: { minimap: { @@ -94,38 +109,146 @@ watch(disabled, (isNewDisabled) => { }); watch(modelValue, (newCode) => { - if (editor.getValue() == newCode) return; + if (!editor || editor.getValue() === newCode) return; editor.getModel().setValue(newCode); }); -watch(language, () => { - monaco.editor.setModelLanguage(editor.getModel(), language.value); +watch(language, (newLang) => { + if (!editor) return; + const model = editor.getModel(); + if (model.getLanguageId() === newLang) return; + + // Dispose old LSP sync before changing language + if (lspSyncDisposable) { + lspSyncDisposable.dispose(); + lspSyncDisposable = null; + } + + // Change language + monaco.editor.setModelLanguage(model, newLang); + + // Re-sync if new language is Python + if (newLang === "python") { + try { + lspSyncDisposable = syncModelWithLSP(model); + } catch (error) { + logger.error("Failed to re-sync model with LSP:", error); + } + } +}); + +watch(diagnosticsEnabled, (enabled) => { + if ( + !editor || + props.language !== "python" || + props.variant === "single-line" + ) { + return; + } + + const model = editor.getModel(); + if (model && language.value === "python") { + diagnosticsDisposable?.dispose(); + if (enabled) { + diagnosticsDisposable = setupLSPDiagnostics(monaco); + } + model.setValue(model.getValue()); + } +}); + +// Watch AI completion setting changes +watch(aiCompletionEnabled, (enabled) => { + if ( + !editor || + props.language !== "python" || + props.variant === "single-line" + ) + return; + + if (enabled) { + // Enable AI completion + if (!monacopilotCleanup) { + try { + monacopilotCleanup = useMonacopilot( + monaco, + editor, + props.language, + ); + } catch (error) { + logger.error("Failed to enable AI completion:", error); + } + } + } else { + // Disable AI completion + if (monacopilotCleanup) { + monacopilotCleanup(); + monacopilotCleanup = null; + } + } }); -onMounted(() => { - editor = monaco.editor.create(editorContainerEl.value, { - value: modelValue.value ?? "", - language: props.language, +onMounted(async () => { + // Create model with proper URI for LSP + const modelUri = monaco.Uri.parse(`inmemory://model/${Date.now()}.py`); + const model = monaco.editor.createModel( + modelValue.value ?? "", + props.language || "python", + props.language === "python" ? modelUri : undefined, + ); + + editor = monaco.editor.create(editorContainerEl.value as HTMLElement, { + model: model, readOnly: props.disabled, fixedOverflowWidgets: true, + quickSuggestions: { + other: true, + comments: true, + strings: true, + }, ...VARIANTS_SETTINGS[props.variant], }); - editor.getModel().onDidChangeContent(() => { + + model.onDidChangeContent(() => { const newCode = editor.getValue(); emit("update:modelValue", newCode); }); - resizeObserver.observe(rootEl.value); + + resizeObserver.observe(rootEl.value as Element); + + // Manually sync model with LSP for Python language + // This is required because we're in a browser (no filesystem) + if (props.language === "python" && props.variant !== "single-line") { + try { + lspSyncDisposable = syncModelWithLSP(model); + } catch (error) { + logger.error("Failed to sync model with LSP:", error); + } + + // Register AI-powered code completions (if AI completion enabled) + if (aiCompletionEnabled.value) { + try { + monacopilotCleanup = useMonacopilot( + monaco, + editor, + props.language, + ); + } catch (error) { + logger.error("Failed to initialize monacopilot:", error); + } + } + + diagnosticsDisposable?.dispose(); + if (diagnosticsEnabled.value) { + diagnosticsDisposable = setupLSPDiagnostics(monaco); + } + } // when in modal, focus the editor and set the cursor to the last line if (props.variant === "half-screen") { editor.focus(); editor.setPosition({ - lineNumber: editor.getModel().getLineCount(), - column: editor - .getModel() - .getLineLastNonWhitespaceColumn( - editor.getModel().getLineCount(), - ), + lineNumber: model.getLineCount(), + column: model.getLineLastNonWhitespaceColumn(model.getLineCount()), }); } }); @@ -135,7 +258,32 @@ function updateDimensions() { } onUnmounted(() => { - editor.dispose(); + // Clean up LSP sync + if (lspSyncDisposable) { + lspSyncDisposable.dispose(); + lspSyncDisposable = null; + } + + const model = editor?.getModel(); + + // Clear diagnostics before disposing model + if (model && language.value === "python") { + setModelDiagnostics(monaco, model, []); + } + + if (editor) { + editor.dispose(); + } + if (model) { + model.dispose(); + } + if (monacopilotCleanup) { + monacopilotCleanup(); + } + if (diagnosticsDisposable) { + diagnosticsDisposable.dispose(); + diagnosticsDisposable = null; + } resizeObserver.disconnect(); }); diff --git a/src/ui/src/builder/BuilderHeaderMoreDropdown.vue b/src/ui/src/builder/BuilderHeaderMoreDropdown.vue index dcff8cc2b..9b602121b 100644 --- a/src/ui/src/builder/BuilderHeaderMoreDropdown.vue +++ b/src/ui/src/builder/BuilderHeaderMoreDropdown.vue @@ -31,13 +31,22 @@ import { computed, inject, ref, useTemplateRef } from "vue"; import injectionKeys from "@/injectionKeys"; import { useToasts } from "./useToast"; import { useWriterTracking } from "@/composables/useWriterTracking"; +import { useCodeEditorSettings } from "@/composables/useCodeEditorSettings"; import SharedMoreDropdown, { Option, } from "@/components/shared/SharedMoreDropdown.vue"; import WdsModal, { ModalAction } from "@/wds/WdsModal.vue"; import WdsCheckbox from "@/wds/WdsCheckbox.vue"; +const { diagnosticsEnabled, aiCompletionEnabled } = useCodeEditorSettings(); + const options = computed(() => [ + // Project Management Section + { + type: "header", + value: "project-management-header", + label: "General", + }, { label: "Import agent .zip file", value: "import", icon: "upload" }, { label: "Download agent .zip file", value: "export", icon: "download" }, { @@ -47,6 +56,28 @@ const options = computed(() => [ value: "awake", icon: "coffee", }, + // Divider + { type: "divider", value: "divider-1" }, + // Code Settings Section + { + type: "header", + value: "code-settings-header", + label: "Code Editor Settings", + }, + { + type: "switch", + value: "diagnostics", + label: "Code Linting", + detail: "Show diagnostics in editor", + checked: diagnosticsEnabled.value, + }, + { + type: "switch", + value: "ai-completion", + label: "AI Code Completion", + detail: "Enable AI-powered suggestions", + checked: aiCompletionEnabled.value, + }, ]); const wf = inject(injectionKeys.core); @@ -154,6 +185,13 @@ async function onSelect(key: string) { break; case "awake": socketTimeout?.togglePreventTaskId("stayAwake"); + break; + case "diagnostics": + diagnosticsEnabled.value = !diagnosticsEnabled.value; + break; + case "ai-completion": + aiCompletionEnabled.value = !aiCompletionEnabled.value; + break; } } diff --git a/src/ui/src/builder/builderEditorWorker.ts b/src/ui/src/builder/builderEditorWorker.ts index 5b847bfb3..1abc1d202 100644 --- a/src/ui/src/builder/builderEditorWorker.ts +++ b/src/ui/src/builder/builderEditorWorker.ts @@ -1,27 +1,34 @@ -import * as monaco from 'monaco-editor'; -import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; -import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'; -import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'; -import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'; -import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'; +import * as monaco from "monaco-editor"; +import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"; +import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker"; +import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker"; +import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker"; +import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"; -// @ts-ignore +if (!monaco.languages.getLanguages().some((lang) => lang.id === "python")) { + monaco.languages.register({ id: "python" }); +} + +// MonacoEnvironment global configuration self.MonacoEnvironment = { - getWorker(_: any, label: string) { - if (label === 'json') { + getWorker(_moduleId: string, label: string) { + // Fallback to built-in Monaco workers for standard languages + if (label === "json") { return new jsonWorker(); } - if (label === 'css' || label === 'scss' || label === 'less') { + if (label === "css" || label === "scss" || label === "less") { return new cssWorker(); } - if (label === 'html' || label === 'handlebars' || label === 'razor') { + if (label === "html" || label === "handlebars" || label === "razor") { return new htmlWorker(); } - if (label === 'typescript' || label === 'javascript') { + if (label === "typescript" || label === "javascript") { return new tsWorker(); } + + // Default editor worker return new editorWorker(); - } + }, }; monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true); diff --git a/src/ui/src/builder/lsp/lspClient.ts b/src/ui/src/builder/lsp/lspClient.ts new file mode 100644 index 000000000..f704e7e5a --- /dev/null +++ b/src/ui/src/builder/lsp/lspClient.ts @@ -0,0 +1,335 @@ +/** + * Language Server Protocol (LSP) client for Python language support. + * + * This module manages the WebSocket connection to the python-lsp-server (pylsp) + * and integrates it with Monaco editor for enhanced Python language features. + */ + +import { + WebSocketMessageReader, + WebSocketMessageWriter, + toSocket, +} from "vscode-ws-jsonrpc"; +import { CloseAction, ErrorAction } from "vscode-languageclient/browser.js"; +import { MonacoLanguageClient } from "monaco-languageclient"; +import { useLogger } from "../../composables/useLogger.js"; +import { registerLSPClient } from "./lspClientRegistry.js"; + +const logger = useLogger(); + +interface LSPConfig { + enabled: boolean; + port: number | null; + host: string | null; +} + +let languageClient: MonacoLanguageClient | null = null; +let webSocket: WebSocket | null = null; +let clientReadyPromise: Promise | null = null; +let clientReadyResolve: (() => void) | null = null; +let isInitializing = false; + +/** + * Constructs the WebSocket URL for the LSP server. + * Uses the same pattern as the main WebSocket connection in core/index.ts. + * + * @param port - The port the LSP server is running on + * @returns WebSocket URL + * + * Note: The host parameter from config is intentionally ignored. We always + * use the current window.location.hostname to ensure the WebSocket connects + * to the same host serving the web application, which is critical for + * browser security policies and CORS. + */ +function constructWebSocketURL(port: number): string { + // Use relative URL construction like the main WebSocket + const url = new URL(window.location.href); + url.protocol = url.protocol.replace("https", "wss"); + url.protocol = url.protocol.replace("http", "ws"); + url.port = port.toString(); + url.pathname = "/"; // LSP server is at the root + url.hash = ""; // Remove fragment identifier (WebSocket doesn't allow it) + url.search = ""; // Remove query string + return url.href; +} + +/** + * Fetches the LSP configuration from the backend API. + * + * @returns LSP configuration or null if fetch fails + */ +async function fetchLSPConfig(): Promise { + try { + const response = await fetch("/api/lsp-config"); + if (!response.ok) { + logger.warn("LSP config endpoint not available:", response.status); + return null; + } + const config: LSPConfig = await response.json(); + return config; + } catch (error) { + logger.error("Failed to fetch LSP config:", error); + return null; + } +} + +/** + * Creates a Monaco Language Client instance. + * + * @param reader - WebSocket message reader + * @param writer - WebSocket message writer + * @returns Configured MonacoLanguageClient + */ +const createLanguageClient = ( + reader: WebSocketMessageReader, + writer: WebSocketMessageWriter, +): MonacoLanguageClient => + new MonacoLanguageClient({ + name: "Python Language Client", + clientOptions: { + // Document selector for Python files + documentSelector: [ + { language: "python" }, + { scheme: "inmemory", language: "python" }, + { pattern: "**/*.py" }, + ], + // Disable default error handler to prevent client restart loops + errorHandler: { + error: () => ({ action: ErrorAction.Continue }), + closed: () => ({ action: CloseAction.DoNotRestart }), + }, + // Synchronize settings with server + synchronize: { + fileEvents: [], + }, + }, + // Provide message transports directly + messageTransports: { reader, writer }, + }); + +/** + * Initializes the WebSocket connection and starts the language client. + * + * @param url - WebSocket URL for the LSP server + * @returns WebSocket instance or null if connection fails + */ +function initWebSocketAndStartClient(url: string): WebSocket | null { + try { + const ws = new WebSocket(url); + + // Create a promise that resolves when the client is fully ready + clientReadyPromise = new Promise((resolve) => { + clientReadyResolve = resolve; + }); + + const resolveClientReady = () => { + if (clientReadyResolve) { + clientReadyResolve(); + clientReadyResolve = null; + } + }; + + ws.onopen = () => { + // Python LSP client connected + + // Create message transports + const socket = toSocket(ws); + const reader = new WebSocketMessageReader(socket); + const writer = new WebSocketMessageWriter(socket); + + // Create and start language client + const client = createLanguageClient(reader, writer); + + languageClient = client; + registerLSPClient(client); // Register in the registry + + // Start the client and wait for it to be ready + client + .start() + .then(() => { + // Python LSP client ready + + // Send workspace configuration to enable diagnostics + // Configure to use flake8 instead of pyflakes so we can use builtins config + client + .sendNotification("workspace/didChangeConfiguration", { + settings: { + pylsp: { + // Use flake8 as configuration source instead of default pycodestyle + configurationSources: ["flake8"], + plugins: { + // Disable default linters (pyflakes, pycodestyle, mccabe) + // to avoid duplicate messages from flake8 + pyflakes: { enabled: false }, + pycodestyle: { enabled: false }, + mccabe: { enabled: false }, + // Enable flake8 which will read setup.cfg + flake8: { enabled: true }, + // Import organization + isort: { enabled: true }, + // Code intelligence + jedi: { enabled: true }, + }, + }, + }, + }) + .then(() => { + // Sent workspace configuration to pylsp + // Resolve the ready promise after configuration is sent + resolveClientReady(); + }) + .catch((error) => { + logger.error( + "Failed to send workspace config:", + error, + ); + // Still resolve even if config fails + resolveClientReady(); + }); + }) + .catch((error) => { + logger.error("Failed to start LSP client:", error); + // Resolve promise even on error + resolveClientReady(); + }); + + // Stop client when connection closes + reader.onClose(() => { + client.stop(); + }); + }; + + ws.onerror = (error) => { + logger.error("LSP WebSocket error:", error); + }; + + ws.onclose = () => { + // Resolve clientReadyPromise if it was never resolved + // This prevents hanging when WebSocket fails to connect + if (clientReadyResolve) { + clientReadyResolve(); + clientReadyResolve = null; + } + + if (languageClient) { + languageClient.stop(); + languageClient = null; + registerLSPClient(null); // Unregister from the registry + } + webSocket = null; + clientReadyPromise = null; + }; + + return ws; + } catch (error) { + logger.error("Failed to create LSP WebSocket connection:", error); + return null; + } +} + +/** + * Initializes the Python LSP client. + * This should be called when the application starts in edit mode. + * Will retry if the server is not ready yet. + * + * @param retryCount - Internal retry counter + * @returns Promise that resolves to true if initialization succeeds, false otherwise + */ +export async function initializeLSPClient(retryCount = 0): Promise { + const MAX_RETRIES = 3; + const RETRY_DELAY = 1000; // 1 second + + // Prevent concurrent initialization attempts + if (isInitializing) { + logger.log("LSP client initialization already in progress, waiting..."); + // Wait for current initialization to complete + if (clientReadyPromise) { + await clientReadyPromise; + } + return languageClient !== null; + } + + // Check if already initialized + if (languageClient !== null) { + return true; + } + + isInitializing = true; + + try { + // Fetch LSP configuration + const config = await fetchLSPConfig(); + + if (!config || !config.enabled || !config.port) { + if (retryCount < MAX_RETRIES) { + logger.log( + `LSP server not ready, retrying (${retryCount + 1}/${MAX_RETRIES})...`, + ); + await new Promise((resolve) => + setTimeout(resolve, RETRY_DELAY), + ); + isInitializing = false; + return initializeLSPClient(retryCount + 1); + } + isInitializing = false; + return false; + } + + logger.log("Initializing Python LSP client..."); + + // Construct WebSocket URL using current host + const websocketUrl = constructWebSocketURL(config.port); + + // Initialize WebSocket connection + webSocket = initWebSocketAndStartClient(websocketUrl); + + if (webSocket === null) { + isInitializing = false; + return false; + } + + // Wait for the client to be fully ready (including config sent) + if (clientReadyPromise) { + await clientReadyPromise; + } + + isInitializing = false; + return true; + } catch (error) { + isInitializing = false; + logger.error("Error during LSP client initialization:", error); + return false; + } +} + +/** + * Checks if the LSP client is currently active. + * + * @returns True if LSP client is running, false otherwise + */ +export const isLSPClientActive = (): boolean => + languageClient !== null && + webSocket !== null && + webSocket.readyState === WebSocket.OPEN; + +/** + * Gets the active language client instance. + * Used for manual document synchronization. + * + * @returns The active MonacoLanguageClient or null + */ +export const getLSPClient = (): MonacoLanguageClient | null => languageClient; + +/** + * Stops the LSP client and closes the WebSocket connection. + * This should be called when the application is shutting down or switching modes. + */ +export function stopLSPClient(): void { + isInitializing = false; + languageClient?.stop(); + languageClient = null; + registerLSPClient(null); // Unregister from the registry + + webSocket?.close(); + webSocket = null; +} diff --git a/src/ui/src/builder/lsp/lspClientRegistry.ts b/src/ui/src/builder/lsp/lspClientRegistry.ts new file mode 100644 index 000000000..70a2b243e --- /dev/null +++ b/src/ui/src/builder/lsp/lspClientRegistry.ts @@ -0,0 +1,27 @@ +/** + * LSP Client Registry - Lightweight module for accessing the LSP client instance. + * + * This module is separate from lspClient.ts to avoid importing heavy dependencies + * (monaco-languageclient, vscode-languageclient) in modules that only need to + * access an already-initialized client. + */ + +import type { MonacoLanguageClient } from "monaco-languageclient"; + +let languageClient: MonacoLanguageClient | null = null; + +/** + * Registers the LSP client instance. + * Called by lspClient.ts after initialization. + */ +export function registerLSPClient(client: MonacoLanguageClient | null): void { + languageClient = client; +} + +export function getLSPClient(): MonacoLanguageClient | null { + return languageClient; +} + +export function isLSPClientAvailable(): boolean { + return languageClient !== null; +} diff --git a/src/ui/src/builder/lsp/lspCompletionProvider.ts b/src/ui/src/builder/lsp/lspCompletionProvider.ts new file mode 100644 index 000000000..7620cf048 --- /dev/null +++ b/src/ui/src/builder/lsp/lspCompletionProvider.ts @@ -0,0 +1,284 @@ +/** + * Manual LSP Completion Provider for Monaco Editor + * + * This provider manually bridges Monaco Editor's completion API with the LSP client. + * This is needed when using monaco-vscode-api if the automatic wiring doesn't work. + */ + +import type * as monaco from "monaco-editor"; +import { getLSPClient } from "./lspClientRegistry.js"; +import { useLogger } from "../../composables/useLogger.js"; + +type LSPCompletionItem = { + label: string; + kind?: number; + detail?: string; + documentation?: string; + insertText?: string; + insertTextFormat?: number; // 1 = PlainText, 2 = Snippet + sortText?: string; + filterText?: string; + textEdit?: { + newText: string; + range: unknown; + }; +}; + +const logger = useLogger(); + +/** + * Common Python snippets to augment LSP completions. + * These provide useful code templates with tab stops. + */ +/* eslint-disable no-template-curly-in-string */ +const PYTHON_SNIPPETS: Record< + string, + { + label: string; + snippet: string; + detail: string; + documentation: string; + } +> = { + class: { + label: "class", + snippet: + "class ${1:ClassName}:\n\tdef __init__(self, ${2:args}):\n\t\t${0:pass}", + detail: "Class definition", + documentation: "Create a new class with __init__ method", + }, + def: { + label: "def", + snippet: "def ${1:function_name}(${2:args}):\n\t${0:pass}", + detail: "Function definition", + documentation: "Create a new function", + }, + for: { + label: "for", + snippet: "for ${1:item} in ${2:iterable}:\n\t${0:pass}", + detail: "For loop", + documentation: "Iterate over an iterable", + }, + while: { + label: "while", + snippet: "while ${1:condition}:\n\t${0:pass}", + detail: "While loop", + documentation: "Loop while condition is true", + }, + if: { + label: "if", + snippet: "if ${1:condition}:\n\t${0:pass}", + detail: "If statement", + documentation: "Conditional statement", + }, + elif: { + label: "elif", + snippet: "elif ${1:condition}:\n\t${0:pass}", + detail: "Elif statement", + documentation: "Else-if conditional", + }, + else: { + label: "else", + snippet: "else:\n\t${0:pass}", + detail: "Else statement", + documentation: "Else clause", + }, + try: { + label: "try", + snippet: + "try:\n\t${1:pass}\nexcept ${2:Exception} as ${3:e}:\n\t${0:pass}", + detail: "Try-except block", + documentation: "Exception handling", + }, + with: { + label: "with", + snippet: "with ${1:expression} as ${2:variable}:\n\t${0:pass}", + detail: "With statement", + documentation: "Context manager", + }, + main: { + label: "if __name__ == '__main__'", + snippet: 'if __name__ == "__main__":\n\t${0:pass}', + detail: "Main guard", + documentation: "Python main entry point", + }, +}; +/* eslint-enable no-template-curly-in-string */ + +/** + * Registers a manual completion provider that forwards requests to the LSP client. + * + * @param monacoInstance - The Monaco Editor API instance + * @returns Disposable to unregister the provider + */ +export function registerLSPCompletionProvider( + monacoInstance: typeof monaco, +): monaco.IDisposable { + return monacoInstance.languages.registerCompletionItemProvider("python", { + provideCompletionItems: async (model, position, context) => { + const lspClient = getLSPClient(); + if (!lspClient) { + return { suggestions: [] }; + } + + const uri = model.uri.toString(); + + try { + // Send textDocument/completion request to LSP server + const result: unknown = await lspClient.sendRequest( + "textDocument/completion", + { + textDocument: { uri }, + position: { + line: position.lineNumber - 1, // Monaco is 1-based, LSP is 0-based + character: position.column - 1, + }, + context: { + triggerKind: context.triggerKind, + triggerCharacter: context.triggerCharacter, + }, + }, + ); + + // Convert LSP completion items to Monaco completion items + const items = Array.isArray(result) + ? result + : (result as { items?: unknown[] })?.items || []; + + const lspSuggestions = items.map((item: unknown) => { + const completionItem = item as LSPCompletionItem; + + // Use textEdit.newText if available, otherwise insertText or label + const insertText = + completionItem.textEdit?.newText || + completionItem.insertText || + completionItem.label; + + // Check if this is a snippet (insertTextFormat === 2) + const isSnippet = completionItem.insertTextFormat === 2; + + const insertTextRule = isSnippet + ? monacoInstance.languages.CompletionItemInsertTextRule + .InsertAsSnippet + : undefined; + + return { + label: completionItem.label, + kind: convertCompletionItemKind(completionItem.kind), + detail: completionItem.detail, + documentation: completionItem.documentation, + insertText: insertText, + insertTextRules: insertTextRule, + range: undefined, // Let Monaco handle the range + sortText: completionItem.sortText, + filterText: completionItem.filterText, + }; + }); + + // Add custom Python snippets + const snippetSuggestions = Object.values(PYTHON_SNIPPETS).map( + (snippet) => { + const kind = + monacoInstance.languages.CompletionItemKind.Snippet; + const insertTextRules = + monacoInstance.languages + .CompletionItemInsertTextRule.InsertAsSnippet; + return { + label: snippet.label, + kind: kind, + detail: snippet.detail, + documentation: snippet.documentation, + insertText: snippet.snippet, + insertTextRules: insertTextRules, + range: undefined, + sortText: `_${snippet.label}`, // Sort snippets near the top + }; + }, + ); + + // Combine LSP suggestions with custom snippets + const allSuggestions = [ + ...lspSuggestions, + ...snippetSuggestions, + ]; + + return { suggestions: allSuggestions }; + } catch (error) { + logger.error("Failed to get LSP completions:", error); + return { suggestions: [] }; + } + }, + }); +} + +/** + * Converts LSP CompletionItemKind to Monaco CompletionItemKind. + */ +function convertCompletionItemKind( + lspKind: number | undefined, +): monaco.languages.CompletionItemKind { + // Import monaco dynamically to avoid circular dependencies + const monacoInstance = (window as { monaco?: typeof monaco }).monaco; + if (!monacoInstance) { + return 0; // Text + } + + const CompletionItemKind = monacoInstance.languages.CompletionItemKind; + + // LSP CompletionItemKind mapping + // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemKind + switch (lspKind) { + case 1: + return CompletionItemKind.Text; + case 2: + return CompletionItemKind.Method; + case 3: + return CompletionItemKind.Function; + case 4: + return CompletionItemKind.Constructor; + case 5: + return CompletionItemKind.Field; + case 6: + return CompletionItemKind.Variable; + case 7: + return CompletionItemKind.Class; + case 8: + return CompletionItemKind.Interface; + case 9: + return CompletionItemKind.Module; + case 10: + return CompletionItemKind.Property; + case 11: + return CompletionItemKind.Unit; + case 12: + return CompletionItemKind.Value; + case 13: + return CompletionItemKind.Enum; + case 14: + return CompletionItemKind.Keyword; + case 15: + return CompletionItemKind.Snippet; + case 16: + return CompletionItemKind.Color; + case 17: + return CompletionItemKind.File; + case 18: + return CompletionItemKind.Reference; + case 19: + return CompletionItemKind.Folder; + case 20: + return CompletionItemKind.EnumMember; + case 21: + return CompletionItemKind.Constant; + case 22: + return CompletionItemKind.Struct; + case 23: + return CompletionItemKind.Event; + case 24: + return CompletionItemKind.Operator; + case 25: + return CompletionItemKind.TypeParameter; + default: + return CompletionItemKind.Text; + } +} diff --git a/src/ui/src/builder/lsp/lspDiagnostics.ts b/src/ui/src/builder/lsp/lspDiagnostics.ts new file mode 100644 index 000000000..d2bfe9c7d --- /dev/null +++ b/src/ui/src/builder/lsp/lspDiagnostics.ts @@ -0,0 +1,176 @@ +import type * as monaco from "monaco-editor"; +import { getLSPClient } from "./lspClientRegistry.js"; +import { useLogger } from "../../composables/useLogger.js"; + +const logger = useLogger(); + +/** + * Writer framework globals that are available in code blocks and event handlers. + * These should not be flagged as undefined by the linter. + */ +const WRITER_FRAMEWORK_GLOBALS = new Set([ + // Event handler globals + "state", + "payload", + "context", + "session", + "ui", + "blueprint_runner", + "vault", + // Code block globals + "set_output", + "result", + "results", + "logger", + // IfElse block globals + "set_outcome", +]); + +/** + * LSP Diagnostic Severity levels + */ +enum DiagnosticSeverity { + Error = 1, + Warning = 2, + Information = 3, + Hint = 4, +} + +type LSPDiagnostic = { + uri: string; + diagnostics: Array<{ + range: { + start: { line: number; character: number }; + end: { line: number; character: number }; + }; + severity: number; + message: string; + source?: string; + code?: string | number; + }>; +}; + +/** + * Checks if a diagnostic is a false positive for Writer framework globals. + * Returns true if this diagnostic should be filtered out. + */ +function isWriterFrameworkGlobalError(diagnostic: { + message: string; + code?: string | number; + source?: string; +}): boolean { + // Check if it's an undefined name error (F821 from pyflakes or flake8) + if (diagnostic.source === "pyflakes" || diagnostic.source === "flake8") { + if ( + diagnostic.code === "F821" || + diagnostic.message.includes("undefined name") + ) { + // Extract the variable name from the message + // Messages are like: "undefined name 'state'" or "F821 undefined name 'state'" + const match = diagnostic.message.match(/undefined name '([^']+)'/); + if (match?.[1]) { + const varName = match[1]; + return WRITER_FRAMEWORK_GLOBALS.has(varName); + } + } + } + return false; +} + +/** + * Converts LSP diagnostic severity to Monaco marker severity. + */ +function convertSeverity( + lspSeverity: number, + monacoInstance: typeof monaco, +): monaco.MarkerSeverity { + switch (lspSeverity) { + case DiagnosticSeverity.Error: + return monacoInstance.MarkerSeverity.Error; + case DiagnosticSeverity.Warning: + return monacoInstance.MarkerSeverity.Warning; + case DiagnosticSeverity.Information: + return monacoInstance.MarkerSeverity.Info; + case DiagnosticSeverity.Hint: + return monacoInstance.MarkerSeverity.Hint; + default: + return monacoInstance.MarkerSeverity.Error; + } +} + +/** + * Sets up a listener for LSP diagnostics and displays them as Monaco markers. + * + * @param monacoInstance - The Monaco Editor API instance + * @returns Disposable to unregister the listener + */ +export function setupLSPDiagnostics( + monacoInstance: typeof monaco, +): monaco.IDisposable { + const lspClient = getLSPClient(); + if (!lspClient) { + logger.warn("LSP client not available for diagnostics"); + return { dispose: () => {} }; + } + + // Listen for textDocument/publishDiagnostics notifications from LSP server + const disposable = lspClient.onNotification( + "textDocument/publishDiagnostics", + (params: LSPDiagnostic) => { + try { + const uri = monacoInstance.Uri.parse(params.uri); + const model = monacoInstance.editor.getModel(uri); + + if (!model) { + // Model might have been closed, this is normal + return; + } + + // Convert LSP diagnostics to Monaco markers + const markers: monaco.editor.IMarkerData[] = params.diagnostics + // Filter out false positives for Writer framework globals + .filter( + (diagnostic) => + !isWriterFrameworkGlobalError(diagnostic), + ) + .map((diagnostic) => ({ + severity: convertSeverity( + diagnostic.severity, + monacoInstance, + ), + message: diagnostic.message, + source: diagnostic.source || "pylsp", + code: diagnostic.code?.toString(), + startLineNumber: diagnostic.range.start.line + 1, // LSP is 0-based, Monaco is 1-based + startColumn: diagnostic.range.start.character + 1, + endLineNumber: diagnostic.range.end.line + 1, + endColumn: diagnostic.range.end.character + 1, + })); + + // Set markers on the model (this displays red/yellow squiggly lines) + monacoInstance.editor.setModelMarkers( + model, + "pylsp", // Owner ID + markers, + ); + } catch (error) { + logger.error("Failed to process LSP diagnostics:", error); + } + }, + ); + + return disposable; +} + +/** + * Sets the diagnostics for a specific model. + * + * @param monacoInstance - The Monaco Editor API instance + * @param model - The Monaco model to set diagnostics for + * @param markers - The markers to set for the model + */ +export const setModelDiagnostics = ( + monacoInstance: typeof monaco, + model: monaco.editor.ITextModel, + markers: monaco.editor.IMarkerData[], +) => monacoInstance.editor.setModelMarkers(model, "pylsp", markers); diff --git a/src/ui/src/builder/lsp/lspModelSync.ts b/src/ui/src/builder/lsp/lspModelSync.ts new file mode 100644 index 000000000..2e18f2ea7 --- /dev/null +++ b/src/ui/src/builder/lsp/lspModelSync.ts @@ -0,0 +1,99 @@ +/** + * Manual LSP Document Synchronization Helper + * + * This module provides manual textDocument/didOpen and textDocument/didChange + * synchronization for Monaco Editor models with the LSP client. + * + * Required when using Monaco Editor in a browser environment (no filesystem) + * where automatic document sync may not work properly. + * + * Based on: https://peerprep.dev/tech-blog/lsp + */ + +import type * as monaco from "monaco-editor"; +import { useLogger } from "../../composables/useLogger.js"; +import { getLSPClient } from "./lspClientRegistry.js"; + +const logger = useLogger(); + +/** + * Manually synchronizes a Monaco model with the LSP server. + * Sends textDocument/didOpen when the model is created and + * textDocument/didChange when the content changes. + * + * @param model - The Monaco editor model to synchronize + * @returns Disposable to stop synchronization + */ +export function syncModelWithLSP( + model: monaco.editor.ITextModel, +): monaco.IDisposable { + const lspClient = getLSPClient(); + if (!lspClient) { + return { dispose: () => {} }; + } + + const uri = model.uri.toString(); + const languageId = model.getLanguageId(); + + let timeoutId: ReturnType | null = null; + + // Wait a bit for the LSP client to be fully ready + timeoutId = setTimeout(() => { + timeoutId = null; + // Send textDocument/didOpen + lspClient + .sendNotification("textDocument/didOpen", { + textDocument: { + uri, + languageId, + version: model.getVersionId(), + text: model.getValue(), + }, + }) + .catch((error: unknown) => { + logger.error("Failed to send textDocument/didOpen:", error); + }); + }, 100); + + // Listen for content changes and send textDocument/didChange + const changeDisposable = model.onDidChangeContent(() => { + lspClient + .sendNotification("textDocument/didChange", { + textDocument: { + uri, + version: model.getVersionId(), + }, + contentChanges: [ + { + text: model.getValue(), + }, + ], + }) + .catch((error: unknown) => { + logger.error("Failed to send textDocument/didChange:", error); + }); + }); + + // Return disposable that sends textDocument/didClose and stops listening + return { + dispose: () => { + // Clear pending timeout to prevent didOpen after didClose + if (timeoutId !== null) { + clearTimeout(timeoutId); + timeoutId = null; + } + + changeDisposable.dispose(); + lspClient + .sendNotification("textDocument/didClose", { + textDocument: { uri }, + }) + .catch((error: unknown) => { + logger.error( + "Failed to send textDocument/didClose:", + error, + ); + }); + }, + }; +} diff --git a/src/ui/src/builder/lsp/lspSetup.ts b/src/ui/src/builder/lsp/lspSetup.ts new file mode 100644 index 000000000..9513d9d66 --- /dev/null +++ b/src/ui/src/builder/lsp/lspSetup.ts @@ -0,0 +1,97 @@ +/** + * LSP Setup and Initialization + * + * Handles all VSCode services and LSP client initialization for Python language support. + */ + +import { initialize as initializeVSCodeServices } from "@codingame/monaco-vscode-api/services"; +import { initializeLSPClient, stopLSPClient } from "./lspClient.js"; +import { registerLSPCompletionProvider } from "./lspCompletionProvider.js"; +import { useLogger } from "../../composables/useLogger.js"; +import type * as monaco from "monaco-editor"; + +const logger = useLogger(); + +let vscodeServicesReady = false; +let completionProviderDisposable: monaco.IDisposable | null = null; + +/** + * Initialize VSCode services required for LSP. + * This sets up the minimal services needed for monaco-vscode-api integration. + * + * @returns Promise that resolves to true if successful + */ +async function initializeVSCodeServicesForLSP(): Promise { + if (vscodeServicesReady) return true; + + try { + // Import required service overrides for LSP support + // Note: We only import TextMate (syntax highlighting) and Languages (LSP) services + const getTextMateServiceOverride = ( + await import("@codingame/monaco-vscode-textmate-service-override") + ).default; + const getLanguagesServiceOverride = ( + await import("@codingame/monaco-vscode-languages-service-override") + ).default; + + // Initialize with minimal services + await initializeVSCodeServices({ + ...getTextMateServiceOverride(), + ...getLanguagesServiceOverride(), + }); + + vscodeServicesReady = true; + return true; + } catch (error) { + logger.error("Failed to initialize services for LSP:", error); + return false; + } +} + +/** + * Complete LSP setup: Initialize VSCode services, LSP client, and completion provider. + * This is the main entry point for setting up Python language support. + * + * @returns Promise that resolves to true if setup was successful + */ +export async function setupLSP(): Promise { + try { + // Step 1: Initialize VSCode services + const servicesReady = await initializeVSCodeServicesForLSP(); + if (!servicesReady) { + return false; + } + + // Step 2: Wait for DOM to be fully ready + await new Promise((resolve) => setTimeout(resolve, 100)); + + // Step 3: Initialize LSP client (now waits for full initialization) + const lspInitialized = await initializeLSPClient(); + if (!lspInitialized) { + return false; + } + + // Step 4: Register completion provider + // Note: Diagnostics are automatically handled via LSP notifications + // when the client connects - no explicit setup needed here + const monaco = await import("monaco-editor"); + completionProviderDisposable = registerLSPCompletionProvider(monaco); + + logger.log("Python LSP setup complete"); + return true; + } catch (error) { + logger.error("Failed to setup LSP:", error); + return false; + } +} + +/** + * Cleanup LSP resources. + */ +export function cleanupLSP() { + if (completionProviderDisposable) { + completionProviderDisposable.dispose(); + completionProviderDisposable = null; + } + stopLSPClient(); +} diff --git a/src/ui/src/components/shared/SharedMoreDropdown.vue b/src/ui/src/components/shared/SharedMoreDropdown.vue index 07b2bcf53..f73812c9f 100644 --- a/src/ui/src/components/shared/SharedMoreDropdown.vue +++ b/src/ui/src/components/shared/SharedMoreDropdown.vue @@ -106,6 +106,16 @@ watch( ); function onSelect(value: string) { + // Don't close dropdown for switch interactions + // These option types should stay open for better UX + const option = props.options.find((opt) => opt.value === value); + if (option && option.type === "switch") { + // Keep dropdown open, just emit the event + emits("select", value); + return; + } + + // For regular items, close the dropdown after selection isOpen.value = false; emits("select", value); } diff --git a/src/ui/src/composables/useCodeEditorSettings.ts b/src/ui/src/composables/useCodeEditorSettings.ts new file mode 100644 index 000000000..ecfb81775 --- /dev/null +++ b/src/ui/src/composables/useCodeEditorSettings.ts @@ -0,0 +1,91 @@ +/** + * Composable for managing code editor settings (diagnostics, AI completion) + * Settings are persisted to localStorage and apply immediately. + * All instances share the same reactive state. + */ +import { computed, ref, watch } from "vue"; +import { useLocalStorageJSON } from "./useStorageJSON"; + +export interface CodeEditorSettings { + diagnosticsEnabled: boolean; + aiCompletionEnabled: boolean; +} + +const DEFAULT_SETTINGS: CodeEditorSettings = { + diagnosticsEnabled: true, + aiCompletionEnabled: false, +}; + +const STORAGE_KEY = "codeEditorSettings"; + +/** + * Validates that the stored settings have the correct shape + */ +function validateSettings(value: unknown): value is CodeEditorSettings { + if (typeof value !== "object" || value === null) return false; + const obj = value as Record; + return ( + typeof obj.diagnosticsEnabled === "boolean" && + typeof obj.aiCompletionEnabled === "boolean" + ); +} + +// Shared reactive state that persists to localStorage +// This is created once and shared across all component instances +const settings = useLocalStorageJSON( + STORAGE_KEY, + validateSettings, +); + +// Initialize with defaults if not set +if (!settings.value) { + settings.value = { ...DEFAULT_SETTINGS }; +} + +// Create shared reactive refs +const diagnosticsEnabledRef = ref( + settings.value?.diagnosticsEnabled ?? DEFAULT_SETTINGS.diagnosticsEnabled, +); +const aiCompletionEnabledRef = ref( + settings.value?.aiCompletionEnabled ?? DEFAULT_SETTINGS.aiCompletionEnabled, +); + +// Sync changes to localStorage +watch(diagnosticsEnabledRef, (value) => { + settings.value = { + ...settings.value, + diagnosticsEnabled: value, + } as CodeEditorSettings; +}); + +watch(aiCompletionEnabledRef, (value) => { + settings.value = { + ...settings.value, + aiCompletionEnabled: value, + } as CodeEditorSettings; +}); + +/** + * Hook to access and modify code editor settings. + * Settings are stored in localStorage and reactive across all instances. + */ +export function useCodeEditorSettings() { + const diagnosticsEnabled = computed({ + get: () => diagnosticsEnabledRef.value, + set: (value: boolean) => { + diagnosticsEnabledRef.value = value; + }, + }); + + const aiCompletionEnabled = computed({ + get: () => aiCompletionEnabledRef.value, + set: (value: boolean) => { + aiCompletionEnabledRef.value = value; + }, + }); + + return { + diagnosticsEnabled, + aiCompletionEnabled, + }; +} diff --git a/src/ui/src/composables/useMonacopilot.ts b/src/ui/src/composables/useMonacopilot.ts new file mode 100644 index 000000000..4cf0ea753 --- /dev/null +++ b/src/ui/src/composables/useMonacopilot.ts @@ -0,0 +1,43 @@ +/** + * Composable for integrating monacopilot AI code completions + * with Monaco Editor instances. + */ +import * as monaco from "monaco-editor"; +import { registerCompletion } from "monacopilot"; + +/** + * Registers AI-powered code completion for a Monaco Editor instance. + * + * @param monacoInstance - The Monaco Editor API instance + * @param editor - The standalone code editor instance + * @param language - The programming language (e.g., 'python', 'javascript', 'typescript') + * @returns Cleanup function to unregister the completion provider + */ +export function useMonacopilot( + monacoInstance: typeof monaco, + editor: monaco.editor.IStandaloneCodeEditor, + language: string, +): () => void { + try { + // Register the completion provider with monacopilot + const disposable = registerCompletion(monacoInstance, editor, { + language: language, + // API endpoint for code completions + endpoint: "/api/code-completion", + technologies: ["writer-agent-builder"], + // Optional: Trigger completion automatically + trigger: "onIdle", + }); + + // Return cleanup function + return () => { + if (disposable && typeof disposable.deregister === "function") { + disposable.deregister(); + } + }; + } catch (error) { + console.error("Failed to register monacopilot:", error); + // Return no-op cleanup function on error + return () => {}; + } +} diff --git a/src/ui/src/main.ts b/src/ui/src/main.ts index 53571baa9..62db09f78 100644 --- a/src/ui/src/main.ts +++ b/src/ui/src/main.ts @@ -9,7 +9,7 @@ import { useLogger } from "./composables/useLogger.js"; import { useWriterApi } from "./composables/useWriterApi.js"; import { useCollaborationManager } from "./composables/useCollaborationManager.js"; import { useNotesManager } from "./core/useNotesManager.js"; -import { CollaborationManager } from "./writerTypes.js"; +import type { CollaborationManager } from "./writerTypes.js"; import { useSecretsManager } from "./core/useSecretsManager.js"; import { RECONNECT_DELAY_MS, MAX_RETRIES } from "@/constants/retry"; import { useConfigJs } from "./composables/useConfigJs.js"; @@ -29,11 +29,12 @@ async function load() { await wf.init(); const mode = wf.mode.value; - const wfbm = mode == "edit" ? generateBuilderManager() : undefined; + + const wfbm = mode === "edit" ? generateBuilderManager() : undefined; const notesManager = useNotesManager(wf, wfbm); - const secretsManager = mode == "edit" ? useSecretsManager(wf) : undefined; + const secretsManager = mode === "edit" ? useSecretsManager(wf) : undefined; const collaborationManager = - mode == "edit" ? useCollaborationManager(wf) : undefined; + mode === "edit" ? useCollaborationManager(wf) : undefined; if (wfbm) { wf.addMailSubscription("logEntry", wfbm.handleLogEntry); @@ -62,6 +63,39 @@ async function load() { app.mount("#app"); + // Initialize Monaco editor workers and LSP client in edit mode + if (mode === "edit") { + // Setup Monaco editor workers first - MUST be synchronous to ensure + // MonacoEnvironment is set before any editor instances are created + try { + await import("./builder/builderEditorWorker.js"); + logger.log("Monaco editor workers initialized"); + } catch (error) { + logger.error("Failed to initialize Monaco workers:", error); + } + + // Then initialize LSP + try { + const { setupLSP, cleanupLSP } = await import( + "./builder/lsp/lspSetup.js" + ); + await setupLSP(); + + // Setup cleanup on browser unload (tab close, refresh, navigation away) + window.addEventListener("beforeunload", () => { + cleanupLSP(); + if (collaborationManager) { + collaborationManager.updateOutgoingPing({ + action: "leave", + }); + collaborationManager.sendCollaborationPing(); + } + }); + } catch (error) { + logger.error("Failed to initialize LSP:", error); + } + } + const { loadConfigJs } = useConfigJs(wf); loadConfigJs().catch(logger.error); @@ -71,7 +105,6 @@ async function load() { if (wf.isWriterCloudApp.value && secretsManager) { secretsManager.load().catch(logger.error); } - if ( wfbm?.activeRootId.value === "blueprints_root" && wf.activePageId.value === undefined @@ -92,10 +125,7 @@ async function enableCollaboration(collaborationManager: CollaborationManager) { }); collaborationManager.sendCollaborationPing(); collaborationManager.groomSnapshot(); - window.addEventListener("beforeunload", function () { - collaborationManager.updateOutgoingPing({ action: "leave" }); - collaborationManager.sendCollaborationPing(); - }); + // Note: beforeunload cleanup is now handled in the main load() function } async function initialise() { diff --git a/src/ui/src/wds/WdsDropdownMenu.vue b/src/ui/src/wds/WdsDropdownMenu.vue index 972fda696..189707b05 100644 --- a/src/ui/src/wds/WdsDropdownMenu.vue +++ b/src/ui/src/wds/WdsDropdownMenu.vue @@ -58,42 +58,86 @@
No results
- - - + + + + diff --git a/src/ui/vite.config.ts b/src/ui/vite.config.ts index 7ba92d5e6..6102daef1 100644 --- a/src/ui/vite.config.ts +++ b/src/ui/vite.config.ts @@ -1,13 +1,47 @@ import { fileURLToPath, URL } from "url"; -import { defineConfig, UserConfig } from "vite"; +import { defineConfig, UserConfig, Plugin } from "vite"; import vue from "@vitejs/plugin-vue"; import writerPlugin from "./viteWriterPlugin"; import postcssAssignLayer from "postcss-assign-layer"; +// Plugin to fix monaco-vscode-api internal path resolution +// The package's exports use wildcards: "./vscode/*" -> "./vscode/src/*.js" +// Rollup doesn't respect these, so we manually construct absolute paths +function fixMonacoVSCodePaths(): Plugin { + return { + name: "fix-monaco-vscode-paths", + enforce: "pre", // Run before other plugins + resolveId(source) { + // Intercept: @codingame/monaco-vscode-api/vscode/vs/... + // Transform to absolute path in node_modules + if (source.startsWith("@codingame/monaco-vscode-api/vscode/vs/")) { + // Extract the path after /vscode/vs/ + const pathAfterVs = source.slice( + "@codingame/monaco-vscode-api/vscode/vs/".length, + ); + + // Construct absolute path: ../../node_modules (from src/ui/) + const absolutePath = fileURLToPath( + new URL( + `../../node_modules/@codingame/monaco-vscode-api/vscode/src/vs/${pathAfterVs}.js`, + import.meta.url, + ), + ); + + return { + id: absolutePath, + external: false, + }; + } + return null; + }, + }; +} + // https://vitejs.dev/config/ export default defineConfig({ base: "./", - plugins: [vue(), writerPlugin()], + plugins: [vue(), writerPlugin(), fixMonacoVSCodePaths()], includeWriterComponentPath: false, define: { WRITER_LIVE_CCT: JSON.stringify("no"), @@ -34,6 +68,7 @@ export default defineConfig({ alias: { "@": fileURLToPath(new URL("./src", import.meta.url)), }, + dedupe: ["monaco-editor"], }, test: { environment: "jsdom", diff --git a/src/writer/ai/code_completion.py b/src/writer/ai/code_completion.py new file mode 100644 index 000000000..00f2fbce2 --- /dev/null +++ b/src/writer/ai/code_completion.py @@ -0,0 +1,214 @@ +""" +Code completion handler using Writer Palmyra. + +This module provides AI-powered code completions for Monaco Editor instances +via the monacopilot library. +""" +import logging +import os +from typing import Any, Dict, Optional + +logger = logging.getLogger(__name__) + + +class CodeCompletionHandler: + """ + Handles code completion requests using Writer Palmyra. + + Environment Variables: + WRITER_COPILOT_ENABLED: Set to "true" to enable completions + WRITER_API_KEY: API key for Writer AI (Palmyra models) + WRITER_COPILOT_MODEL: Model to use (default: "palmyra-x5") + """ + + def __init__(self): + """Initialize the completion handler with Writer Palmyra.""" + self.enabled = os.getenv("WRITER_COPILOT_ENABLED", "true").lower() == "true" + self.client = None + self.model = None + + if not self.enabled: + return + + # Initialize Writer provider + writer_key = os.getenv("WRITER_API_KEY") + + if writer_key: + self._init_writer(writer_key) + else: + logger.warning( + "WRITER_COPILOT_ENABLED is true but WRITER_API_KEY is not set. " + "Code completions will be disabled." + ) + self.enabled = False + + def _init_writer(self, api_key: str): + """Initialize Writer client.""" + try: + from writerai import AsyncWriter + self.client = AsyncWriter(api_key=api_key) + self.model = os.getenv("WRITER_COPILOT_MODEL", "palmyra-x5") + logger.info(f"Code completion handler initialized with Writer {self.model}") + except ImportError: + logger.error( + "writer-sdk not properly installed. " + "Install it with: pip install writer-sdk" + ) + self.enabled = False + except Exception as e: + logger.error(f"Failed to initialize Writer client: {e}") + self.enabled = False + + async def get_completion(self, request_body: Dict[str, Any]) -> Dict[str, Any]: + """ + Get code completion from Writer Palmyra. + + Args: + request_body: Request body from monacopilot containing: + - completionMetadata: Metadata about the completion request + - filename: Name of the file being edited + - language: Programming language + - textBeforeCursor: Text before cursor position + - textAfterCursor: Text after cursor position + + Returns: + Dictionary with completion result: + - completion: The suggested code completion text + """ + # Return empty completion if disabled or not initialized + if not self.enabled or not self.client: + return {"completion": ""} + + # Extract the actual completion metadata + completion_data = request_body.get("completionMetadata", request_body) + + # Get completion from Writer + return await self._get_writer_completion(completion_data) + + async def _get_writer_completion(self, request_body: Dict[str, Any]) -> Dict[str, Any]: + """Get completion from Writer Palmyra.""" + try: + # Extract context from request + language = request_body.get("language", "") + text_before = request_body.get("textBeforeCursor", "") + text_after = request_body.get("textAfterCursor", "") + filename = request_body.get("filename", "untitled") + + # Build prompt for Writer + prompt = self._build_completion_prompt( + language=language, + text_before=text_before, + text_after=text_after, + filename=filename + ) + + # Call Writer Chat API + response = await self.client.chat.chat( + model=self.model, + messages=[ + { + "role": "user", + "content": prompt + } + ], + max_tokens=200, + temperature=0.3, + ) + + # Extract completion text + if response.choices and len(response.choices) > 0: + completion_text = response.choices[0].message.content.strip() + completion_text = self._clean_completion(completion_text) + return {"completion": completion_text} + else: + return {"completion": ""} + + except Exception as e: + logger.error(f"Error getting completion from Writer: {e}") + return {"completion": ""} + + def _build_completion_prompt( + self, + language: str, + text_before: str, + text_after: str, + filename: str + ) -> str: + """ + Build the prompt for Writer Palmyra to generate code completion. + + Args: + language: Programming language + text_before: Code before cursor + text_after: Code after cursor + filename: Name of the file + + Returns: + Formatted prompt string + """ + return f"""You are an expert code completion assistant. Your task is to provide a short, relevant code completion that continues from the cursor position. + +File: {filename} +Language: {language} + +Code before cursor: +```{language} +{text_before} +``` + +Code after cursor: +```{language} +{text_after} +``` + +Provide ONLY the completion text that should be inserted at the cursor position. Do not include any explanations, markdown formatting, or code blocks. Just output the exact text that should be inserted. + +The completion should: +- Be contextually relevant to the code before and after +- Follow the existing code style and conventions +- Be concise (typically 1-3 lines) +- NOT repeat code that already exists before the cursor +- Complete the current statement, expression, or block naturally if it makes sense, otherwise use next line as a starting point + +Completion:""" + + def _clean_completion(self, completion: str) -> str: + """ + Clean up the completion text from Writer Palmyra. + + Args: + completion: Raw completion from Writer + + Returns: + Cleaned completion text + """ + # Remove markdown code blocks if present + if completion.startswith("```"): + lines = completion.split("\n") + # Remove first line (```language) + lines = lines[1:] + # Remove last line if it's ``` + if lines and lines[-1].strip() == "```": + lines = lines[:-1] + completion = "\n".join(lines) + + # Trim excessive whitespace but preserve intentional indentation + completion = completion.rstrip() + + return completion + + +# Global instance - initialized at module load time for simplicity +# This avoids the need for thread synchronization in FastAPI's multi-threaded environment +_completion_handler: CodeCompletionHandler = CodeCompletionHandler() + + +def get_completion_handler() -> CodeCompletionHandler: + """ + Get the global completion handler instance. + + Returns: + CodeCompletionHandler instance + """ + return _completion_handler + diff --git a/src/writer/lsp_manager.py b/src/writer/lsp_manager.py new file mode 100644 index 000000000..c99b40ddb --- /dev/null +++ b/src/writer/lsp_manager.py @@ -0,0 +1,171 @@ +""" +Language Server Protocol (LSP) manager for Python language support. + +This module manages the lifecycle of the python-lsp-server (pylsp) process, +providing WebSocket-based LSP features to the Monaco editor in the frontend. +""" + +import logging +import shutil +import socket +import subprocess +import time +from typing import Optional + +logger = logging.getLogger(__name__) + + +class LSPManager: + """Manages the Python Language Server Protocol server lifecycle.""" + + def __init__(self): + self.process: Optional[subprocess.Popen] = None + self.port: Optional[int] = None + self.host: str = "127.0.0.1" + + def _find_available_port(self, start_port: int = 5007, end_port: int = 5099) -> int: + """ + Find an available port in the given range. + + Args: + start_port: Starting port to check + end_port: Ending port to check + + Returns: + Available port number + + Raises: + RuntimeError: If no available port is found + + Note: + This method has a time-of-check-to-time-of-use (TOCTOU) race condition: + another process could claim the port between when we check it's available + and when pylsp tries to bind to it. This is an acceptable limitation for + a development tool where such races are unlikely. The alternative of using + port 0 (letting the OS assign a port) would require parsing pylsp's output + to discover the assigned port, adding complexity for minimal benefit. + """ + for port in range(start_port, end_port): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + try: + s.bind((self.host, port)) + return port + except OSError: + continue + raise RuntimeError(f"No available port found in range {start_port}-{end_port}") + + def _is_pylsp_available(self) -> bool: + """Check if pylsp command is available in the system.""" + return shutil.which("pylsp") is not None + + def start(self) -> bool: + """ + Start the Python LSP server. + + Returns: + True if server started successfully, False otherwise + """ + # Check if existing process is still alive + if self.process is not None and self.process.poll() is None: + logger.info("LSP server is already running") + return True + + # Clean up dead process if any + if self.process is not None: + logger.info("Cleaning up dead LSP server process") + self.process = None + + if not self._is_pylsp_available(): + logger.warning( + "pylsp command not found. Python LSP features will not be available. " + "Install with: pip install python-lsp-server[all]" + ) + return False + + try: + self.port = self._find_available_port() + logger.info(f"Starting Python LSP server on port {self.port}") + + # Start pylsp with WebSocket support + # Note: pylsp --ws may exit when client disconnects, this is expected behavior + self.process = subprocess.Popen( + ["pylsp", "--ws", "--port", str(self.port), "--host", self.host], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + logger.info(f"LSP server process started with PID {self.process.pid}") + + # Give the server a moment to start + time.sleep(0.5) + + # Check if process started successfully + if self.process.poll() is not None: + # Process terminated immediately + _, stderr = self.process.communicate() + logger.error(f"LSP server failed to start: {stderr}") + self.process = None + self.port = None + return False + + logger.info(f"Python LSP server started successfully on port {self.port}") + return True + + except Exception as e: + logger.error(f"Failed to start LSP server: {e}") + self.process = None + self.port = None + return False + + def stop(self): + """Stop the Python LSP server.""" + if self.process is None: + return + + logger.info("Stopping Python LSP server") + try: + self.process.terminate() + # Wait for graceful shutdown + try: + self.process.wait(timeout=5) + except subprocess.TimeoutExpired: + logger.warning("LSP server didn't terminate gracefully, forcing kill") + self.process.kill() + self.process.wait() + except Exception as e: + logger.error(f"Error stopping LSP server: {e}") + finally: + self.process = None + self.port = None + + def is_running(self) -> bool: + """Check if the LSP server is running.""" + if self.process is None: + return False + poll_result = self.process.poll() + if poll_result is not None: + logger.debug(f"LSP server process {self.process.pid} has exited with code {poll_result}") + return False + return True + + def get_config(self) -> dict: + """ + Get the LSP server configuration for the frontend. + Automatically restarts the server if it has stopped. + + Returns: + Dictionary with LSP configuration. + Note: websocket_url is set to None - frontend should construct + the URL using the current host and the provided port. + """ + # Auto-restart if server has stopped + if not self.is_running(): + logger.info("LSP server is not running, attempting to restart...") + self.start() + + return { + "enabled": self.is_running(), + "port": self.port, + "host": self.host, # For reference, but frontend should use window.location.hostname + } + diff --git a/src/writer/serve.py b/src/writer/serve.py index 0dc02a22c..9718040e0 100644 --- a/src/writer/serve.py +++ b/src/writer/serve.py @@ -42,7 +42,9 @@ from writer import VERSION, abstract from writer.ai import Graph +from writer.ai.code_completion import get_completion_handler from writer.app_runner import AppRunner +from writer.lsp_manager import LSPManager from writer.ss_types import ( AppProcessServerResponse, AutogenRequestBody, @@ -77,6 +79,7 @@ class WriterState(typing.Protocol): app_runner: AppRunner writer_app: bool is_server_static_mounted: bool + lsp_manager: Optional[LSPManager] meta: Union[Dict[str, Any], Callable[[], Dict[str, Any]]] # meta tags for SEO opengraph_tags: Union[ Dict[str, Any], Callable[[], Dict[str, Any]] @@ -133,6 +136,12 @@ async def lifespan(asgi_app: FastAPI): app_runner.hook_to_running_event_loop() app_runner.load() + # Start LSP server in edit mode + if serve_mode == "edit" and hasattr(asgi_app.state, "lsp_manager"): + lsp_manager = asgi_app.state.lsp_manager + if lsp_manager is not None: + lsp_manager.start() + if ( on_load is not None and hasattr(asgi_app.state, "is_server_static_mounted") @@ -152,6 +161,12 @@ async def lifespan(asgi_app: FastAPI): except asyncio.CancelledError: pass + # Stop LSP server + if serve_mode == "edit" and hasattr(asgi_app.state, "lsp_manager"): + lsp_manager = asgi_app.state.lsp_manager + if lsp_manager is not None: + lsp_manager.stop() + app_runner.shut_down() if on_shutdown is not None: on_shutdown() @@ -163,6 +178,12 @@ async def lifespan(asgi_app: FastAPI): """ app.state.writer_app = True app.state.app_runner = app_runner + + # Initialize LSP manager for edit mode + if serve_mode == "edit": + app.state.lsp_manager = LSPManager() + else: + app.state.lsp_manager = None def _get_extension_paths() -> List[str]: extensions_path = pathlib.Path(user_app_path) / "extensions" @@ -207,6 +228,8 @@ def _get_run_starter_pack(payload: InitSessionResponsePayload): def _get_edit_starter_pack(payload: InitSessionResponsePayload): run_code: Optional[str] = app_runner.run_code + + return InitResponseBodyEdit( mode="edit", sessionId=payload.sessionId, @@ -244,6 +267,26 @@ async def health(): return {"status": "ok"} + @app.get("/api/lsp-config") + async def lsp_config(): + """ + Returns LSP server configuration for the frontend. + Only available in edit mode. + """ + if serve_mode != "edit": + raise HTTPException(status_code=403, detail="LSP config only available in edit mode.") + + lsp_manager = app.state.lsp_manager + if lsp_manager is None: + return { + "enabled": False, + "websocket_url": None, + "port": None, + "host": None + } + + return lsp_manager.get_config() + @app.get("/api/export") async def export_zip(): if serve_mode != "edit": @@ -325,6 +368,61 @@ async def delete_key(key: str): return None + @app.post("/api/code-completion") + async def code_completion(request: Request): + """ + Handles AI-powered code completion requests from monacopilot. + + Only available in edit mode for security reasons. + + Requires environment variables: + - WRITER_COPILOT_ENABLED: Set to "true" to enable + - WRITER_API_KEY: API key for Writer AI (Palmyra models) + """ + # Check edit mode - only allow completions in edit mode + if serve_mode != "edit": + raise HTTPException( + status_code=403, + detail="Code completion only available in edit mode" + ) + + # Validate request size to prevent DoS + content_length = request.headers.get("content-length") + if content_length: + try: + size = int(content_length) + if size > 50000: # 50KB limit + raise HTTPException( + status_code=413, + detail="Request too large" + ) + except ValueError: + raise HTTPException( + status_code=400, + detail="Invalid content-length header" + ) + + try: + request_body = await request.json() + + # Validate request structure + if not isinstance(request_body, dict): + raise HTTPException( + status_code=400, + detail="Invalid request format" + ) + + handler = get_completion_handler() + result = await handler.get_completion(request_body) + return JSONResponse(content=result) + except HTTPException: + # Re-raise HTTP exceptions + raise + except Exception as e: + logging.error(f"Error in code completion endpoint: {e}") + # Return empty completion on error + return JSONResponse(content={"completion": ""}) + @app.post("/api/init") async def init( initBody: InitRequestBody, request: Request, response: Response