summaryrefslogtreecommitdiff
path: root/extension
diff options
context:
space:
mode:
Diffstat (limited to 'extension')
-rw-r--r--extension/DEV_README.md2
-rw-r--r--extension/README.md18
-rw-r--r--extension/media/edit.pngbin246984 -> 675283 bytes
-rw-r--r--extension/media/explain.pngbin464952 -> 897037 bytes
-rw-r--r--extension/media/generate.pngbin1049613 -> 411580 bytes
-rw-r--r--extension/package-lock.json4
-rw-r--r--extension/package.json26
-rw-r--r--extension/react-app/index.html2
-rw-r--r--extension/react-app/package-lock.json1473
-rw-r--r--extension/react-app/package.json2
-rw-r--r--extension/react-app/public/vite.svg1
-rw-r--r--extension/react-app/src/App.tsx13
-rw-r--r--extension/react-app/src/TestPage.tsx33
-rw-r--r--extension/react-app/src/assets/Hubot-Sans.woff2bin165932 -> 0 bytes
-rw-r--r--extension/react-app/src/assets/Mona-Sans.woff2bin133748 -> 0 bytes
-rw-r--r--extension/react-app/src/assets/react.svg1
-rw-r--r--extension/react-app/src/components/CodeMultiselect.tsx276
-rw-r--r--extension/react-app/src/components/ComboBox.tsx68
-rw-r--r--extension/react-app/src/components/LoadingCover.tsx50
-rw-r--r--extension/react-app/src/components/Onboarding.tsx136
-rw-r--r--extension/react-app/src/components/PillButton.tsx167
-rw-r--r--extension/react-app/src/components/StepContainer.tsx89
-rw-r--r--extension/react-app/src/components/TextDialog.tsx7
-rw-r--r--extension/react-app/src/highlight/dark.min.css53
-rw-r--r--extension/react-app/src/hooks/messenger.ts10
-rw-r--r--extension/react-app/src/pages/gui.tsx (renamed from extension/react-app/src/tabs/gui.tsx)37
-rw-r--r--extension/react-app/src/tabs/additionalContext.tsx18
-rw-r--r--extension/react-app/src/tabs/chat/MessageDiv.tsx75
-rw-r--r--extension/react-app/src/tabs/chat/index.tsx267
-rw-r--r--extension/react-app/src/tabs/main.tsx189
-rw-r--r--extension/react-app/src/tabs/welcome.tsx22
-rw-r--r--extension/react-app/src/util/api.ts43
-rw-r--r--extension/react-app/src/util/editCache.ts89
-rw-r--r--extension/react-app/src/util/index.ts62
-rw-r--r--extension/scripts/.gitignore5
-rw-r--r--extension/scripts/README.md5
-rw-r--r--extension/scripts/chroma.py152
-rw-r--r--extension/scripts/index.py52
-rw-r--r--extension/scripts/query.py63
-rw-r--r--extension/scripts/replace.py17
-rw-r--r--extension/scripts/requirements.txt6
-rw-r--r--extension/scripts/update.py185
-rw-r--r--extension/server/.gitignore1
-rw-r--r--extension/server/README.md3
-rw-r--r--extension/server/requirements.txt1
-rw-r--r--extension/server/run_continue_server.py (renamed from extension/scripts/run_continue_server.py)0
-rw-r--r--extension/src/activation/activate.ts47
-rw-r--r--extension/src/activation/environmentSetup.ts246
-rw-r--r--extension/src/bridge.ts213
-rw-r--r--extension/src/commands.ts117
-rw-r--r--extension/src/continueIdeClient.ts164
-rw-r--r--extension/src/debugPanel.ts114
-rw-r--r--extension/src/diffs.ts175
-rw-r--r--extension/src/extension.ts20
-rw-r--r--extension/src/lang-server/codeActions.ts53
-rw-r--r--extension/src/lang-server/codeLens.ts51
-rw-r--r--extension/src/suggestions.ts60
-rw-r--r--extension/src/util/messenger.ts10
-rw-r--r--extension/src/util/util.ts29
59 files changed, 2535 insertions, 2487 deletions
diff --git a/extension/DEV_README.md b/extension/DEV_README.md
index c247b82c..87ed9334 100644
--- a/extension/DEV_README.md
+++ b/extension/DEV_README.md
@@ -4,7 +4,7 @@ This is the Continue VS Code Extension. Its primary jobs are
1. Implement the IDE side of the Continue IDE protocol, allowing a Continue server to interact natively in an IDE. This happens in `src/continueIdeClient.ts`.
2. Open the Continue React app in a side panel. The React app's source code lives in the `react-app` directory. The panel is opened by the `continue.openContinueGUI` command, as defined in `src/commands.ts`.
-3. Run a Continue server in the background, which connects to both the IDE protocol and the React app. The server is launched in `src/activation/environmentSetup.ts` by calling Python code that lives in `scripts/` (unless extension settings define a server URL other than localhost:65432, in which case the extension will just connect to that).
+3. Run a Continue server in the background, which connects to both the IDE protocol and the React app. The server is launched in `src/activation/environmentSetup.ts` by calling Python code that lives in `server/` (unless extension settings define a server URL other than localhost:65432, in which case the extension will just connect to that).
4. Open Continue
diff --git a/extension/README.md b/extension/README.md
index b57aedb7..2d449b92 100644
--- a/extension/README.md
+++ b/extension/README.md
@@ -7,23 +7,23 @@
### Get possible explainations
Ask Continue about a part of your code to get another perspective
-- `what might cause this error?`
-- `what is the load_dotenv library name?`
-- `how do I find running process on port 8000?`
+- “how can I set up a Prisma schema that cascades deletes?”
+- “where in the page should I be making this request to the backend?”
+- “how can I communicate between these iframes?”
### Edit in natural language
Highlight a section of code and instruct Continue to refactor it
-- `/edit Make this use more descriptive variable names`
-- `/edit Rewrite this API call to grab all pages`
-- `/edit Use 'Union' instead of a vertical bar here`
+- “/edit migrate this digital ocean terraform file into one that works for GCP”
+- “/edit change this plot into a bar chart in this dashboard component”
+- “/edit rewrite this function to be async”
### Generate files from scratch
Let Continue build the scaffolding of Python scripts, React components, and more
-- `Create a shell script to back up my home dir to /tmp/`
-- `Write Python in a new file to get Posthog events`
-- `Add a React component for syntax highlighted code`
+- “/edit here is a connector for postgres, now write one for kafka”
+- “/edit make an IAM policy that creates a user with read-only access to S3”
+- “/edit use this schema to write me a SQL query that gets recently churned users”
## OpenAI API Key
diff --git a/extension/media/edit.png b/extension/media/edit.png
index 5e77c0ea..f4ca623c 100644
--- a/extension/media/edit.png
+++ b/extension/media/edit.png
Binary files differ
diff --git a/extension/media/explain.png b/extension/media/explain.png
index 196ab914..79e8ccc9 100644
--- a/extension/media/explain.png
+++ b/extension/media/explain.png
Binary files differ
diff --git a/extension/media/generate.png b/extension/media/generate.png
index 9d84e4ae..c16d9f9f 100644
--- a/extension/media/generate.png
+++ b/extension/media/generate.png
Binary files differ
diff --git a/extension/package-lock.json b/extension/package-lock.json
index 5733c2dd..fbd3d92d 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "continue",
- "version": "0.0.143",
+ "version": "0.0.175",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "continue",
- "version": "0.0.143",
+ "version": "0.0.175",
"license": "Apache-2.0",
"dependencies": {
"@electron/rebuild": "^3.2.10",
diff --git a/extension/package.json b/extension/package.json
index 444372f8..02a2ec1a 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -14,7 +14,7 @@
"displayName": "Continue",
"pricing": "Free",
"description": "The open-source coding autopilot",
- "version": "0.0.143",
+ "version": "0.0.175",
"publisher": "Continue",
"engines": {
"vscode": "^1.67.0"
@@ -44,18 +44,13 @@
"configuration": {
"title": "Continue",
"properties": {
- "continue.automode": {
- "type": "boolean",
- "default": true,
- "description": "Automatically find relevant code and suggest a fix whenever a traceback is found."
- },
"continue.serverUrl": {
"type": "string",
"default": "http://localhost:65432",
"description": "The URL of the Continue server to use."
},
"continue.OPENAI_API_KEY": {
- "type": "password",
+ "type": "string",
"default": null,
"description": "The OpenAI API key to use for code generation."
},
@@ -111,13 +106,18 @@
"command": "continue.quickTextEntry",
"category": "Continue",
"title": "Quick Text Entry"
+ },
+ {
+ "command": "continue.quickFix",
+ "category": "Continue",
+ "title": "Quick Fix"
}
],
"keybindings": [
{
"command": "continue.focusContinueInput",
- "mac": "cmd+k",
- "key": "ctrl+k"
+ "mac": "cmd+m",
+ "key": "ctrl+m"
},
{
"command": "continue.suggestionDown",
@@ -181,7 +181,7 @@
{
"id": "edit",
"title": "Edit in natural language",
- "description": "Highlight a section of code and instruct Continue to refactor it (e.g. `/edit Make this use more descriptive variable names`)",
+ "description": "Highlight a section of code and instruct Continue to refactor it (e.g. `/edit rewrite this function to be async`)",
"media": {
"image": "media/edit.png",
"altText": "Empty image"
@@ -191,7 +191,7 @@
{
"id": "explain",
"title": "Get possible explanations",
- "description": "Ask Continue about a part of your code to get another perspective (e.g. `how do I find running process on port 8000?`)",
+ "description": "Ask Continue about a part of your code to get another perspective (e.g. `where in the page should I be making this request to the backend?`)",
"media": {
"image": "media/explain.png",
"altText": "Empty image"
@@ -201,7 +201,7 @@
{
"id": "generate",
"title": "Generate files from scratch",
- "description": "Let Continue build the scaffolding of Python scripts, React components, and more (e.g. `Create a shell script to back up my home dir to /tmp/`)",
+ "description": "Let Continue build the scaffolding of Python scripts, React components, and more (e.g. `/edit here is a connector for postgres, now write one for kafka`)",
"media": {
"image": "media/generate.png",
"altText": "Empty image"
@@ -227,7 +227,7 @@
"test": "node ./out/test/runTest.js",
"jest": "jest --config ./jest.config.js",
"package": "cp ./config/prod_config.json ./config/config.json && mkdir -p ./build && vsce package --out ./build && cp ./config/dev_config.json ./config/config.json",
- "full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.2-py3-none-any.whl ../extension/scripts/continuedev-0.1.2-py3-none-any.whl && cd ../extension && npm install && npm run typegen && npm run clientgen && cd react-app && npm install && npm run build && cd .. && npm run package",
+ "full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.2-py3-none-any.whl ../extension/server/continuedev-0.1.2-py3-none-any.whl && cd ../extension && npm install && npm run typegen && npm run clientgen && cd react-app && npm install && npm run build && cd .. && npm run package",
"install-extension": "code --install-extension ./build/continue-0.0.8.vsix",
"uninstall": "code --uninstall-extension .continue",
"reinstall": "rm -rf ./build && npm run package && npm run uninstall && npm run install-extension"
diff --git a/extension/react-app/index.html b/extension/react-app/index.html
index e0d1c840..043c307e 100644
--- a/extension/react-app/index.html
+++ b/extension/react-app/index.html
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+ <link rel="icon" type="image/svg+xml" href="/play_button.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json
index 7316581d..13e02e86 100644
--- a/extension/react-app/package-lock.json
+++ b/extension/react-app/package-lock.json
@@ -11,12 +11,12 @@
"@styled-icons/heroicons-outline": "^10.47.0",
"@styled-icons/heroicons-solid": "^10.47.0",
"@types/vscode-webview": "^1.57.1",
+ "@uiw/react-markdown-preview": "^4.1.13",
"downshift": "^7.6.0",
"posthog-js": "^1.58.0",
"prismjs": "^1.29.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-markdown": "^8.0.5",
"react-redux": "^8.0.5",
"react-switch": "^7.0.0",
"react-syntax-highlighter": "^15.5.0",
@@ -963,6 +963,16 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
+ "node_modules/@types/parse5": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
+ "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
+ },
+ "node_modules/@types/prismjs": {
+ "version": "1.26.0",
+ "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz",
+ "integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ=="
+ },
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@@ -1027,6 +1037,34 @@
"resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.1.tgz",
"integrity": "sha512-ghW5SfuDmsGDS2A4xkvGsLwDRNc3Vj5rS6rPOyPm/IryZuf3wceZKxgYaUoW+k9f0f/CB7y2c1rRsdOWZWn0PQ=="
},
+ "node_modules/@uiw/copy-to-clipboard": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/@uiw/copy-to-clipboard/-/copy-to-clipboard-1.0.15.tgz",
+ "integrity": "sha512-1bbGZ3T+SGmA07BoVPK4UCUDcowDN/moctviJGQexfOc9qL8TMLDQPr7mTPvDKhgJkgnlKkAQNFU8PiarIi9sQ=="
+ },
+ "node_modules/@uiw/react-markdown-preview": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@uiw/react-markdown-preview/-/react-markdown-preview-4.1.13.tgz",
+ "integrity": "sha512-fmIGvBpK6HJyDFf7EokjZSIS0713Bq5KwhOsZ8IkbCMYDcDThFlmMkTTqyzGjL3phrkP9ED5O63WSILzefqe6A==",
+ "dependencies": {
+ "@babel/runtime": "^7.17.2",
+ "@uiw/copy-to-clipboard": "~1.0.12",
+ "react-markdown": "~8.0.0",
+ "rehype-attr": "~2.1.0",
+ "rehype-autolink-headings": "~6.1.1",
+ "rehype-ignore": "^1.0.1",
+ "rehype-prism-plus": "~1.5.0",
+ "rehype-raw": "^6.1.1",
+ "rehype-rewrite": "~3.0.6",
+ "rehype-slug": "~5.1.0",
+ "remark-gfm": "~3.0.1",
+ "unist-util-visit": "^4.1.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
"node_modules/@vitejs/plugin-react-swc": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.2.0.tgz",
@@ -1163,6 +1201,15 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/bcp-47-match": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",
+ "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -1172,6 +1219,11 @@
"node": ">=8"
}
},
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+ },
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
@@ -1245,6 +1297,15 @@
}
]
},
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -1370,6 +1431,11 @@
"node": ">=4"
}
},
+ "node_modules/css-selector-parser": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz",
+ "integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g=="
+ },
"node_modules/css-to-react-native": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
@@ -1473,6 +1539,18 @@
"node": ">=0.3.1"
}
},
+ "node_modules/direction": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz",
+ "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==",
+ "bin": {
+ "direction": "cli.js"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
@@ -1684,6 +1762,11 @@
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
+ "node_modules/github-slugger": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
+ "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -1729,6 +1812,86 @@
"node": ">=4"
}
},
+ "node_modules/hast-util-from-parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz",
+ "integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "@types/unist": "^2.0.0",
+ "hastscript": "^7.0.0",
+ "property-information": "^6.0.0",
+ "vfile": "^5.0.0",
+ "vfile-location": "^4.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
+ "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
+ "dependencies": {
+ "@types/hast": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-from-parse5/node_modules/hastscript": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
+ "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^3.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-has-property": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-2.0.1.tgz",
+ "integrity": "sha512-X2+RwZIMTMKpXUzlotatPzWj8bspCymtXH3cfG3iQKV+wPF53Vgaqxi/eLqGck0wKq1kS9nvoB1wchbCPEL8sg==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-heading-rank": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-2.1.1.tgz",
+ "integrity": "sha512-iAuRp+ESgJoRFJbSyaqsfvJDY6zzmFoEnL1gtz1+U8gKtGGj1p0CVlysuUAUjq95qlZESHINLThwJzNGmgGZxA==",
+ "dependencies": {
+ "@types/hast": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-element": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz",
+ "integrity": "sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "@types/unist": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/hast-util-parse-selector": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
@@ -1738,6 +1901,83 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/hast-util-raw": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz",
+ "integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "@types/parse5": "^6.0.0",
+ "hast-util-from-parse5": "^7.0.0",
+ "hast-util-to-parse5": "^7.0.0",
+ "html-void-elements": "^2.0.0",
+ "parse5": "^6.0.0",
+ "unist-util-position": "^4.0.0",
+ "unist-util-visit": "^4.0.0",
+ "vfile": "^5.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-select": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-5.0.5.tgz",
+ "integrity": "sha512-QQhWMhgTFRhCaQdgTKzZ5g31GLQ9qRb1hZtDPMqQaOhpLBziWcshUS0uCR5IJ0U1jrK/mxg35fmcq+Dp/Cy2Aw==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "@types/unist": "^2.0.0",
+ "bcp-47-match": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "css-selector-parser": "^1.0.0",
+ "direction": "^2.0.0",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-to-string": "^2.0.0",
+ "hast-util-whitespace": "^2.0.0",
+ "not": "^0.1.0",
+ "nth-check": "^2.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "unist-util-visit": "^4.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-parse5": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz",
+ "integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz",
+ "integrity": "sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==",
+ "dependencies": {
+ "@types/hast": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/hast-util-whitespace": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz",
@@ -1814,6 +2054,15 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/html-void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz",
+ "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/inline-style-parser": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
@@ -1995,6 +2244,15 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -2019,6 +2277,15 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/markdown-table": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
+ "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/mdast-util-definitions": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz",
@@ -2033,6 +2300,32 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz",
+ "integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^5.0.0",
+ "unist-util-visit-parents": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/mdast-util-from-markdown": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.0.tgz",
@@ -2056,6 +2349,107 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/mdast-util-gfm": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz",
+ "integrity": "sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==",
+ "dependencies": {
+ "mdast-util-from-markdown": "^1.0.0",
+ "mdast-util-gfm-autolink-literal": "^1.0.0",
+ "mdast-util-gfm-footnote": "^1.0.0",
+ "mdast-util-gfm-strikethrough": "^1.0.0",
+ "mdast-util-gfm-table": "^1.0.0",
+ "mdast-util-gfm-task-list-item": "^1.0.0",
+ "mdast-util-to-markdown": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz",
+ "integrity": "sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "ccount": "^2.0.0",
+ "mdast-util-find-and-replace": "^2.0.0",
+ "micromark-util-character": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz",
+ "integrity": "sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-to-markdown": "^1.3.0",
+ "micromark-util-normalize-identifier": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz",
+ "integrity": "sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-to-markdown": "^1.3.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz",
+ "integrity": "sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^1.0.0",
+ "mdast-util-to-markdown": "^1.3.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz",
+ "integrity": "sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-to-markdown": "^1.3.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz",
+ "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "unist-util-is": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/mdast-util-to-hast": {
"version": "12.3.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz",
@@ -2075,6 +2469,25 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/mdast-util-to-markdown": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz",
+ "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "@types/unist": "^2.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^3.0.0",
+ "mdast-util-to-string": "^3.0.0",
+ "micromark-util-decode-string": "^1.0.0",
+ "unist-util-visit": "^4.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/mdast-util-to-string": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.1.tgz",
@@ -2163,6 +2576,120 @@
"uvu": "^0.5.0"
}
},
+ "node_modules/micromark-extension-gfm": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz",
+ "integrity": "sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^1.0.0",
+ "micromark-extension-gfm-footnote": "^1.0.0",
+ "micromark-extension-gfm-strikethrough": "^1.0.0",
+ "micromark-extension-gfm-table": "^1.0.0",
+ "micromark-extension-gfm-tagfilter": "^1.0.0",
+ "micromark-extension-gfm-task-list-item": "^1.0.0",
+ "micromark-util-combine-extensions": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz",
+ "integrity": "sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==",
+ "dependencies": {
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-sanitize-uri": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz",
+ "integrity": "sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==",
+ "dependencies": {
+ "micromark-core-commonmark": "^1.0.0",
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-normalize-identifier": "^1.0.0",
+ "micromark-util-sanitize-uri": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz",
+ "integrity": "sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==",
+ "dependencies": {
+ "micromark-util-chunked": "^1.0.0",
+ "micromark-util-classify-character": "^1.0.0",
+ "micromark-util-resolve-all": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz",
+ "integrity": "sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==",
+ "dependencies": {
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz",
+ "integrity": "sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==",
+ "dependencies": {
+ "micromark-util-types": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz",
+ "integrity": "sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==",
+ "dependencies": {
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/micromark-factory-destination": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz",
@@ -2589,6 +3116,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/not": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz",
+ "integrity": "sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA=="
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -2632,6 +3175,16 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/parse-numeric-range": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz",
+ "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ=="
+ },
+ "node_modules/parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@@ -3047,6 +3600,257 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
+ "node_modules/rehype-attr": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/rehype-attr/-/rehype-attr-2.1.4.tgz",
+ "integrity": "sha512-iAeaL5JyF4XxkcvWzpi/0SAF7iV7qOTaHS56tJuEsXziQc3+PEmMn65kV8OFgbO9mRVY7J1fRC/aLvot1PsNkg==",
+ "dependencies": {
+ "unified": "~10.1.1",
+ "unist-util-visit": "~4.1.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/rehype-autolink-headings": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz",
+ "integrity": "sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "extend": "^3.0.0",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-heading-rank": "^2.0.0",
+ "hast-util-is-element": "^2.0.0",
+ "unified": "^10.0.0",
+ "unist-util-visit": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-ignore": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/rehype-ignore/-/rehype-ignore-1.0.5.tgz",
+ "integrity": "sha512-JQXS5eDwXaYKwB8JEYFJJA/YvGi0sSNUOYuiURMtuPTg8tuWHFB91JMYLbImH1FyvyGQM4fIBqNMAPB50WR2Bw==",
+ "dependencies": {
+ "hast-util-select": "^5.0.5",
+ "unified": "^10.1.2",
+ "unist-util-visit": "^4.1.2"
+ },
+ "engines": {
+ "node": "^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/rehype-parse": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-8.0.4.tgz",
+ "integrity": "sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "hast-util-from-parse5": "^7.0.0",
+ "parse5": "^6.0.0",
+ "unified": "^10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-prism-plus": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-1.5.1.tgz",
+ "integrity": "sha512-mowYefSfrIkMMxkb0fwuEXlvc5nA9b1vQ6mzujM81Qx28RI0mo7jCHsBZ2tJ4eIJKXdFn+EdPkZZBGB10K02vg==",
+ "dependencies": {
+ "hast-util-to-string": "^2.0.0",
+ "parse-numeric-range": "^1.3.0",
+ "refractor": "^4.7.0",
+ "rehype-parse": "^8.0.2",
+ "unist-util-filter": "^4.0.0",
+ "unist-util-visit": "^4.0.0"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/hast-util-parse-selector": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
+ "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
+ "dependencies": {
+ "@types/hast": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/hastscript": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
+ "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^3.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/parse-entities": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz",
+ "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/rehype-prism-plus/node_modules/refractor": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.8.1.tgz",
+ "integrity": "sha512-/fk5sI0iTgFYlmVGYVew90AoYnNMP6pooClx/XKqyeeCQXrL0Kvgn8V0VEht5ccdljbzzF1i3Q213gcntkRExg==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "@types/prismjs": "^1.0.0",
+ "hastscript": "^7.0.0",
+ "parse-entities": "^4.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/rehype-raw": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-6.1.1.tgz",
+ "integrity": "sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "hast-util-raw": "^7.2.0",
+ "unified": "^10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-rewrite": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/rehype-rewrite/-/rehype-rewrite-3.0.6.tgz",
+ "integrity": "sha512-REDTNCvsKcAazy8IQWzKp66AhSUDSOIKssSCqNqCcT9sN7JCwAAm3mWGTUdUzq80ABuy8d0D6RBwbnewu1aY1g==",
+ "dependencies": {
+ "hast-util-select": "~5.0.1",
+ "unified": "~10.1.1",
+ "unist-util-visit": "~4.1.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/rehype-slug": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-5.1.0.tgz",
+ "integrity": "sha512-Gf91dJoXneiorNEnn+Phx97CO7oRMrpi+6r155tTxzGuLtm+QrI4cTwCa9e1rtePdL4i9tSO58PeSS6HWfgsiw==",
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "github-slugger": "^2.0.0",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-heading-rank": "^2.0.0",
+ "hast-util-to-string": "^2.0.0",
+ "unified": "^10.0.0",
+ "unist-util-visit": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-gfm": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz",
+ "integrity": "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==",
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-gfm": "^2.0.0",
+ "micromark-extension-gfm": "^2.0.0",
+ "unified": "^10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/remark-parse": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz",
@@ -3365,6 +4169,16 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/unist-util-filter": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-filter/-/unist-util-filter-4.0.1.tgz",
+ "integrity": "sha512-RynicUM/vbOSTSiUK+BnaK9XMfmQUh6gyi7L6taNgc7FIf84GukXVV3ucGzEN/PhUUkdP5hb1MmXc+3cvPUm5Q==",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^5.0.0",
+ "unist-util-visit-parents": "^5.0.0"
+ }
+ },
"node_modules/unist-util-generated": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz",
@@ -3517,6 +4331,19 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/vfile-location": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz",
+ "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "vfile": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/vfile-message": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz",
@@ -3587,6 +4414,15 @@
"fs-extra": "^10.0.0"
}
},
+ "node_modules/web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -3603,6 +4439,15 @@
"engines": {
"node": ">= 6"
}
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
}
},
"dependencies": {
@@ -4143,6 +4988,16 @@
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA=="
},
+ "@types/parse5": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
+ "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
+ },
+ "@types/prismjs": {
+ "version": "1.26.0",
+ "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz",
+ "integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ=="
+ },
"@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@@ -4207,6 +5062,30 @@
"resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.1.tgz",
"integrity": "sha512-ghW5SfuDmsGDS2A4xkvGsLwDRNc3Vj5rS6rPOyPm/IryZuf3wceZKxgYaUoW+k9f0f/CB7y2c1rRsdOWZWn0PQ=="
},
+ "@uiw/copy-to-clipboard": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/@uiw/copy-to-clipboard/-/copy-to-clipboard-1.0.15.tgz",
+ "integrity": "sha512-1bbGZ3T+SGmA07BoVPK4UCUDcowDN/moctviJGQexfOc9qL8TMLDQPr7mTPvDKhgJkgnlKkAQNFU8PiarIi9sQ=="
+ },
+ "@uiw/react-markdown-preview": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@uiw/react-markdown-preview/-/react-markdown-preview-4.1.13.tgz",
+ "integrity": "sha512-fmIGvBpK6HJyDFf7EokjZSIS0713Bq5KwhOsZ8IkbCMYDcDThFlmMkTTqyzGjL3phrkP9ED5O63WSILzefqe6A==",
+ "requires": {
+ "@babel/runtime": "^7.17.2",
+ "@uiw/copy-to-clipboard": "~1.0.12",
+ "react-markdown": "~8.0.0",
+ "rehype-attr": "~2.1.0",
+ "rehype-autolink-headings": "~6.1.1",
+ "rehype-ignore": "^1.0.1",
+ "rehype-prism-plus": "~1.5.0",
+ "rehype-raw": "^6.1.1",
+ "rehype-rewrite": "~3.0.6",
+ "rehype-slug": "~5.1.0",
+ "remark-gfm": "~3.0.1",
+ "unist-util-visit": "^4.1.0"
+ }
+ },
"@vitejs/plugin-react-swc": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.2.0.tgz",
@@ -4299,12 +5178,22 @@
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="
},
+ "bcp-47-match": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz",
+ "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="
+ },
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+ },
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
@@ -4343,6 +5232,11 @@
"integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==",
"dev": true
},
+ "ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="
+ },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -4436,6 +5330,11 @@
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="
},
+ "css-selector-parser": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz",
+ "integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g=="
+ },
"css-to-react-native": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
@@ -4506,6 +5405,11 @@
"resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
"integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw=="
},
+ "direction": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz",
+ "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA=="
+ },
"dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
@@ -4672,6 +5576,11 @@
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
+ "github-slugger": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
+ "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
+ },
"glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -4705,11 +5614,130 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
+ "hast-util-from-parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz",
+ "integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "@types/unist": "^2.0.0",
+ "hastscript": "^7.0.0",
+ "property-information": "^6.0.0",
+ "vfile": "^5.0.0",
+ "vfile-location": "^4.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "dependencies": {
+ "hast-util-parse-selector": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
+ "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
+ "requires": {
+ "@types/hast": "^2.0.0"
+ }
+ },
+ "hastscript": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
+ "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^3.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0"
+ }
+ }
+ }
+ },
+ "hast-util-has-property": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-2.0.1.tgz",
+ "integrity": "sha512-X2+RwZIMTMKpXUzlotatPzWj8bspCymtXH3cfG3iQKV+wPF53Vgaqxi/eLqGck0wKq1kS9nvoB1wchbCPEL8sg=="
+ },
+ "hast-util-heading-rank": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-2.1.1.tgz",
+ "integrity": "sha512-iAuRp+ESgJoRFJbSyaqsfvJDY6zzmFoEnL1gtz1+U8gKtGGj1p0CVlysuUAUjq95qlZESHINLThwJzNGmgGZxA==",
+ "requires": {
+ "@types/hast": "^2.0.0"
+ }
+ },
+ "hast-util-is-element": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz",
+ "integrity": "sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "@types/unist": "^2.0.0"
+ }
+ },
"hast-util-parse-selector": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ=="
},
+ "hast-util-raw": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz",
+ "integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "@types/parse5": "^6.0.0",
+ "hast-util-from-parse5": "^7.0.0",
+ "hast-util-to-parse5": "^7.0.0",
+ "html-void-elements": "^2.0.0",
+ "parse5": "^6.0.0",
+ "unist-util-position": "^4.0.0",
+ "unist-util-visit": "^4.0.0",
+ "vfile": "^5.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ }
+ },
+ "hast-util-select": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-5.0.5.tgz",
+ "integrity": "sha512-QQhWMhgTFRhCaQdgTKzZ5g31GLQ9qRb1hZtDPMqQaOhpLBziWcshUS0uCR5IJ0U1jrK/mxg35fmcq+Dp/Cy2Aw==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "@types/unist": "^2.0.0",
+ "bcp-47-match": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "css-selector-parser": "^1.0.0",
+ "direction": "^2.0.0",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-to-string": "^2.0.0",
+ "hast-util-whitespace": "^2.0.0",
+ "not": "^0.1.0",
+ "nth-check": "^2.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "unist-util-visit": "^4.0.0",
+ "zwitch": "^2.0.0"
+ }
+ },
+ "hast-util-to-parse5": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz",
+ "integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ }
+ },
+ "hast-util-to-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz",
+ "integrity": "sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==",
+ "requires": {
+ "@types/hast": "^2.0.0"
+ }
+ },
"hast-util-whitespace": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz",
@@ -4767,6 +5795,11 @@
}
}
},
+ "html-void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz",
+ "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A=="
+ },
"inline-style-parser": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
@@ -4880,6 +5913,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="
+ },
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -4897,6 +5935,11 @@
"highlight.js": "~10.7.0"
}
},
+ "markdown-table": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
+ "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw=="
+ },
"mdast-util-definitions": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz",
@@ -4907,6 +5950,24 @@
"unist-util-visit": "^4.0.0"
}
},
+ "mdast-util-find-and-replace": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz",
+ "integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^5.0.0",
+ "unist-util-visit-parents": "^5.0.0"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
+ }
+ }
+ },
"mdast-util-from-markdown": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.0.tgz",
@@ -4926,6 +5987,79 @@
"uvu": "^0.5.0"
}
},
+ "mdast-util-gfm": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz",
+ "integrity": "sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==",
+ "requires": {
+ "mdast-util-from-markdown": "^1.0.0",
+ "mdast-util-gfm-autolink-literal": "^1.0.0",
+ "mdast-util-gfm-footnote": "^1.0.0",
+ "mdast-util-gfm-strikethrough": "^1.0.0",
+ "mdast-util-gfm-table": "^1.0.0",
+ "mdast-util-gfm-task-list-item": "^1.0.0",
+ "mdast-util-to-markdown": "^1.0.0"
+ }
+ },
+ "mdast-util-gfm-autolink-literal": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz",
+ "integrity": "sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "ccount": "^2.0.0",
+ "mdast-util-find-and-replace": "^2.0.0",
+ "micromark-util-character": "^1.0.0"
+ }
+ },
+ "mdast-util-gfm-footnote": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz",
+ "integrity": "sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-to-markdown": "^1.3.0",
+ "micromark-util-normalize-identifier": "^1.0.0"
+ }
+ },
+ "mdast-util-gfm-strikethrough": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz",
+ "integrity": "sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-to-markdown": "^1.3.0"
+ }
+ },
+ "mdast-util-gfm-table": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz",
+ "integrity": "sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^1.0.0",
+ "mdast-util-to-markdown": "^1.3.0"
+ }
+ },
+ "mdast-util-gfm-task-list-item": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz",
+ "integrity": "sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-to-markdown": "^1.3.0"
+ }
+ },
+ "mdast-util-phrasing": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz",
+ "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "unist-util-is": "^5.0.0"
+ }
+ },
"mdast-util-to-hast": {
"version": "12.3.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz",
@@ -4941,6 +6075,21 @@
"unist-util-visit": "^4.0.0"
}
},
+ "mdast-util-to-markdown": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz",
+ "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "@types/unist": "^2.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^3.0.0",
+ "mdast-util-to-string": "^3.0.0",
+ "micromark-util-decode-string": "^1.0.0",
+ "unist-util-visit": "^4.0.0",
+ "zwitch": "^2.0.0"
+ }
+ },
"mdast-util-to-string": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.1.1.tgz",
@@ -5002,6 +6151,92 @@
"uvu": "^0.5.0"
}
},
+ "micromark-extension-gfm": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz",
+ "integrity": "sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==",
+ "requires": {
+ "micromark-extension-gfm-autolink-literal": "^1.0.0",
+ "micromark-extension-gfm-footnote": "^1.0.0",
+ "micromark-extension-gfm-strikethrough": "^1.0.0",
+ "micromark-extension-gfm-table": "^1.0.0",
+ "micromark-extension-gfm-tagfilter": "^1.0.0",
+ "micromark-extension-gfm-task-list-item": "^1.0.0",
+ "micromark-util-combine-extensions": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "micromark-extension-gfm-autolink-literal": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz",
+ "integrity": "sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==",
+ "requires": {
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-sanitize-uri": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "micromark-extension-gfm-footnote": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz",
+ "integrity": "sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==",
+ "requires": {
+ "micromark-core-commonmark": "^1.0.0",
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-normalize-identifier": "^1.0.0",
+ "micromark-util-sanitize-uri": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ }
+ },
+ "micromark-extension-gfm-strikethrough": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz",
+ "integrity": "sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==",
+ "requires": {
+ "micromark-util-chunked": "^1.0.0",
+ "micromark-util-classify-character": "^1.0.0",
+ "micromark-util-resolve-all": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ }
+ },
+ "micromark-extension-gfm-table": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz",
+ "integrity": "sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==",
+ "requires": {
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ }
+ },
+ "micromark-extension-gfm-tagfilter": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz",
+ "integrity": "sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==",
+ "requires": {
+ "micromark-util-types": "^1.0.0"
+ }
+ },
+ "micromark-extension-gfm-task-list-item": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz",
+ "integrity": "sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==",
+ "requires": {
+ "micromark-factory-space": "^1.0.0",
+ "micromark-util-character": "^1.0.0",
+ "micromark-util-symbol": "^1.0.0",
+ "micromark-util-types": "^1.0.0",
+ "uvu": "^0.5.0"
+ }
+ },
"micromark-factory-destination": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz",
@@ -5217,6 +6452,19 @@
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
"dev": true
},
+ "not": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz",
+ "integrity": "sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA=="
+ },
+ "nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -5248,6 +6496,16 @@
}
}
},
+ "parse-numeric-range": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz",
+ "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ=="
+ },
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ },
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@@ -5510,6 +6768,190 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
+ "rehype-attr": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/rehype-attr/-/rehype-attr-2.1.4.tgz",
+ "integrity": "sha512-iAeaL5JyF4XxkcvWzpi/0SAF7iV7qOTaHS56tJuEsXziQc3+PEmMn65kV8OFgbO9mRVY7J1fRC/aLvot1PsNkg==",
+ "requires": {
+ "unified": "~10.1.1",
+ "unist-util-visit": "~4.1.0"
+ }
+ },
+ "rehype-autolink-headings": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz",
+ "integrity": "sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "extend": "^3.0.0",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-heading-rank": "^2.0.0",
+ "hast-util-is-element": "^2.0.0",
+ "unified": "^10.0.0",
+ "unist-util-visit": "^4.0.0"
+ }
+ },
+ "rehype-ignore": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/rehype-ignore/-/rehype-ignore-1.0.5.tgz",
+ "integrity": "sha512-JQXS5eDwXaYKwB8JEYFJJA/YvGi0sSNUOYuiURMtuPTg8tuWHFB91JMYLbImH1FyvyGQM4fIBqNMAPB50WR2Bw==",
+ "requires": {
+ "hast-util-select": "^5.0.5",
+ "unified": "^10.1.2",
+ "unist-util-visit": "^4.1.2"
+ }
+ },
+ "rehype-parse": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-8.0.4.tgz",
+ "integrity": "sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "hast-util-from-parse5": "^7.0.0",
+ "parse5": "^6.0.0",
+ "unified": "^10.0.0"
+ }
+ },
+ "rehype-prism-plus": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-1.5.1.tgz",
+ "integrity": "sha512-mowYefSfrIkMMxkb0fwuEXlvc5nA9b1vQ6mzujM81Qx28RI0mo7jCHsBZ2tJ4eIJKXdFn+EdPkZZBGB10K02vg==",
+ "requires": {
+ "hast-util-to-string": "^2.0.0",
+ "parse-numeric-range": "^1.3.0",
+ "refractor": "^4.7.0",
+ "rehype-parse": "^8.0.2",
+ "unist-util-filter": "^4.0.0",
+ "unist-util-visit": "^4.0.0"
+ },
+ "dependencies": {
+ "character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="
+ },
+ "character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="
+ },
+ "hast-util-parse-selector": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
+ "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
+ "requires": {
+ "@types/hast": "^2.0.0"
+ }
+ },
+ "hastscript": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
+ "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^3.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0"
+ }
+ },
+ "is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="
+ },
+ "is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "requires": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ }
+ },
+ "is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="
+ },
+ "is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="
+ },
+ "parse-entities": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz",
+ "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==",
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "character-entities": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ }
+ },
+ "refractor": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.8.1.tgz",
+ "integrity": "sha512-/fk5sI0iTgFYlmVGYVew90AoYnNMP6pooClx/XKqyeeCQXrL0Kvgn8V0VEht5ccdljbzzF1i3Q213gcntkRExg==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "@types/prismjs": "^1.0.0",
+ "hastscript": "^7.0.0",
+ "parse-entities": "^4.0.0"
+ }
+ }
+ }
+ },
+ "rehype-raw": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-6.1.1.tgz",
+ "integrity": "sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "hast-util-raw": "^7.2.0",
+ "unified": "^10.0.0"
+ }
+ },
+ "rehype-rewrite": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/rehype-rewrite/-/rehype-rewrite-3.0.6.tgz",
+ "integrity": "sha512-REDTNCvsKcAazy8IQWzKp66AhSUDSOIKssSCqNqCcT9sN7JCwAAm3mWGTUdUzq80ABuy8d0D6RBwbnewu1aY1g==",
+ "requires": {
+ "hast-util-select": "~5.0.1",
+ "unified": "~10.1.1",
+ "unist-util-visit": "~4.1.0"
+ }
+ },
+ "rehype-slug": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-5.1.0.tgz",
+ "integrity": "sha512-Gf91dJoXneiorNEnn+Phx97CO7oRMrpi+6r155tTxzGuLtm+QrI4cTwCa9e1rtePdL4i9tSO58PeSS6HWfgsiw==",
+ "requires": {
+ "@types/hast": "^2.0.0",
+ "github-slugger": "^2.0.0",
+ "hast-util-has-property": "^2.0.0",
+ "hast-util-heading-rank": "^2.0.0",
+ "hast-util-to-string": "^2.0.0",
+ "unified": "^10.0.0",
+ "unist-util-visit": "^4.0.0"
+ }
+ },
+ "remark-gfm": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz",
+ "integrity": "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==",
+ "requires": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-gfm": "^2.0.0",
+ "micromark-extension-gfm": "^2.0.0",
+ "unified": "^10.0.0"
+ }
+ },
"remark-parse": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz",
@@ -5722,6 +7164,16 @@
"vfile": "^5.0.0"
}
},
+ "unist-util-filter": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-filter/-/unist-util-filter-4.0.1.tgz",
+ "integrity": "sha512-RynicUM/vbOSTSiUK+BnaK9XMfmQUh6gyi7L6taNgc7FIf84GukXVV3ucGzEN/PhUUkdP5hb1MmXc+3cvPUm5Q==",
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "unist-util-is": "^5.0.0",
+ "unist-util-visit-parents": "^5.0.0"
+ }
+ },
"unist-util-generated": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz",
@@ -5819,6 +7271,15 @@
"vfile-message": "^3.0.0"
}
},
+ "vfile-location": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz",
+ "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==",
+ "requires": {
+ "@types/unist": "^2.0.0",
+ "vfile": "^5.0.0"
+ }
+ },
"vfile-message": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz",
@@ -5849,6 +7310,11 @@
"fs-extra": "^10.0.0"
}
},
+ "web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="
+ },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -5859,6 +7325,11 @@
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true
+ },
+ "zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="
}
}
}
diff --git a/extension/react-app/package.json b/extension/react-app/package.json
index 4bedb813..704f520a 100644
--- a/extension/react-app/package.json
+++ b/extension/react-app/package.json
@@ -12,12 +12,12 @@
"@styled-icons/heroicons-outline": "^10.47.0",
"@styled-icons/heroicons-solid": "^10.47.0",
"@types/vscode-webview": "^1.57.1",
+ "@uiw/react-markdown-preview": "^4.1.13",
"downshift": "^7.6.0",
"posthog-js": "^1.58.0",
"prismjs": "^1.29.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-markdown": "^8.0.5",
"react-redux": "^8.0.5",
"react-switch": "^7.0.0",
"react-syntax-highlighter": "^15.5.0",
diff --git a/extension/react-app/public/vite.svg b/extension/react-app/public/vite.svg
deleted file mode 100644
index e7b8dfb1..00000000
--- a/extension/react-app/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> \ No newline at end of file
diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx
index 8785f88f..c9bd42e0 100644
--- a/extension/react-app/src/App.tsx
+++ b/extension/react-app/src/App.tsx
@@ -1,8 +1,5 @@
import DebugPanel from "./components/DebugPanel";
-import MainTab from "./tabs/main";
-import WelcomeTab from "./tabs/welcome";
-import ChatTab from "./tabs/chat";
-import GUI from "./tabs/gui";
+import GUI from "./pages/gui";
import { createContext } from "react";
import useContinueGUIProtocol from "./hooks/useWebsocket";
import ContinueGUIClientProtocol from "./hooks/useContinueGUIProtocol";
@@ -18,13 +15,7 @@ function App() {
<GUIClientContext.Provider value={client}>
<DebugPanel
tabs={[
- {
- element: <GUI />,
- title: "GUI",
- },
- // { element: <MainTab />, title: "Debug Panel" },
- // { element: <WelcomeTab />, title: "Welcome" },
- // { element: <ChatTab />, title: "Chat" },
+ { element: <GUI />, title: "GUI" }
]}
/>
</GUIClientContext.Provider>
diff --git a/extension/react-app/src/TestPage.tsx b/extension/react-app/src/TestPage.tsx
deleted file mode 100644
index d104980b..00000000
--- a/extension/react-app/src/TestPage.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-
-const SideBySideDiv = styled.div`
- display: grid;
- grid-template-columns: 1fr 1fr;
- grid-template-rows: 1fr;
- grid-template-areas: "left right";
-`;
-
-const LeftDiv = styled.div`
- grid-area: left;
-`;
-
-const RightDiv = styled.div`
- grid-area: right;
-`;
-
-function TestPage() {
- return (
- <div>
- <h1>Continue</h1>
- <SideBySideDiv>
- <LeftDiv>
- <h2>Left</h2>
- </LeftDiv>
- <RightDiv>
- <h2>Right</h2>
- </RightDiv>
- </SideBySideDiv>
- </div>
- );
-}
diff --git a/extension/react-app/src/assets/Hubot-Sans.woff2 b/extension/react-app/src/assets/Hubot-Sans.woff2
deleted file mode 100644
index 5089fc47..00000000
--- a/extension/react-app/src/assets/Hubot-Sans.woff2
+++ /dev/null
Binary files differ
diff --git a/extension/react-app/src/assets/Mona-Sans.woff2 b/extension/react-app/src/assets/Mona-Sans.woff2
deleted file mode 100644
index 8208a500..00000000
--- a/extension/react-app/src/assets/Mona-Sans.woff2
+++ /dev/null
Binary files differ
diff --git a/extension/react-app/src/assets/react.svg b/extension/react-app/src/assets/react.svg
deleted file mode 100644
index 6c87de9b..00000000
--- a/extension/react-app/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg> \ No newline at end of file
diff --git a/extension/react-app/src/components/CodeMultiselect.tsx b/extension/react-app/src/components/CodeMultiselect.tsx
deleted file mode 100644
index c0ab9400..00000000
--- a/extension/react-app/src/components/CodeMultiselect.tsx
+++ /dev/null
@@ -1,276 +0,0 @@
-import React, { useEffect, useState } from "react";
-import styled from "styled-components";
-import { Button, buttonColor, defaultBorderRadius, secondaryDark } from ".";
-import { useSelector } from "react-redux";
-import {
- selectDebugContext,
- selectAllRangesInFiles,
- selectRangesMask,
-} from "../redux/selectors/debugContextSelectors";
-import "../highlight/dark.min.css";
-import hljs from "highlight.js";
-import { postVscMessage } from "../vscode";
-import { RootStore } from "../redux/store";
-import { useDispatch } from "react-redux";
-import {
- addRangeInFile,
- deleteRangeInFileAt,
- toggleSelectionAt,
- updateFileSystem,
-} from "../redux/slices/debugContexSlice";
-import { RangeInFile } from "../../../src/client";
-import { readRangeInVirtualFileSystem } from "../util";
-
-//#region Styled Components
-
-const MultiSelectContainer = styled.div`
- border-radius: ${defaultBorderRadius};
- padding: 4px;
- display: flex;
- flex-direction: column;
- gap: 4px;
-`;
-
-const MultiSelectHeader = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: left;
- border-bottom: 1px solid gray;
- padding-left: 4px;
- padding-right: 4px;
- & p {
- overflow-wrap: break-word;
- word-wrap: break-word;
- -ms-wrap-flow: break-word;
- overflow: hidden;
- }
-`;
-
-const MultiSelectOption = styled.div`
- border-radius: ${defaultBorderRadius};
- padding-top: 4px;
- cursor: pointer;
- background-color: ${secondaryDark};
-`;
-
-const DeleteSelectedRangeButton = styled(Button)`
- align-self: right;
- padding: 0px;
- margin-top: 0;
- aspect-ratio: 1/1;
- height: 28px;
-`;
-
-const ToggleHighlightButton = styled(Button)`
- display: grid;
- justify-content: center;
- align-items: center;
- grid-template-columns: 30px 1fr;
- margin-left: 20px;
- order: 1;
- width: fit-content;
-`;
-
-//#endregion
-
-//#region Path Formatting
-
-const filenameToLanguageMap: any = {
- py: "python",
- js: "javascript",
- ts: "typescript",
- html: "html",
- css: "css",
- java: "java",
- c: "c",
- cpp: "cpp",
- cs: "csharp",
- go: "go",
- rb: "ruby",
- rs: "rust",
- swift: "swift",
- php: "php",
- scala: "scala",
- kt: "kotlin",
- dart: "dart",
- hs: "haskell",
- lua: "lua",
- pl: "perl",
- r: "r",
- sql: "sql",
- vb: "vb",
- xml: "xml",
- yaml: "yaml",
-};
-
-function filenameToLanguage(filename: string): string {
- const extension = filename.split(".").pop();
- if (extension === undefined) {
- return "";
- }
- return filenameToLanguageMap[extension] || "";
-}
-
-function formatPathRelativeToWorkspace(
- path: string,
- workspacePath: string | undefined
-) {
- if (workspacePath === undefined) {
- return path;
- }
- if (path.startsWith(workspacePath)) {
- return path.substring(workspacePath.length + 1);
- } else {
- return path;
- }
-}
-
-function formatFileRange(
- rangeInFile: RangeInFile,
- workspacePath: string | undefined
-) {
- return `${formatPathRelativeToWorkspace(
- rangeInFile.filepath,
- workspacePath
- )} (lines ${rangeInFile.range.start.line + 1}-${
- rangeInFile.range.end.line + 1
- })`;
- // +1 because VS Code Ranges are 0-indexed
-}
-
-//#endregion
-
-function CodeMultiselect(props: {}) {
- // State
- const [highlightLocked, setHighlightLocked] = useState(true);
-
- // Redux
- const dispatch = useDispatch();
- const workspacePath = useSelector(
- (state: RootStore) => state.config.workspacePath
- );
- const debugContext = useSelector(selectDebugContext);
- const rangesInFiles = useSelector(selectAllRangesInFiles);
- const rangesInFilesMask = useSelector(selectRangesMask);
-
- useEffect(() => {
- let eventListener = (event: any) => {
- switch (event.data.type) {
- case "highlightedCode":
- if (!highlightLocked) {
- dispatch(
- addRangeInFile({
- rangeInFile: event.data.rangeInFile,
- canUpdateLast: true,
- })
- );
- dispatch(updateFileSystem(event.data.filesystem));
- }
- break;
- case "findSuspiciousCode":
- for (let c of event.data.codeLocations) {
- dispatch(addRangeInFile({ rangeInFile: c, canUpdateLast: false }));
- }
- dispatch(updateFileSystem(event.data.filesystem));
- postVscMessage("listTenThings", { debugContext });
- break;
- }
- };
- window.addEventListener("message", eventListener);
- return () => window.removeEventListener("message", eventListener);
- }, [debugContext, highlightLocked]);
-
- useEffect(() => {
- hljs.highlightAll();
- }, [rangesInFiles]);
-
- return (
- <MultiSelectContainer>
- {rangesInFiles.map((range: RangeInFile, index: number) => {
- return (
- <MultiSelectOption
- key={index}
- style={{
- border: `1px solid ${
- rangesInFilesMask[index] ? buttonColor : "gray"
- }`,
- }}
- onClick={() => {
- dispatch(toggleSelectionAt(index));
- }}
- >
- <MultiSelectHeader>
- <p style={{ margin: "4px" }}>
- {formatFileRange(range, workspacePath)}
- </p>
- <DeleteSelectedRangeButton
- onClick={() => dispatch(deleteRangeInFileAt(index))}
- >
- x
- </DeleteSelectedRangeButton>
- </MultiSelectHeader>
- <pre>
- <code
- className={"language-" + filenameToLanguage(range.filepath)}
- >
- {readRangeInVirtualFileSystem(range, debugContext.filesystem)}
- </code>
- </pre>
- </MultiSelectOption>
- );
- })}
- {rangesInFiles.length === 0 && (
- <>
- <p>Highlight relevant code in the editor.</p>
- </>
- )}
- <ToggleHighlightButton
- onClick={() => {
- setHighlightLocked(!highlightLocked);
- }}
- >
- {highlightLocked ? (
- <>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="20px"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth="1.5"
- stroke="currentColor"
- className="w-6 h-6"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"
- />
- </svg>{" "}
- Enable Highlight
- </>
- ) : (
- <>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="20px"
- fill="none"
- viewBox="0 0 24 24"
- strokeWidth="1.5"
- stroke="currentColor"
- className="w-6 h-6"
- >
- <path
- strokeLinecap="round"
- strokeLinejoin="round"
- d="M13.5 10.5V6.75a4.5 4.5 0 119 0v3.75M3.75 21.75h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H3.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"
- />
- </svg>{" "}
- Disable Highlight
- </>
- )}
- </ToggleHighlightButton>
- </MultiSelectContainer>
- );
-}
-
-export default CodeMultiselect;
diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx
index 801c3a03..f11e07af 100644
--- a/extension/react-app/src/components/ComboBox.tsx
+++ b/extension/react-app/src/components/ComboBox.tsx
@@ -1,30 +1,19 @@
-import React, {
- useCallback,
- useEffect,
- useImperativeHandle,
- useState,
-} from "react";
+import React, { useEffect, useImperativeHandle, useState } from "react";
import { useCombobox } from "downshift";
import styled from "styled-components";
import {
- buttonColor,
defaultBorderRadius,
lightGray,
secondaryDark,
vscBackground,
} from ".";
import CodeBlock from "./CodeBlock";
-import { RangeInFile } from "../../../src/client";
import PillButton from "./PillButton";
import HeaderButtonWithText from "./HeaderButtonWithText";
-import {
- Trash,
- LockClosed,
- LockOpen,
- Plus,
- DocumentPlus,
-} from "@styled-icons/heroicons-outline";
+import { DocumentPlus } from "@styled-icons/heroicons-outline";
import { HighlightedRangeContext } from "../../../schema/FullState";
+import { postVscMessage } from "../vscode";
+import { getMetaKeyLabel } from "../util";
// #region styled components
const mainInputFontSize = 13;
@@ -180,6 +169,27 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
useImperativeHandle(ref, () => downshiftProps, [downshiftProps]);
+ const [metaKeyPressed, setMetaKeyPressed] = useState(false);
+ const [focused, setFocused] = useState(false);
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Meta") {
+ setMetaKeyPressed(true);
+ }
+ };
+ const handleKeyUp = (e: KeyboardEvent) => {
+ if (e.key === "Meta") {
+ setMetaKeyPressed(false);
+ }
+ };
+ window.addEventListener("keydown", handleKeyDown);
+ window.addEventListener("keyup", handleKeyUp);
+ return () => {
+ window.removeEventListener("keydown", handleKeyDown);
+ window.removeEventListener("keyup", handleKeyUp);
+ };
+ });
+
useEffect(() => {
if (!inputRef.current) {
return;
@@ -221,6 +231,11 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
)} */}
{highlightedCodeSections.map((section, idx) => (
<PillButton
+ warning={
+ section.range.contents.length > 4000 && section.editing
+ ? "Editing such a large range may be slow"
+ : undefined
+ }
editing={section.editing}
pinned={section.pinned}
index={idx}
@@ -272,7 +287,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
<div className="flex px-2" ref={divRef} hidden={!downshiftProps.isOpen}>
<MainTextInput
disabled={props.disabled}
- placeholder="Ask a question, give instructions, or type '/' to see slash commands"
+ placeholder={`Ask a question, give instructions, or type '/' to see slash commands. ${getMetaKeyLabel()}⏎ to edit.`}
{...getInputProps({
onChange: (e) => {
const target = e.target as HTMLTextAreaElement;
@@ -285,6 +300,13 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
// setShowContextDropdown(target.value.endsWith("@"));
},
+ onFocus: (e) => {
+ setFocused(true);
+ },
+ onBlur: (e) => {
+ setFocused(false);
+ postVscMessage("blurContinueInput", {});
+ },
onKeyDown: (event) => {
if (event.key === "Enter" && event.shiftKey) {
// Prevent Downshift's default 'Enter' behavior.
@@ -311,7 +333,6 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
) {
(event.nativeEvent as any).preventDownshiftDefault = true;
} else if (event.key === "ArrowUp") {
- console.log("OWJFOIJO");
if (positionInHistory == 0) return;
else if (
positionInHistory == history.length &&
@@ -357,10 +378,15 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
))}
</Ul>
</div>
- {/* <span className="text-trueGray-400 ml-auto m-auto text-xs text-right">
- Highlight code to include as context. Currently open file included by
- default. {highlightedCodeSections.length === 0 && ""}
- </span> */}
+ {highlightedCodeSections.length === 0 &&
+ (downshiftProps.inputValue?.startsWith("/edit") ||
+ (focused &&
+ metaKeyPressed &&
+ downshiftProps.inputValue?.length > 0)) && (
+ <div className="text-trueGray-400 pr-4 text-xs text-right">
+ Inserting at cursor
+ </div>
+ )}
<ContextDropdown
onMouseEnter={() => {
setHoveringContextDropdown(true);
diff --git a/extension/react-app/src/components/LoadingCover.tsx b/extension/react-app/src/components/LoadingCover.tsx
deleted file mode 100644
index a0f8f7a2..00000000
--- a/extension/react-app/src/components/LoadingCover.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from "react";
-import styled from "styled-components";
-
-const StyledDiv = styled.div`
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100vh;
- background: linear-gradient(
- 101.79deg,
- #12887a 0%,
- #87245c 32%,
- #e12637 63%,
- #ffb215 100%
- );
- display: flex;
- justify-content: center;
- align-items: center;
- flex-direction: column;
- z-index: 10;
-`;
-
-const StyledImg = styled.img`
- /* add your styles here */
-`;
-
-const StyledDiv2 = styled.div`
- width: 50%;
- height: 5px;
- background: white;
- margin-top: 20px;
-`;
-
-interface LoadingCoverProps {
- message: string;
- hidden?: boolean;
-}
-
-const LoadingCover = (props: LoadingCoverProps) => {
- return (
- <StyledDiv style={{ display: props.hidden ? "none" : "inherit" }}>
- <StyledImg src="continue.gif" alt="centered image" width="50%" />
- <StyledDiv2></StyledDiv2>
- <p>{props.message}</p>
- </StyledDiv>
- );
-};
-
-export default LoadingCover;
diff --git a/extension/react-app/src/components/Onboarding.tsx b/extension/react-app/src/components/Onboarding.tsx
new file mode 100644
index 00000000..231c1e93
--- /dev/null
+++ b/extension/react-app/src/components/Onboarding.tsx
@@ -0,0 +1,136 @@
+import { useSelector } from "react-redux";
+import { RootStore } from "../redux/store";
+import React, { useState, useEffect } from "react";
+import styled from "styled-components";
+import { ArrowLeft, ArrowRight } from "@styled-icons/heroicons-outline";
+import { defaultBorderRadius } from ".";
+import Loader from "./Loader";
+
+const StyledDiv = styled.div`
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #1e1e1e;
+ z-index: 200;
+`;
+
+const StyledSpan = styled.span`
+ padding: 8px;
+ border-radius: ${defaultBorderRadius};
+ &:hover {
+ background-color: #ffffff33;
+ }
+ white-space: nowrap;
+`;
+
+const Onboarding = () => {
+ const [counter, setCounter] = useState(4);
+ const gifs = ["intro", "highlight", "question", "help"];
+ const topMessages = [
+ "Welcome!",
+ "Highlight code",
+ "Ask a question",
+ "Use /help to learn more",
+ ];
+
+ useEffect(() => {
+ const hasVisited = localStorage.getItem("hasVisited");
+ if (hasVisited) {
+ setCounter(4);
+ } else {
+ setCounter(0);
+ localStorage.setItem("hasVisited", "true");
+ }
+ }, []);
+
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ setLoading(true);
+ }, [counter]);
+
+ return (
+ <StyledDiv hidden={counter >= 4}>
+ <div
+ style={{
+ display: "grid",
+ justifyContent: "center",
+ alignItems: "center",
+ height: "100%",
+ textAlign: "center",
+ background: `linear-gradient(
+ 101.79deg,
+ #12887a66 0%,
+ #87245c66 32%,
+ #e1263766 63%,
+ #ffb21566 100%
+ )`,
+ paddingLeft: "16px",
+ paddingRight: "16px",
+ }}
+ >
+ <h1>{topMessages[counter]}</h1>
+ <div style={{ display: "flex", justifyContent: "center" }}>
+ {loading && (
+ <div style={{ margin: "auto", position: "absolute", zIndex: 0 }}>
+ <Loader />
+ </div>
+ )}
+ {counter % 2 === 0 ? (
+ <img
+ src={`https://github.com/continuedev/continue/blob/main/media/${gifs[counter]}.gif?raw=true`}
+ width="100%"
+ key={"even-gif"}
+ alt={topMessages[counter]}
+ onLoad={() => {
+ setLoading(false);
+ }}
+ style={{ zIndex: 1 }}
+ />
+ ) : (
+ <img
+ src={`https://github.com/continuedev/continue/blob/main/media/${gifs[counter]}.gif?raw=true`}
+ width="100%"
+ key={"odd-gif"}
+ alt={topMessages[counter]}
+ onLoad={() => {
+ setLoading(false);
+ }}
+ style={{ zIndex: 1 }}
+ />
+ )}
+ </div>
+ <p
+ style={{
+ paddingLeft: "50px",
+ paddingRight: "50px",
+ paddingBottom: "50px",
+ textAlign: "center",
+ cursor: "pointer",
+ whiteSpace: "nowrap",
+ }}
+ >
+ <StyledSpan
+ hidden={counter === 0}
+ onClick={() => setCounter((prev) => Math.max(prev - 1, 0))}
+ >
+ <ArrowLeft width="18px" strokeWidth="2px" /> Previous
+ </StyledSpan>
+ <span hidden={counter === 0}>{" | "}</span>
+ <StyledSpan onClick={() => setCounter((prev) => prev + 1)}>
+ {counter === 0
+ ? "Click to learn how to use Continue"
+ : counter === 3
+ ? "Get Started"
+ : "Next"}{" "}
+ <ArrowRight width="18px" strokeWidth="2px" />
+ </StyledSpan>
+ </p>
+ </div>
+ </StyledDiv>
+ );
+};
+
+export default Onboarding;
diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx
index 31d98c0f..d9d779d1 100644
--- a/extension/react-app/src/components/PillButton.tsx
+++ b/extension/react-app/src/components/PillButton.tsx
@@ -1,12 +1,11 @@
import { useContext, useState } from "react";
import styled from "styled-components";
+import { StyledTooltip, defaultBorderRadius, secondaryDark } from ".";
import {
- StyledTooltip,
- defaultBorderRadius,
- lightGray,
- secondaryDark,
-} from ".";
-import { Trash, PaintBrush, MapPin } from "@styled-icons/heroicons-outline";
+ Trash,
+ PaintBrush,
+ ExclamationTriangle,
+} from "@styled-icons/heroicons-outline";
import { GUIClientContext } from "../App";
const Button = styled.button`
@@ -31,7 +30,6 @@ const GridDiv = styled.div`
grid-template-columns: 1fr 1fr;
align-items: center;
border-radius: ${defaultBorderRadius};
- overflow: hidden;
background-color: ${secondaryDark};
`;
@@ -48,6 +46,21 @@ const ButtonDiv = styled.div<{ backgroundColor: string }>`
}
`;
+const CircleDiv = styled.div`
+ position: absolute;
+ top: -10px;
+ right: -10px;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ background-color: red;
+ color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 2px;
+`;
+
interface PillButtonProps {
onHover?: (arg0: boolean) => void;
onDelete?: () => void;
@@ -55,6 +68,7 @@ interface PillButtonProps {
index: number;
editing: boolean;
pinned: boolean;
+ warning?: string;
}
const PillButton = (props: PillButtonProps) => {
@@ -63,75 +77,96 @@ const PillButton = (props: PillButtonProps) => {
return (
<>
- <Button
- style={{
- position: "relative",
- borderColor: props.editing
- ? "#8800aa"
- : props.pinned
- ? "#ffff0099"
- : "transparent",
- borderWidth: "1px",
- borderStyle: "solid",
- }}
- onMouseEnter={() => {
- setIsHovered(true);
- if (props.onHover) {
- props.onHover(true);
- }
- }}
- onMouseLeave={() => {
- setIsHovered(false);
- if (props.onHover) {
- props.onHover(false);
- }
- }}
- >
- {isHovered && (
- <GridDiv>
- <ButtonDiv
- data-tooltip-id={`edit-${props.index}`}
- backgroundColor={"#8800aa55"}
- onClick={() => {
- client?.setEditingAtIndices([props.index]);
- }}
- >
- <PaintBrush style={{ margin: "auto" }} width="1.6em"></PaintBrush>
- </ButtonDiv>
+ <div style={{ position: "relative" }}>
+ <Button
+ style={{
+ position: "relative",
+ borderColor: props.warning
+ ? "red"
+ : props.editing
+ ? "#8800aa"
+ : props.pinned
+ ? "#ffff0099"
+ : "transparent",
+ borderWidth: "1px",
+ borderStyle: "solid",
+ }}
+ onMouseEnter={() => {
+ setIsHovered(true);
+ if (props.onHover) {
+ props.onHover(true);
+ }
+ }}
+ onMouseLeave={() => {
+ setIsHovered(false);
+ if (props.onHover) {
+ props.onHover(false);
+ }
+ }}
+ >
+ {isHovered && (
+ <GridDiv>
+ <ButtonDiv
+ data-tooltip-id={`edit-${props.index}`}
+ backgroundColor={"#8800aa55"}
+ onClick={() => {
+ client?.setEditingAtIndices([props.index]);
+ }}
+ >
+ <PaintBrush
+ style={{ margin: "auto" }}
+ width="1.6em"
+ ></PaintBrush>
+ </ButtonDiv>
- {/* <ButtonDiv
+ {/* <ButtonDiv
data-tooltip-id={`pin-${props.index}`}
backgroundColor={"#ffff0055"}
onClick={() => {
client?.setPinnedAtIndices([props.index]);
}}
- >
+ >
<MapPin style={{ margin: "auto" }} width="1.6em"></MapPin>
</ButtonDiv> */}
- <StyledTooltip id={`pin-${props.index}`}>
- Edit this range
+ <StyledTooltip id={`pin-${props.index}`}>
+ Edit this range
+ </StyledTooltip>
+ <ButtonDiv
+ data-tooltip-id={`delete-${props.index}`}
+ backgroundColor={"#cc000055"}
+ onClick={() => {
+ if (props.onDelete) {
+ props.onDelete();
+ }
+ }}
+ >
+ <Trash style={{ margin: "auto" }} width="1.6em"></Trash>
+ </ButtonDiv>
+ </GridDiv>
+ )}
+ {props.title}
+ </Button>
+ <StyledTooltip id={`edit-${props.index}`}>
+ {props.editing
+ ? "Editing this range (with rest of file as context)"
+ : "Edit this range"}
+ </StyledTooltip>
+ <StyledTooltip id={`delete-${props.index}`}>Delete</StyledTooltip>
+ {props.warning && (
+ <>
+ <CircleDiv data-tooltip-id={`circle-div-${props.title}`}>
+ <ExclamationTriangle
+ style={{ margin: "auto" }}
+ width="1.0em"
+ strokeWidth={2}
+ />
+ </CircleDiv>
+ <StyledTooltip id={`circle-div-${props.title}`}>
+ {props.warning}
</StyledTooltip>
- <ButtonDiv
- data-tooltip-id={`delete-${props.index}`}
- backgroundColor={"#cc000055"}
- onClick={() => {
- if (props.onDelete) {
- props.onDelete();
- }
- }}
- >
- <Trash style={{ margin: "auto" }} width="1.6em"></Trash>
- </ButtonDiv>
- </GridDiv>
+ </>
)}
- {props.title}
- </Button>
- <StyledTooltip id={`edit-${props.index}`}>
- {props.editing
- ? "Editing this range (with rest of file as context)"
- : "Edit this range"}
- </StyledTooltip>
- <StyledTooltip id={`delete-${props.index}`}>Delete</StyledTooltip>
+ </div>
</>
);
};
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx
index d480c565..93bdbc89 100644
--- a/extension/react-app/src/components/StepContainer.tsx
+++ b/extension/react-app/src/components/StepContainer.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import styled, { keyframes } from "styled-components";
import {
appear,
@@ -15,9 +15,9 @@ import {
} from "@styled-icons/heroicons-outline";
import { StopCircle } from "@styled-icons/heroicons-solid";
import { HistoryNode } from "../../../schema/HistoryNode";
-import ReactMarkdown from "react-markdown";
import HeaderButtonWithText from "./HeaderButtonWithText";
-import CodeBlock from "./CodeBlock";
+import MarkdownPreview from "@uiw/react-markdown-preview";
+import { getMetaKeyLabel, isMetaEquivalentKeyPressed } from "../util";
interface StepContainerProps {
historyNode: HistoryNode;
@@ -72,19 +72,6 @@ const ContentDiv = styled.div<{ isUserInput: boolean }>`
font-size: 13px;
`;
-const MarkdownPre = styled.pre`
- background-color: ${secondaryDark};
- padding: 10px;
- border-radius: ${defaultBorderRadius};
- border: 0.5px solid white;
-`;
-
-const StyledCode = styled.code`
- word-wrap: break-word;
- color: #f69292;
- background: transparent;
-`;
-
const gradient = keyframes`
0% {
background-position: 0px 0;
@@ -124,6 +111,31 @@ const GradientBorder = styled.div<{
background-size: 200% 200%;
`;
+const StyledMarkdownPreview = styled(MarkdownPreview)`
+ pre {
+ background-color: ${secondaryDark};
+ padding: 1px;
+ border-radius: ${defaultBorderRadius};
+ border: 0.5px solid white;
+ }
+
+ code {
+ color: #f69292;
+ word-wrap: break-word;
+ }
+
+ pre > code {
+ background-color: ${secondaryDark};
+ color: white;
+ }
+
+ background-color: ${vscBackground};
+ font-family: "Lexend", sans-serif;
+ font-size: 13px;
+ padding: 8px;
+ color: white;
+`;
+
// #endregion
function StepContainer(props: StepContainerProps) {
@@ -158,7 +170,7 @@ function StepContainer(props: StepContainerProps) {
>
<StepContainerDiv open={props.open}>
<GradientBorder
- loading={props.historyNode.active as boolean | false}
+ loading={(props.historyNode.active as boolean) || false}
isFirst={props.isFirst}
isLast={props.isLast}
borderColor={
@@ -170,7 +182,7 @@ function StepContainer(props: StepContainerProps) {
}
className="overflow-hidden cursor-pointer"
onClick={(e) => {
- if (e.metaKey) {
+ if (isMetaEquivalentKeyPressed(e)) {
props.onToggleAll();
} else {
props.onToggle();
@@ -178,7 +190,7 @@ function StepContainer(props: StepContainerProps) {
}}
>
<HeaderDiv
- loading={props.historyNode.active as boolean | false}
+ loading={(props.historyNode.active as boolean) || false}
error={props.historyNode.observation?.error ? true : false}
>
<div className="m-2">
@@ -206,7 +218,11 @@ function StepContainer(props: StepContainerProps) {
e.stopPropagation();
props.onDelete();
}}
- text={props.historyNode.active ? "Stop (⌘⌫)" : "Delete"}
+ text={
+ props.historyNode.active
+ ? `Stop (${getMetaKeyLabel()}⌫)`
+ : "Delete"
+ }
>
{props.historyNode.active ? (
<StopCircle size="1.6em" onClick={props.onDelete} />
@@ -242,31 +258,16 @@ function StepContainer(props: StepContainerProps) {
)}
{props.historyNode.observation?.error ? (
- <pre className="overflow-x-scroll">
- {props.historyNode.observation.error as string}
- </pre>
+ <details>
+ <summary>View Traceback</summary>
+ <pre className="overflow-x-scroll">
+ {props.historyNode.observation.error as string}
+ </pre>
+ </details>
) : (
- <ReactMarkdown
- key={1}
- className="overflow-x-scroll"
- components={{
- pre: ({ node, ...props }) => {
- return (
- <CodeBlock
- children={(props.children[0] as any).props.children[0]}
- />
- );
- },
- code: ({ node, ...props }) => {
- return <StyledCode children={props.children[0] as any} />;
- },
- ul: ({ node, ...props }) => {
- return <ul className="ml-0" {...props} />;
- },
- }}
- >
- {props.historyNode.step.description as any}
- </ReactMarkdown>
+ <StyledMarkdownPreview
+ source={props.historyNode.step.description || ""}
+ />
)}
</ContentDiv>
</StepContainerDiv>
diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx
index ea5727f0..646d6846 100644
--- a/extension/react-app/src/components/TextDialog.tsx
+++ b/extension/react-app/src/components/TextDialog.tsx
@@ -2,6 +2,7 @@
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import { Button, buttonColor, secondaryDark, vscBackground } from ".";
+import { isMetaEquivalentKeyPressed } from "../util";
const ScreenCover = styled.div`
position: absolute;
@@ -81,7 +82,11 @@ const TextDialog = (props: {
rows={10}
ref={textAreaRef}
onKeyDown={(e) => {
- if (e.key === "Enter" && e.metaKey && textAreaRef.current) {
+ if (
+ e.key === "Enter" &&
+ isMetaEquivalentKeyPressed(e) &&
+ textAreaRef.current
+ ) {
props.onEnter(textAreaRef.current.value);
setText("");
} else if (e.key === "Escape") {
diff --git a/extension/react-app/src/highlight/dark.min.css b/extension/react-app/src/highlight/dark.min.css
deleted file mode 100644
index 9268d7c9..00000000
--- a/extension/react-app/src/highlight/dark.min.css
+++ /dev/null
@@ -1,53 +0,0 @@
-pre code.hljs {
- display: block;
- overflow-x: auto;
- padding: 1em;
-}
-code.hljs {
- padding: 3px 5px;
-}
-.hljs {
- color: #ddd;
- background: #252526;
-}
-.hljs-keyword,
-.hljs-link,
-.hljs-literal,
-.hljs-section,
-.hljs-selector-tag {
- color: #fff;
-}
-.hljs-addition,
-.hljs-attribute,
-.hljs-built_in,
-.hljs-bullet,
-.hljs-name,
-.hljs-string,
-.hljs-symbol,
-.hljs-template-tag,
-.hljs-template-variable,
-.hljs-title,
-.hljs-type,
-.hljs-variable {
- color: #d88;
-}
-.hljs-comment,
-.hljs-deletion,
-.hljs-meta,
-.hljs-quote {
- color: #979797;
-}
-.hljs-doctag,
-.hljs-keyword,
-.hljs-literal,
-.hljs-name,
-.hljs-section,
-.hljs-selector-tag,
-.hljs-strong,
-.hljs-title,
-.hljs-type {
- font-weight: 700;
-}
-.hljs-emphasis {
- font-style: italic;
-}
diff --git a/extension/react-app/src/hooks/messenger.ts b/extension/react-app/src/hooks/messenger.ts
index e2a0bab8..00ce1fbb 100644
--- a/extension/react-app/src/hooks/messenger.ts
+++ b/extension/react-app/src/hooks/messenger.ts
@@ -1,6 +1,3 @@
-// console.log("Websocket import");
-// const WebSocket = require("ws");
-
export abstract class Messenger {
abstract send(messageType: string, data: object): void;
@@ -28,13 +25,6 @@ export class WebsocketMessenger extends Messenger {
private serverUrl: string;
_newWebsocket(): WebSocket {
- // // Dynamic import, because WebSocket is builtin with browser, but not with node. And can't use require in browser.
- // if (typeof process === "object") {
- // console.log("Using node");
- // // process is only available in Node
- // var WebSocket = require("ws");
- // }
-
const newWebsocket = new WebSocket(this.serverUrl);
for (const listener of this.onOpenListeners) {
this.onOpen(listener);
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/pages/gui.tsx
index e1ecec9e..64207487 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/pages/gui.tsx
@@ -20,9 +20,10 @@ import ReactSwitch from "react-switch";
import { usePostHog } from "posthog-js/react";
import { useSelector } from "react-redux";
import { RootStore } from "../redux/store";
-import LoadingCover from "../components/LoadingCover";
import { postVscMessage } from "../vscode";
import UserInputContainer from "../components/UserInputContainer";
+import Onboarding from "../components/Onboarding";
+import { isMetaEquivalentKeyPressed } from "../util";
const TopGUIDiv = styled.div`
overflow: hidden;
@@ -95,11 +96,8 @@ function GUI(props: GUIProps) {
name: "Welcome to Continue",
hide: false,
description: `- Highlight code and ask a question or give instructions
-- Use \`cmd+k\` (Mac) / \`ctrl+k\` (Windows) to open Continue
-- Use \`cmd+shift+e\` / \`ctrl+shift+e\` to open file Explorer
-- Add your own OpenAI API key to VS Code Settings with \`cmd+,\`
-- Use slash commands when you want fine-grained control
-- Past steps are included as part of the context by default`,
+ - Use \`cmd+m\` (Mac) / \`ctrl+m\` (Windows) to open Continue
+ - Use \`/help\` to ask questions about how to use Continue`,
system_message: null,
chat_context: [],
manage_own_chat_context: false,
@@ -140,13 +138,14 @@ function GUI(props: GUIProps) {
useEffect(() => {
const listener = (e: any) => {
// Cmd + i to toggle fast model
- if (e.key === "i" && e.metaKey && e.shiftKey) {
+ if (e.key === "i" && isMetaEquivalentKeyPressed(e) && e.shiftKey) {
setUsingFastModel((prev) => !prev);
// Cmd + backspace to stop currently running step
} else if (
e.key === "Backspace" &&
- e.metaKey &&
- typeof history?.current_index !== "undefined"
+ isMetaEquivalentKeyPressed(e) &&
+ typeof history?.current_index !== "undefined" &&
+ history.timeline[history.current_index]?.active
) {
client?.deleteAtIndex(history.current_index);
}
@@ -169,6 +168,7 @@ function GUI(props: GUIProps) {
const waitingForSteps =
state.active &&
state.history.current_index < state.history.timeline.length &&
+ state.history.timeline[state.history.current_index] &&
state.history.timeline[
state.history.current_index
].step.description?.trim() === "";
@@ -221,7 +221,7 @@ function GUI(props: GUIProps) {
if (mainTextInputRef.current) {
let input = (mainTextInputRef.current as any).inputValue;
// cmd+enter to /edit
- if (event?.metaKey) {
+ if (isMetaEquivalentKeyPressed(event)) {
input = `/edit ${input}`;
}
(mainTextInputRef.current as any).setInputValue("");
@@ -235,14 +235,14 @@ function GUI(props: GUIProps) {
history.current_index < history.timeline.length
) {
if (
- history.timeline[history.current_index].step.name ===
+ history.timeline[history.current_index]?.step.name ===
"Waiting for user input"
) {
if (input.trim() === "") return;
onStepUserInput(input, history!.current_index);
return;
} else if (
- history.timeline[history.current_index].step.name ===
+ history.timeline[history.current_index]?.step.name ===
"Waiting for user confirmation"
) {
onStepUserInput("ok", history!.current_index);
@@ -260,14 +260,13 @@ function GUI(props: GUIProps) {
const onStepUserInput = (input: string, index: number) => {
if (!client) return;
- console.log("Sending step user input", input, index);
client.sendStepUserInput(input, index);
};
// const iterations = useSelector(selectIterations);
return (
<>
- <LoadingCover hidden={true} message="Downloading local model..." />
+ <Onboarding />
<TextDialog
showDialog={showFeedbackDialog}
onEnter={(text) => {
@@ -278,7 +277,7 @@ function GUI(props: GUIProps) {
setShowFeedbackDialog(false);
}}
message={feedbackDialogMessage}
- ></TextDialog>
+ />
<TopGUIDiv
ref={topGuiDivRef}
@@ -348,12 +347,6 @@ function GUI(props: GUIProps) {
</div>
<ComboBox
- // disabled={
- // history?.timeline.length
- // ? history.timeline[history.current_index].step.name ===
- // "Waiting for user confirmation"
- // : false
- // }
ref={mainTextInputRef}
onEnter={(e) => {
onMainTextInput(e);
@@ -438,7 +431,7 @@ function GUI(props: GUIProps) {
if (!usingFastModel) {
// Show the dialog
setFeedbackDialogMessage(
- "We don't yet support local models, but we're working on it! If privacy is a concern of yours, please use the feedback button in the bottom right to let us know."
+ "We don't yet support local models, but we're working on it! If privacy is a concern of yours, please write a short note to let us know."
);
setShowFeedbackDialog(true);
}
diff --git a/extension/react-app/src/tabs/additionalContext.tsx b/extension/react-app/src/tabs/additionalContext.tsx
deleted file mode 100644
index 98fce9f1..00000000
--- a/extension/react-app/src/tabs/additionalContext.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-import { H3, TextArea } from "../components";
-
-function AdditionalContextTab() {
- return (
- <div className="mx-5">
- <H3>Additional Context</H3>
- <TextArea
- rows={8}
- placeholder="Copy and paste information related to the bug from GitHub Issues, Slack threads, or other notes here."
- className="additionalContextTextarea"
- ></TextArea>
- <br></br>
- </div>
- );
-}
-
-export default AdditionalContextTab;
diff --git a/extension/react-app/src/tabs/chat/MessageDiv.tsx b/extension/react-app/src/tabs/chat/MessageDiv.tsx
deleted file mode 100644
index 3543dd93..00000000
--- a/extension/react-app/src/tabs/chat/MessageDiv.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import React, { useEffect } from "react";
-import { ChatMessage } from "../../redux/store";
-import styled from "styled-components";
-import {
- buttonColor,
- defaultBorderRadius,
- secondaryDark,
-} from "../../components";
-import VSCodeFileLink from "../../components/VSCodeFileLink";
-import ReactMarkdown from "react-markdown";
-import "../../highlight/dark.min.css";
-import hljs from "highlight.js";
-import { useSelector } from "react-redux";
-import { selectIsStreaming } from "../../redux/selectors/chatSelectors";
-
-const Container = styled.div`
- padding-left: 8px;
- padding-right: 8px;
- border-radius: 8px;
- margin: 3px;
- width: fit-content;
- max-width: 75%;
- overflow-y: scroll;
- word-wrap: break-word;
- -ms-word-wrap: break-word;
- height: fit-content;
- overflow: hidden;
- background-color: ${(props) => {
- if (props.role === "user") {
- return buttonColor;
- } else {
- return secondaryDark;
- }
- }};
- float: ${(props) => {
- if (props.role === "user") {
- return "right";
- } else {
- return "left";
- }
- }};
- display: block;
-
- & pre {
- border: 1px solid gray;
- border-radius: ${defaultBorderRadius};
- }
-`;
-
-function MessageDiv(props: ChatMessage) {
- const [richContent, setRichContent] = React.useState<JSX.Element[]>([]);
- const isStreaming = useSelector(selectIsStreaming);
-
- useEffect(() => {
- if (!isStreaming) {
- hljs.highlightAll();
- }
- }, [richContent, isStreaming]);
-
- useEffect(() => {
- setRichContent([
- <ReactMarkdown key={1} children={props.content}></ReactMarkdown>,
- ]);
- }, [props.content]);
-
- return (
- <>
- <div className="overflow-auto">
- <Container role={props.role}>{richContent}</Container>
- </div>
- </>
- );
-}
-
-export default MessageDiv;
diff --git a/extension/react-app/src/tabs/chat/index.tsx b/extension/react-app/src/tabs/chat/index.tsx
deleted file mode 100644
index a93ad4f9..00000000
--- a/extension/react-app/src/tabs/chat/index.tsx
+++ /dev/null
@@ -1,267 +0,0 @@
-import React, { useCallback, useEffect, useRef, useState } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import { selectChatMessages } from "../../redux/selectors/chatSelectors";
-import MessageDiv from "./MessageDiv";
-import styled from "styled-components";
-import { addMessage, setIsStreaming } from "../../redux/slices/chatSlice";
-import { AnyAction, Dispatch } from "@reduxjs/toolkit";
-import { closeStream, streamUpdate } from "../../redux/slices/chatSlice";
-import { ChatMessage, RootStore } from "../../redux/store";
-import { postVscMessage, vscRequest } from "../../vscode";
-import { defaultBorderRadius, Loader } from "../../components";
-import { selectHighlightedCode } from "../../redux/selectors/miscSelectors";
-import { readRangeInVirtualFileSystem } from "../../util";
-import { selectDebugContext } from "../../redux/selectors/debugContextSelectors";
-
-let textEntryBarHeight = "30px";
-
-const ChatContainer = styled.div`
- display: grid;
- grid-template-rows: 1fr auto;
- height: 100%;
-`;
-
-const BottomDiv = styled.div`
- display: grid;
- grid-template-rows: auto auto;
-`;
-
-const BottomButton = styled.button(
- (props: { active: boolean }) => `
- font-size: 10px;
- border: none;
- color: white;
- margin-right: 4px;
- cursor: pointer;
- background-color: ${props.active ? "black" : "gray"};
- border-radius: ${defaultBorderRadius};
- padding: 8px;
-`
-);
-
-const TextEntryBar = styled.input`
- height: ${textEntryBarHeight};
- border-bottom-left-radius: ${defaultBorderRadius};
- border-bottom-right-radius: ${defaultBorderRadius};
- padding: 8px;
- border: 1px solid white;
- background-color: black;
- color: white;
- outline: none;
-`;
-
-function ChatTab() {
- const dispatch = useDispatch();
- const chatMessages = useSelector(selectChatMessages);
- const isStreaming = useSelector((state: RootStore) => state.chat.isStreaming);
- const baseUrl = useSelector((state: RootStore) => state.config.apiUrl);
- const debugContext = useSelector(selectDebugContext);
-
- const [includeHighlightedCode, setIncludeHighlightedCode] = useState(true);
- const [writeToEditor, setWriteToEditor] = useState(false);
- const [waitingForResponse, setWaitingForResponse] = useState(false);
-
- const highlightedCode = useSelector(selectHighlightedCode);
-
- const streamToStateThunk = useCallback(
- (dispatch: Dispatch<AnyAction>, getResponse: () => Promise<Response>) => {
- let streamToCursor = writeToEditor;
- getResponse().then((resp) => {
- setWaitingForResponse(false);
- if (resp.body) {
- resp.body.pipeTo(
- new WritableStream({
- write(chunk) {
- let update = new TextDecoder("utf-8").decode(chunk);
- dispatch(streamUpdate(update));
- if (streamToCursor) {
- postVscMessage("streamUpdate", { update });
- }
- },
- close() {
- dispatch(closeStream());
- if (streamToCursor) {
- postVscMessage("closeStream", null);
- }
- },
- })
- );
- }
- });
- },
- [writeToEditor]
- );
-
- const compileHiddenChatMessages = useCallback(async () => {
- let messages: ChatMessage[] = [];
- if (
- includeHighlightedCode &&
- highlightedCode?.filepath !== undefined &&
- highlightedCode?.range !== undefined &&
- debugContext.filesystem[highlightedCode.filepath] !== undefined
- ) {
- let fileContents = readRangeInVirtualFileSystem(
- highlightedCode,
- debugContext.filesystem
- );
- if (fileContents) {
- messages.push({
- role: "user",
- content: fileContents,
- });
- }
- } else {
- // Similarity search over workspace
- let data = await vscRequest("queryEmbeddings", {
- query: chatMessages[chatMessages.length - 1].content,
- });
- let codeContextMessages = data.results.map(
- (result: { id: string; document: string }) => {
- let msg: ChatMessage = {
- role: "user",
- content: `File: ${result.id} \n ${result.document}`,
- };
- return msg;
- }
- );
- codeContextMessages.push({
- role: "user",
- content:
- "Use the above code to help you answer the question below. Answer in asterisk bullet points, and give the full path whenever you reference files.",
- });
- messages.push(...codeContextMessages);
- }
-
- let systemMsgContent = writeToEditor
- ? "Respond only with the exact code requested, no additional text."
- : "Use the above code to help you answer the question below. Respond in markdown if using bullets or other special formatting, being sure to specify language for code blocks.";
-
- messages.push({
- role: "system",
- content: systemMsgContent,
- });
- return messages;
- }, [highlightedCode, chatMessages, includeHighlightedCode, writeToEditor]);
-
- useEffect(() => {
- if (
- chatMessages.length > 0 &&
- chatMessages[chatMessages.length - 1].role === "user" &&
- !isStreaming
- ) {
- dispatch(setIsStreaming(true));
- streamToStateThunk(dispatch, async () => {
- if (chatMessages.length === 0) {
- return new Promise((resolve, _) => resolve(new Response()));
- }
- let hiddenChatMessages = await compileHiddenChatMessages();
- let augmentedMessages = [
- ...chatMessages.slice(0, -1),
- ...hiddenChatMessages,
- chatMessages[chatMessages.length - 1],
- ];
- console.log(augmentedMessages);
- // The autogenerated client can't handle streams, so have to go raw
- return fetch(`${baseUrl}/chat/complete`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- messages: augmentedMessages,
- }),
- });
- });
- }
- }, [chatMessages, dispatch, isStreaming, highlightedCode]);
-
- const chatMessagesDiv = useRef<HTMLDivElement>(null);
- useEffect(() => {
- // Scroll to bottom
- let interval = setInterval(() => {
- if (chatMessagesDiv.current && !waitingForResponse) {
- chatMessagesDiv.current.scrollTop += Math.max(
- 4,
- 0.05 * chatMessagesDiv.current.scrollHeight -
- chatMessagesDiv.current.clientHeight -
- chatMessagesDiv.current.scrollTop
- );
- if (
- chatMessagesDiv.current.scrollTop >=
- chatMessagesDiv.current.scrollHeight -
- chatMessagesDiv.current.clientHeight
- ) {
- clearInterval(interval);
- }
- }
- }, 10);
- }, [chatMessages, chatMessagesDiv, waitingForResponse]);
-
- return (
- <ChatContainer>
- <div className="mx-5 overflow-y-scroll" ref={chatMessagesDiv}>
- <h1>Chat</h1>
- <hr></hr>
- <div>
- {chatMessages.length > 0 ? (
- chatMessages.map((message, idx) => {
- return <MessageDiv key={idx} {...message}></MessageDiv>;
- })
- ) : (
- <p className="text-gray-400 m-auto text-center">
- You can ask questions about your codebase or ask for code written
- directly in the editor.
- </p>
- )}
- {waitingForResponse && <Loader></Loader>}
- </div>
- </div>
-
- <BottomDiv>
- <div className="h-12 bg-secondary-">
- <div className="flex items-center p-2">
- {/* <p className="mr-auto text-xs">
- Highlighted code is automatically included in your chat message.
- </p> */}
- <BottomButton
- className="ml-auto"
- active={writeToEditor}
- onClick={() => {
- setWriteToEditor(!writeToEditor);
- }}
- >
- {writeToEditor ? "Writing to editor" : "Write to editor"}
- </BottomButton>
-
- <BottomButton
- active={includeHighlightedCode}
- onClick={() => {
- setIncludeHighlightedCode(!includeHighlightedCode);
- }}
- >
- {includeHighlightedCode
- ? "Including highlighted code"
- : "Automatically finding relevant code"}
- </BottomButton>
- </div>
- </div>
- <TextEntryBar
- type="text"
- placeholder="Enter your message here"
- onKeyDown={(e) => {
- if (e.key === "Enter" && e.currentTarget.value !== "") {
- console.log("Sending message", e.currentTarget.value);
- dispatch(
- addMessage({ content: e.currentTarget.value, role: "user" })
- );
- (e.target as any).value = "";
- setWaitingForResponse(true);
- }
- }}
- ></TextEntryBar>
- </BottomDiv>
- </ChatContainer>
- );
-}
-
-export default ChatTab;
diff --git a/extension/react-app/src/tabs/main.tsx b/extension/react-app/src/tabs/main.tsx
deleted file mode 100644
index a8b3300d..00000000
--- a/extension/react-app/src/tabs/main.tsx
+++ /dev/null
@@ -1,189 +0,0 @@
-import React, { useEffect, useState } from "react";
-import { H3, TextArea, Button, Pre, Loader } from "../components";
-import styled from "styled-components";
-import { postVscMessage, withProgress } from "../vscode";
-import { useDebugContextValue } from "../redux/hooks";
-import CodeMultiselect from "../components/CodeMultiselect";
-import { useSelector } from "react-redux";
-import { selectDebugContext } from "../redux/selectors/debugContextSelectors";
-import { useDispatch } from "react-redux";
-import { updateValue } from "../redux/slices/debugContexSlice";
-import { setWorkspacePath } from "../redux/slices/configSlice";
-import { SerializedDebugContext } from "../../../src/client";
-import { useEditCache } from "../util/editCache";
-import { useApi } from "../util/api";
-
-const ButtonDiv = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 4px;
- margin: 4px;
- flex-wrap: wrap;
-
- & button {
- flex-grow: 1;
- }
-`;
-
-function MainTab(props: any) {
- const dispatch = useDispatch();
-
- const [suggestion, setSuggestion] = useState("");
- const [traceback, setTraceback] = useDebugContextValue("traceback", "");
- const [selectedRanges, setSelectedRanges] = useDebugContextValue(
- "rangesInFiles",
- []
- );
-
- const editCache = useEditCache();
- const { debugApi } = useApi();
-
- const [responseLoading, setResponseLoading] = useState(false);
-
- let debugContext = useSelector(selectDebugContext);
-
- useEffect(() => {
- editCache.preloadEdit(debugContext);
- }, [debugContext]);
-
- function postVscMessageWithDebugContext(
- type: string,
- overrideDebugContext: SerializedDebugContext | null = null
- ) {
- postVscMessage(type, {
- debugContext: overrideDebugContext || debugContext,
- });
- }
-
- function launchFindSuspiciousCode(newTraceback: string) {
- // setTraceback's effects don't occur immediately, so we have to add it to the debug context manually
- let updatedDebugContext = {
- ...debugContext,
- traceback: newTraceback,
- };
- postVscMessageWithDebugContext("findSuspiciousCode", updatedDebugContext);
- postVscMessageWithDebugContext("preloadEdit", updatedDebugContext);
- }
-
- useEffect(() => {
- const eventListener = (event: any) => {
- switch (event.data.type) {
- case "suggestFix":
- case "explainCode":
- case "listTenThings":
- setSuggestion(event.data.value);
- setResponseLoading(false);
- break;
- case "traceback":
- setTraceback(event.data.value);
- launchFindSuspiciousCode(event.data.value);
- break;
- case "workspacePath":
- dispatch(setWorkspacePath(event.data.value));
- break;
- }
- };
- window.addEventListener("message", eventListener);
-
- return () => window.removeEventListener("message", eventListener);
- }, [debugContext, selectedRanges]);
-
- return (
- <div className="mx-5">
- <h1>Debug Panel</h1>
-
- <H3>Code Sections</H3>
- <CodeMultiselect></CodeMultiselect>
-
- <H3>Bug Description</H3>
- <TextArea
- id="bugDescription"
- name="bugDescription"
- className="bugDescription"
- rows={4}
- cols={50}
- placeholder="Describe your bug..."
- ></TextArea>
-
- <H3>Stack Trace</H3>
- <TextArea
- id="traceback"
- className="traceback"
- name="traceback"
- rows={4}
- cols={50}
- placeholder="Paste stack trace here"
- onChange={(e) => {
- setTraceback(e.target.value);
- dispatch(updateValue({ key: "traceback", value: e.target.value }));
- // postVscMessageWithDebugContext("findSuspiciousCode");
- }}
- onPaste={(e) => {
- let pasted = e.clipboardData.getData("text");
- console.log("PASTED", pasted);
- setTraceback(pasted);
- launchFindSuspiciousCode(pasted);
- }}
- value={traceback}
- ></TextArea>
-
- <select
- hidden
- id="relevantVars"
- className="relevantVars"
- name="relevantVars"
- ></select>
-
- <ButtonDiv>
- <Button
- onClick={() => {
- postVscMessageWithDebugContext("explainCode");
- setResponseLoading(true);
- }}
- >
- Explain Code
- </Button>
- <Button
- onClick={() => {
- postVscMessageWithDebugContext("suggestFix");
- setResponseLoading(true);
- }}
- >
- Generate Ideas
- </Button>
- <Button
- disabled={selectedRanges.length === 0}
- onClick={async () => {
- withProgress("Generating Fix", async () => {
- let edits = await editCache.getEdit(debugContext);
- postVscMessage("makeEdit", { edits });
- });
- }}
- >
- Suggest Fix
- </Button>
- <Button
- disabled={selectedRanges.length === 0}
- onClick={() => {
- postVscMessageWithDebugContext("generateUnitTest");
- }}
- >
- Create Test
- </Button>
- </ButtonDiv>
- <Loader hidden={!responseLoading}></Loader>
-
- <Pre
- className="fixSuggestion"
- hidden={!(typeof suggestion === "string" && suggestion.length > 0)}
- >
- {suggestion}
- </Pre>
-
- <br></br>
- </div>
- );
-}
-
-export default MainTab;
diff --git a/extension/react-app/src/tabs/welcome.tsx b/extension/react-app/src/tabs/welcome.tsx
deleted file mode 100644
index c29d260a..00000000
--- a/extension/react-app/src/tabs/welcome.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from "react";
-
-function WelcomeTab() {
- return (
- <div className="mx-5">
- <h1>Welcome to Continue</h1>
-
- <p>
- Learn more in the{" "}
- <a href="https://www.notion.so/continue-dev/Continue-User-Guide-1c6ad99887d0474d9e42206f6c98efa4">
- Continue User Guide
- </a>{" "}
- </p>
- <p>Send Nate or Ty your feedback:</p>
- <p>1. What excites you about Continue?</p>
- <p>2. What did you struggle with when using Continue?</p>
- <p>3. How do you wish Continue worked?</p>
- </div>
- );
-}
-
-export default WelcomeTab;
diff --git a/extension/react-app/src/util/api.ts b/extension/react-app/src/util/api.ts
deleted file mode 100644
index bdec1d20..00000000
--- a/extension/react-app/src/util/api.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import {
- Configuration,
- DebugApi,
- UnittestApi,
- ChatApi,
-} from "../../../src/client";
-import { useSelector } from "react-redux";
-import { useEffect, useState } from "react";
-import { RootStore } from "../redux/store";
-
-export function useApi() {
- const apiUrl = useSelector((state: RootStore) => state.config.apiUrl);
- const vscMachineId = useSelector(
- (state: RootStore) => state.config.vscMachineId
- );
- const [debugApi, setDebugApi] = useState<DebugApi>();
- const [unittestApi, setUnittestApi] = useState<UnittestApi>();
- const [chatApi, setChatApi] = useState<ChatApi>();
-
- useEffect(() => {
- if (apiUrl && vscMachineId) {
- let config = new Configuration({
- basePath: apiUrl,
- fetchApi: fetch,
- middleware: [
- {
- pre: async (context) => {
- context.init.headers = {
- ...context.init.headers,
- "x-vsc-machine-id": vscMachineId,
- };
- },
- },
- ],
- });
- setDebugApi(new DebugApi(config));
- setUnittestApi(new UnittestApi(config));
- setChatApi(new ChatApi(config));
- }
- }, [apiUrl, vscMachineId]);
-
- return { debugApi, unittestApi, chatApi };
-}
diff --git a/extension/react-app/src/util/editCache.ts b/extension/react-app/src/util/editCache.ts
deleted file mode 100644
index b8071127..00000000
--- a/extension/react-app/src/util/editCache.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { useApi } from "../util/api";
-import { FileEdit, SerializedDebugContext } from "../../../src/client";
-import { useCallback, useEffect, useState } from "react";
-
-export function useEditCache() {
- const { debugApi } = useApi();
-
- const fetchNewEdit = useCallback(
- async (debugContext: SerializedDebugContext) => {
- return (
- await debugApi?.editEndpointDebugEditPost({
- serializedDebugContext: debugContext,
- })
- )?.completion;
- },
- [debugApi]
- );
-
- const [editCache, setEditCache] = useState(new EditCache(fetchNewEdit));
-
- useEffect(() => {
- setEditCache(new EditCache(fetchNewEdit));
- }, [fetchNewEdit]);
-
- return editCache;
-}
-
-/**
- * Stores preloaded edits, invalidating based off of debug context changes
- */
-class EditCache {
- private _lastDebugContext: SerializedDebugContext | undefined;
- private _cachedEdits: FileEdit[] | undefined;
- private _fetchNewEdit: (
- debugContext: SerializedDebugContext
- ) => Promise<FileEdit[] | undefined>;
- private _debounceTimer: NodeJS.Timeout | undefined;
-
- private _debugContextChanged(debugContext: SerializedDebugContext): boolean {
- if (!this._lastDebugContext) {
- return true;
- }
-
- return (
- JSON.stringify(this._lastDebugContext) !== JSON.stringify(debugContext)
- );
- }
-
- private _debugContextComplete(debugContext: SerializedDebugContext): boolean {
- return debugContext.rangesInFiles.length > 0;
- }
-
- public async preloadEdit(debugContext: SerializedDebugContext) {
- if (this._debounceTimer) {
- clearTimeout(this._debounceTimer);
- }
- if (
- this._debugContextComplete(debugContext) &&
- this._debugContextChanged(debugContext)
- ) {
- this._debounceTimer = setTimeout(async () => {
- console.log("Preloading edits");
- this._cachedEdits = await this._fetchNewEdit(debugContext);
- this._lastDebugContext = debugContext;
- }, 200);
- }
- }
-
- public async getEdit(
- debugContext: SerializedDebugContext
- ): Promise<FileEdit[]> {
- if (this._debugContextChanged(debugContext)) {
- console.log("Cache miss");
- this._cachedEdits = await this._fetchNewEdit(debugContext);
- } else {
- console.log("Cache hit");
- }
-
- return this._cachedEdits!;
- }
-
- constructor(
- fetchNewEdit: (
- debugContext: SerializedDebugContext
- ) => Promise<FileEdit[] | undefined>
- ) {
- this._fetchNewEdit = fetchNewEdit;
- }
-}
diff --git a/extension/react-app/src/util/index.ts b/extension/react-app/src/util/index.ts
index 458f9d95..c4168e13 100644
--- a/extension/react-app/src/util/index.ts
+++ b/extension/react-app/src/util/index.ts
@@ -1,27 +1,43 @@
-import { RangeInFile } from "../../../src/client";
+type Platform = "mac" | "linux" | "windows" | "unknown";
-export function readRangeInVirtualFileSystem(
- rangeInFile: RangeInFile,
- filesystem: { [filepath: string]: string }
-): string | undefined {
- const range = rangeInFile.range;
-
- let data = filesystem[rangeInFile.filepath];
- if (data === undefined) {
- console.log("File not found");
- return undefined;
+export function getPlatform(): Platform {
+ const platform = window.navigator.platform.toUpperCase();
+ if (platform.indexOf("MAC") >= 0) {
+ return "mac";
+ } else if (platform.indexOf("LINUX") >= 0) {
+ return "linux";
+ } else if (platform.indexOf("WIN") >= 0) {
+ return "windows";
} else {
- let lines = data.toString().split("\n");
- if (range.start.line === range.end.line) {
- return lines[rangeInFile.range.start.line].slice(
- rangeInFile.range.start.character,
- rangeInFile.range.end.character
- );
- } else {
- let firstLine = lines[range.start.line].slice(range.start.character);
- let lastLine = lines[range.end.line].slice(0, range.end.character);
- let middleLines = lines.slice(range.start.line + 1, range.end.line);
- return [firstLine, ...middleLines, lastLine].join("\n");
- }
+ return "unknown";
+ }
+}
+
+export function isMetaEquivalentKeyPressed(event: {
+ metaKey: boolean;
+ ctrlKey: boolean;
+}): boolean {
+ const platform = getPlatform();
+ switch (platform) {
+ case "mac":
+ return event.metaKey;
+ case "linux":
+ case "windows":
+ return event.ctrlKey;
+ default:
+ return event.metaKey;
+ }
+}
+
+export function getMetaKeyLabel(): string {
+ const platform = getPlatform();
+ switch (platform) {
+ case "mac":
+ return "⌘";
+ case "linux":
+ case "windows":
+ return "^";
+ default:
+ return "⌘";
}
}
diff --git a/extension/scripts/.gitignore b/extension/scripts/.gitignore
deleted file mode 100644
index fbb3bf9f..00000000
--- a/extension/scripts/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-testdb
-env
-stdout.txt
-.continue_env_installed
-**.whl \ No newline at end of file
diff --git a/extension/scripts/README.md b/extension/scripts/README.md
deleted file mode 100644
index da1ad493..00000000
--- a/extension/scripts/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Scripts
-
-Whenever we need python to run on the client side, we include a script file at the top level of this folder. All other files that are not to be run directly as a script (utility files) should be in a subfolder of `scripts`. You can call one of these scripts from the VS Code extension using the `runPythonScript` function in `bridge.ts`.
-
-When the extension is activated (`activate` function in `src/extension.ts`), we call `setupPythonEnv`, which makes the virtual environment and downloads all the necessary requirements as given in `requirements.txt`. With this in mind, be sure to run `pip freeze > requirements.txt` whenever you add a new requirement.
diff --git a/extension/scripts/chroma.py b/extension/scripts/chroma.py
deleted file mode 100644
index 7425394e..00000000
--- a/extension/scripts/chroma.py
+++ /dev/null
@@ -1,152 +0,0 @@
-import chromadb
-import os
-import json
-import subprocess
-
-from typing import List, Tuple
-
-from chromadb.config import Settings
-
-client = chromadb.Client(Settings(
- chroma_db_impl="duckdb+parquet",
- persist_directory="./data/"
-))
-
-FILE_TYPES_TO_IGNORE = [
- '.pyc',
- '.png',
- '.jpg',
- '.jpeg',
- '.gif',
- '.svg',
- '.ico'
-]
-
-def further_filter(files: List[str], root_dir: str):
- """Further filter files before indexing."""
- for file in files:
- if file.endswith(tuple(FILE_TYPES_TO_IGNORE)) or file.startswith('.git') or file.startswith('archive'):
- continue
- yield root_dir + "/" + file
-
-def get_git_root_dir(path: str):
- """Get the root directory of a Git repository."""
- try:
- return subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], cwd=path).strip().decode()
- except subprocess.CalledProcessError:
- return None
-
-def get_git_ignored_files(root_dir: str):
- """Get the list of ignored files in a Git repository."""
- try:
- output = subprocess.check_output(['git', 'ls-files', '--ignored', '--others', '--exclude-standard'], cwd=root_dir).strip().decode()
- return output.split('\n')
- except subprocess.CalledProcessError:
- return []
-
-def get_all_files(root_dir: str):
- """Get a list of all files in a directory."""
- for dir_path, _, file_names in os.walk(root_dir):
- for file_name in file_names:
- yield os.path.join(os.path.relpath(dir_path, root_dir), file_name)
-
-def get_input_files(root_dir: str):
- """Get a list of all files in a Git repository that are not ignored."""
- ignored_files = set(get_git_ignored_files(root_dir))
- all_files = set(get_all_files(root_dir))
- nonignored_files = all_files - ignored_files
- return further_filter(nonignored_files, root_dir)
-
-def get_git_root_dir(cwd: str):
- """Get the root directory of a Git repository."""
- result = subprocess.run(['git', 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
- return result.stdout.decode().strip()
-
-def get_current_branch(cwd: str) -> str:
- """Get the current Git branch."""
- try:
- return subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=cwd).decode("utf-8").strip()
- except:
- return "main"
-
-def get_current_commit(cwd: str) -> str:
- try:
- return subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=cwd).decode("utf-8").strip()
- except:
- return "NO_COMMITS"
-
-def get_modified_deleted_files(cwd: str) -> Tuple[List[str], List[str]]:
- """Get a list of all files that have been modified since the last commit."""
- branch = get_current_branch(cwd)
- current_commit = get_current_commit(cwd)
-
- with open(f"./data/{branch}.json", 'r') as f:
- previous_commit = json.load(f)["commit"]
-
- modified_deleted_files = subprocess.check_output(["git", "diff", "--name-only", previous_commit, current_commit], cwd=cwd).decode("utf-8").strip()
- modified_deleted_files = modified_deleted_files.split("\n")
- modified_deleted_files = [f for f in modified_deleted_files if f]
-
- root = get_git_root_dir(cwd)
- deleted_files = [f for f in modified_deleted_files if not os.path.exists(root + "/" + f)]
- modified_files = [f for f in modified_deleted_files if os.path.exists(root + "/" + f)]
-
- return further_filter(modified_files, root), further_filter(deleted_files, root)
-
-def create_collection(branch: str, cwd: str):
- """Create a new collection, returning whether it already existed."""
- try:
- collection = client.create_collection(name=branch)
- except Exception as e:
- print(e)
- return
-
- files = get_input_files(get_git_root_dir(cwd))
- for file in files:
- with open(file, 'r') as f:
- collection.add(documents=[f.read()], ids=[file])
- print(f"Added {file}")
- with open(f"./data/{branch}.json", 'w') as f:
- json.dump({"commit": get_current_commit(cwd)}, f)
-
-def collection_exists(cwd: str):
- """Check if a collection exists."""
- branch = get_current_branch(cwd)
- return branch in client.list_collections()
-
-def update_collection(cwd: str):
- """Update the collection."""
- branch = get_current_branch(cwd)
-
- try:
-
- collection = client.get_collection(branch)
-
- modified_files, deleted_files = get_modified_deleted_files(cwd)
-
- for file in deleted_files:
- collection.delete(ids=[file])
- print(f"Deleted {file}")
-
- for file in modified_files:
- with open(file, 'r') as f:
- collection.update(documents=[f.read()], ids=[file])
- print(f"Updated {file}")
-
- with open(f"./data/{branch}.json", 'w') as f:
- json.dump({"commit": get_current_commit(cwd)}, f)
-
- except:
-
- create_collection(branch, cwd)
-
-def query_collection(query: str, n_results: int, cwd: str):
- """Query the collection."""
- branch = get_current_branch(cwd)
- try:
- collection = client.get_collection(branch)
- except:
- create_collection(branch, cwd)
- collection = client.get_collection(branch)
- results = collection.query(query_texts=[query], n_results=n_results)
- return results \ No newline at end of file
diff --git a/extension/scripts/index.py b/extension/scripts/index.py
deleted file mode 100644
index 3afc9131..00000000
--- a/extension/scripts/index.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import sys
-import os
-from typing import TextIO
-from chroma import update_collection, query_collection, create_collection, collection_exists, get_current_branch
-from typer import Typer
-
-app = Typer()
-
-class SilenceStdoutContextManager:
- saved_stdout: TextIO
-
- def __enter__(self):
- self._original_stdout = sys.stdout
- sys.stdout = open(os.devnull, 'w')
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- sys.stdout.close()
- sys.stdout = self._original_stdout
-
-silence = SilenceStdoutContextManager()
-
-@app.command("exists")
-def exists(cwd: str):
- with silence:
- exists = collection_exists(cwd)
- print({"exists": exists})
-
-@app.command("create")
-def create(cwd: str):
- with silence:
- branch = get_current_branch(cwd)
- create_collection(branch, cwd)
- print({"success": True})
-
-@app.command("update")
-def update(cwd: str):
- with silence:
- update_collection(cwd)
- print({"success": True})
-
-@app.command("query")
-def query(query: str, n_results: int, cwd: str):
- with silence:
- resp = query_collection(query, n_results, cwd)
- results = [{
- "id": resp["ids"][0][i],
- "document": resp["documents"][0][i]
- } for i in range(len(resp["ids"][0]))]
- print({"results": results})
-
-if __name__ == "__main__":
- app() \ No newline at end of file
diff --git a/extension/scripts/query.py b/extension/scripts/query.py
deleted file mode 100644
index f2e44413..00000000
--- a/extension/scripts/query.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import subprocess
-import sys
-from gpt_index import GPTSimpleVectorIndex, GPTFaissIndex
-import os
-from typer import Typer
-from enum import Enum
-from update import update_codebase_index, create_codebase_index, index_dir_for, get_current_branch
-from replace import replace_additional_index
-
-app = Typer()
-
-def query_codebase_index(query: str) -> str:
- """Query the codebase index."""
- branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).decode("utf-8").strip()
- path = 'data/{branch}/index.json'
- if not os.path.exists(path):
- print("No index found for the codebase")
- return ""
- index = GPTFaissIndex.load_from_disk(path)
- return index.query(query)
-
-def query_additional_index(query: str) -> str:
- """Query the additional index."""
- index = GPTSimpleVectorIndex.load_from_disk('data/additional_index.json')
- return index.query(query)
-
-class IndexTypeOption(str, Enum):
- codebase = "codebase"
- additional = "additional"
-
-@app.command()
-def query(context: IndexTypeOption, query: str):
- if context == IndexTypeOption.additional:
- response = query_additional_index(query)
- elif context == IndexTypeOption.codebase:
- response = query_codebase_index(query)
- else:
- print("Error: unknown context")
- print({ "response": response })
-
-@app.command()
-def check_index_exists(root_path: str):
- branch = get_current_branch()
- exists = os.path.exists(index_dir_for(branch))
- print({ "exists": exists })
-
-@app.command()
-def update():
- update_codebase_index()
- print("Updated codebase index")
-
-@app.command()
-def create_index(path: str):
- create_codebase_index()
- print("Created file index")
-
-@app.command()
-def replace_additional_index(info: str):
- replace_additional_index()
- print("Replaced additional index")
-
-if __name__ == '__main__':
- app() \ No newline at end of file
diff --git a/extension/scripts/replace.py b/extension/scripts/replace.py
deleted file mode 100644
index 08810243..00000000
--- a/extension/scripts/replace.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import sys
-from gpt_index import GPTSimpleVectorIndex, Document
-
-def replace_additional_index(info: str):
- """Replace the additional index."""
- with open('data/additional_context.txt', 'w') as f:
- f.write(info)
- documents = [Document(info)]
- index = GPTSimpleVectorIndex(documents)
- index.save_to_disk('data/additional_index.json')
- print("Additional index replaced")
-
-if __name__ == "__main__":
- """python3 replace.py <info>"""
- info = sys.argv[1] if len(sys.argv) > 1 else None
- if info:
- replace_additional_index(info) \ No newline at end of file
diff --git a/extension/scripts/requirements.txt b/extension/scripts/requirements.txt
deleted file mode 100644
index c51c9d73..00000000
--- a/extension/scripts/requirements.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-# chromadb==0.3.10
-# pathspec==0.11.0
-# typer==0.7.0
-# pydantic
-# pytest
-./continuedev-0.1.2-py3-none-any.whl \ No newline at end of file
diff --git a/extension/scripts/update.py b/extension/scripts/update.py
deleted file mode 100644
index 15ad6ac0..00000000
--- a/extension/scripts/update.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# import faiss
-import json
-import os
-import subprocess
-
-from gpt_index.langchain_helpers.text_splitter import TokenTextSplitter
-from gpt_index import GPTSimpleVectorIndex, SimpleDirectoryReader, Document, GPTFaissIndex
-from typing import List, Generator, Tuple
-
-FILE_TYPES_TO_IGNORE = [
- '.pyc',
- '.png',
- '.jpg',
- '.jpeg',
- '.gif',
- '.svg',
- '.ico'
-]
-
-def further_filter(files: List[str], root_dir: str):
- """Further filter files before indexing."""
- for file in files:
- if file.endswith(tuple(FILE_TYPES_TO_IGNORE)) or file.startswith('.git') or file.startswith('archive'):
- continue
- yield root_dir + "/" + file
-
-def get_git_root_dir(path: str):
- """Get the root directory of a Git repository."""
- try:
- return subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], cwd=path).strip().decode()
- except subprocess.CalledProcessError:
- return None
-
-def get_git_ignored_files(root_dir: str):
- """Get the list of ignored files in a Git repository."""
- try:
- output = subprocess.check_output(['git', 'ls-files', '--ignored', '--others', '--exclude-standard'], cwd=root_dir).strip().decode()
- return output.split('\n')
- except subprocess.CalledProcessError:
- return []
-
-def get_all_files(root_dir: str):
- """Get a list of all files in a directory."""
- for dir_path, _, file_names in os.walk(root_dir):
- for file_name in file_names:
- yield os.path.join(os.path.relpath(dir_path, root_dir), file_name)
-
-def get_input_files(root_dir: str):
- """Get a list of all files in a Git repository that are not ignored."""
- ignored_files = set(get_git_ignored_files(root_dir))
- all_files = set(get_all_files(root_dir))
- nonignored_files = all_files - ignored_files
- return further_filter(nonignored_files, root_dir)
-
-def load_gpt_index_documents(root: str) -> List[Document]:
- """Loads a list of GPTIndex Documents, respecting .gitignore files."""
- # Get input files
- input_files = get_input_files(root)
- # Use SimpleDirectoryReader to load the files into Documents
- return SimpleDirectoryReader(root, input_files=input_files, file_metadata=lambda filename: {"filename": filename}).load_data()
-
-def index_dir_for(branch: str) -> str:
- return f"data/{branch}"
-
-def get_git_root_dir():
- result = subprocess.run(['git', 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- return result.stdout.decode().strip()
-
-def get_current_branch() -> str:
- return subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).decode("utf-8").strip()
-
-def get_current_commit() -> str:
- return subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("utf-8").strip()
-
-def create_codebase_index():
- """Create a new index for the current branch."""
- branch = get_current_branch()
- if not os.path.exists(index_dir_for(branch)):
- os.makedirs(index_dir_for(branch))
-
- documents = load_gpt_index_documents(get_git_root_dir())
-
- chunks = {}
- doc_chunks = []
- for doc in documents:
- text_splitter = TokenTextSplitter()
- text_chunks = text_splitter.split_text(doc.text)
- filename = doc.extra_info["filename"]
- chunks[filename] = len(text_chunks)
- for i, text in enumerate(text_chunks):
- doc_chunks.append(Document(text, doc_id=f"{filename}::{i}"))
-
- with open(f"{index_dir_for(branch)}/metadata.json", "w") as f:
- json.dump({"commit": get_current_commit(), "chunks" : chunks}, f, indent=4)
-
- index = GPTSimpleVectorIndex([])
- for chunk in doc_chunks:
- index.insert(chunk)
-
- # d = 1536 # Dimension of text-ada-embedding-002
- # faiss_index = faiss.IndexFlatL2(d)
- # index = GPTFaissIndex(documents, faiss_index=faiss_index)
- # index.save_to_disk(f"{index_dir_for(branch)}/index.json", faiss_index_save_path=f"{index_dir_for(branch)}/index_faiss_core.index")
-
- index.save_to_disk(f"{index_dir_for(branch)}/index.json")
-
- print("Codebase index created")
-
-def get_modified_deleted_files() -> Tuple[List[str], List[str]]:
- """Get a list of all files that have been modified since the last commit."""
- branch = get_current_branch()
- current_commit = get_current_commit()
-
- metadata = f"{index_dir_for(branch)}/metadata.json"
- with open(metadata, "r") as f:
- previous_commit = json.load(f)["commit"]
-
- modified_deleted_files = subprocess.check_output(["git", "diff", "--name-only", previous_commit, current_commit]).decode("utf-8").strip()
- modified_deleted_files = modified_deleted_files.split("\n")
- modified_deleted_files = [f for f in modified_deleted_files if f]
-
- root = get_git_root_dir()
- deleted_files = [f for f in modified_deleted_files if not os.path.exists(root + "/" + f)]
- modified_files = [f for f in modified_deleted_files if os.path.exists(root + "/" + f)]
-
- return further_filter(modified_files, index_dir_for(branch)), further_filter(deleted_files, index_dir_for(branch))
-
-def update_codebase_index():
- """Update the index with a list of files."""
- branch = get_current_branch()
-
- if not os.path.exists(index_dir_for(branch)):
- create_codebase_index()
- else:
- # index = GPTFaissIndex.load_from_disk(f"{index_dir_for(branch)}/index.json", faiss_index_save_path=f"{index_dir_for(branch)}/index_faiss_core.index")
- index = GPTSimpleVectorIndex.load_from_disk(f"{index_dir_for(branch)}/index.json")
- modified_files, deleted_files = get_modified_deleted_files()
-
- with open(f"{index_dir_for(branch)}/metadata.json", "r") as f:
- metadata = json.load(f)
-
- for file in deleted_files:
-
- num_chunks = metadata["chunks"][file]
- for i in range(num_chunks):
- index.delete(f"{file}::{i}")
-
- del metadata["chunks"][file]
-
- print(f"Deleted {file}")
-
- for file in modified_files:
-
- if file in metadata["chunks"]:
-
- num_chunks = metadata["chunks"][file]
-
- for i in range(num_chunks):
- index.delete(f"{file}::{i}")
-
- print(f"Deleted old version of {file}")
-
- with open(file, "r") as f:
- text = f.read()
-
- text_splitter = TokenTextSplitter()
- text_chunks = text_splitter.split_text(text)
-
- for i, text in enumerate(text_chunks):
- index.insert(Document(text, doc_id=f"{file}::{i}"))
-
- metadata["chunks"][file] = len(text_chunks)
-
- print(f"Inserted new version of {file}")
-
- metadata["commit"] = get_current_commit()
-
- with open(f"{index_dir_for(branch)}/metadata.json", "w") as f:
- json.dump(metadata, f, indent=4)
-
- print("Codebase index updated")
-
-if __name__ == "__main__":
- """python3 update.py"""
- update_codebase_index() \ No newline at end of file
diff --git a/extension/server/.gitignore b/extension/server/.gitignore
new file mode 100644
index 00000000..0b6e11dd
--- /dev/null
+++ b/extension/server/.gitignore
@@ -0,0 +1 @@
+**.whl \ No newline at end of file
diff --git a/extension/server/README.md b/extension/server/README.md
new file mode 100644
index 00000000..56208b5a
--- /dev/null
+++ b/extension/server/README.md
@@ -0,0 +1,3 @@
+# server
+
+These are the files that get copied over to `~/.continue/server` in order to create the Python environment and start the Continue server.
diff --git a/extension/server/requirements.txt b/extension/server/requirements.txt
new file mode 100644
index 00000000..eb1f3738
--- /dev/null
+++ b/extension/server/requirements.txt
@@ -0,0 +1 @@
+./continuedev-0.1.2-py3-none-any.whl \ No newline at end of file
diff --git a/extension/scripts/run_continue_server.py b/extension/server/run_continue_server.py
index 089cc54d..089cc54d 100644
--- a/extension/scripts/run_continue_server.py
+++ b/extension/server/run_continue_server.py
diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts
index 18650561..5c6ffa02 100644
--- a/extension/src/activation/activate.ts
+++ b/extension/src/activation/activate.ts
@@ -2,24 +2,41 @@ import * as vscode from "vscode";
import { registerAllCommands } from "../commands";
import { registerAllCodeLensProviders } from "../lang-server/codeLens";
import { sendTelemetryEvent, TelemetryEvent } from "../telemetry";
-// import { openCapturedTerminal } from "../terminal/terminalEmulator";
import IdeProtocolClient from "../continueIdeClient";
import { getContinueServerUrl } from "../bridge";
-import { CapturedTerminal } from "../terminal/terminalEmulator";
-import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel";
-import { startContinuePythonServer } from "./environmentSetup";
+import { ContinueGUIWebviewViewProvider } from "../debugPanel";
+import {
+ getExtensionVersion,
+ startContinuePythonServer,
+} from "./environmentSetup";
+import fetch from "node-fetch";
+import registerQuickFixProvider from "../lang-server/codeActions";
// import { CapturedTerminal } from "../terminal/terminalEmulator";
+const PACKAGE_JSON_RAW_GITHUB_URL =
+ "https://raw.githubusercontent.com/continuedev/continue/HEAD/extension/package.json";
+
export let extensionContext: vscode.ExtensionContext | undefined = undefined;
export let ideProtocolClient: IdeProtocolClient;
-export async function activateExtension(
- context: vscode.ExtensionContext,
- showTutorial: boolean
-) {
+export async function activateExtension(context: vscode.ExtensionContext) {
extensionContext = context;
+ // Before anything else, check whether this is an out-of-date version of the extension
+ // Do so by grabbing the package.json off of the GitHub respository for now.
+ fetch(PACKAGE_JSON_RAW_GITHUB_URL)
+ .then(async (res) => res.json())
+ .then((packageJson) => {
+ if (packageJson.version !== getExtensionVersion()) {
+ vscode.window.showInformationMessage(
+ `You are using an out-of-date version of the Continue extension. Please update to the latest version.`
+ );
+ }
+ })
+ .catch((e) => console.log("Error checking for extension updates: ", e));
+
+ // Start the Python server
await new Promise((resolve, reject) => {
vscode.window.withProgress(
{
@@ -35,19 +52,20 @@ export async function activateExtension(
);
});
+ // Register commands and providers
sendTelemetryEvent(TelemetryEvent.ExtensionActivated);
registerAllCodeLensProviders(context);
registerAllCommands(context);
+ registerQuickFixProvider();
+ // Initialize IDE Protocol Client
const serverUrl = getContinueServerUrl();
-
ideProtocolClient = new IdeProtocolClient(
`${serverUrl.replace("http", "ws")}/ide/ws`,
context
);
- // Setup the left panel
- (async () => {
+ {
const sessionIdPromise = await ideProtocolClient.getSessionId();
const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise);
@@ -60,10 +78,5 @@ export async function activateExtension(
}
)
);
- })();
- // All opened terminals should be replaced by our own terminal
- // vscode.window.onDidOpenTerminal((terminal) => {});
-
- // If any terminals are open to start, replace them
- // vscode.window.terminals.forEach((terminal) => {}
+ }
}
diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts
index 714080e3..69a3b75a 100644
--- a/extension/src/activation/environmentSetup.ts
+++ b/extension/src/activation/environmentSetup.ts
@@ -7,23 +7,56 @@ import * as fs from "fs";
import { getContinueServerUrl } from "../bridge";
import fetch from "node-fetch";
import * as vscode from "vscode";
+import * as os from "os";
import fkill from "fkill";
import { sendTelemetryEvent, TelemetryEvent } from "../telemetry";
+const WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR =
+ "A Python virtual enviroment cannot be activated because running scripts is disabled for this user. In order to use Continue, please enable signed scripts to run with this command in PowerShell: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`, reload VS Code, and then try again.";
+
const MAX_RETRIES = 3;
async function retryThenFail(
fn: () => Promise<any>,
retries: number = MAX_RETRIES
): Promise<any> {
try {
+ if (retries < MAX_RETRIES && process.platform === "win32") {
+ let [stdout, stderr] = await runCommand("Get-ExecutionPolicy");
+ if (!stdout.includes("RemoteSigned")) {
+ [stdout, stderr] = await runCommand(
+ "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser"
+ );
+ console.log("Execution policy stdout: ", stdout);
+ console.log("Execution policy stderr: ", stderr);
+ }
+ }
+
return await fn();
} catch (e: any) {
if (retries > 0) {
return await retryThenFail(fn, retries - 1);
}
- vscode.window.showErrorMessage(
- "Failed to set up Continue extension. Please email nate@continue.dev and we'll get this fixed ASAP!"
- );
+
+ // Show corresponding error message depending on the platform
+ let msg =
+ "Failed to set up Continue extension. Please email nate@continue.dev and we'll get this fixed ASAP!";
+ try {
+ switch (process.platform) {
+ case "win32":
+ msg = WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR;
+ break;
+ case "darwin":
+ break;
+ case "linux":
+ const [pythonCmd] = await getPythonPipCommands();
+ msg = await getLinuxAptInstallError(pythonCmd);
+ break;
+ }
+ } finally {
+ console.log("After retries, failed to set up Continue extension", msg);
+ vscode.window.showErrorMessage(msg);
+ }
+
sendTelemetryEvent(TelemetryEvent.ExtensionSetupError, {
error: e.message,
});
@@ -127,8 +160,7 @@ function getActivateUpgradeCommands(pythonCmd: string, pipCmd: string) {
function checkEnvExists() {
const envBinPath = path.join(
- getExtensionUri().fsPath,
- "scripts",
+ serverPath(),
"env",
process.platform == "win32" ? "Scripts" : "bin"
);
@@ -140,10 +172,29 @@ function checkEnvExists() {
);
}
-function checkRequirementsInstalled() {
+async function checkRequirementsInstalled() {
+ // First, check if the requirements have been installed most recently for a later version of the extension
+ if (fs.existsSync(requirementsVersionPath())) {
+ const requirementsVersion = fs.readFileSync(
+ requirementsVersionPath(),
+ "utf8"
+ );
+ if (requirementsVersion !== getExtensionVersion()) {
+ // Remove the old version of continuedev from site-packages
+ const [pythonCmd, pipCmd] = await getPythonPipCommands();
+ const [activateCmd] = getActivateUpgradeCommands(pythonCmd, pipCmd);
+ const removeOldVersionCommand = [
+ `cd "${serverPath()}"`,
+ activateCmd,
+ `${pipCmd} uninstall -y continuedev`,
+ ].join(" ; ");
+ await runCommand(removeOldVersionCommand);
+ return false;
+ }
+ }
+
let envLibsPath = path.join(
- getExtensionUri().fsPath,
- "scripts",
+ serverPath(),
"env",
process.platform == "win32" ? "Lib" : "lib"
);
@@ -165,27 +216,30 @@ function checkRequirementsInstalled() {
const continuePath = path.join(envLibsPath, "continuedev");
return fs.existsSync(continuePath);
-
- // return fs.existsSync(
- // path.join(getExtensionUri().fsPath, "scripts", ".continue_env_installed")
- // );
}
-async function setupPythonEnv() {
- console.log("Setting up python env for Continue extension...");
-
- const [pythonCmd, pipCmd] = await getPythonPipCommands();
- const [activateCmd, pipUpgradeCmd] = getActivateUpgradeCommands(
- pythonCmd,
- pipCmd
- );
+async function getLinuxAptInstallError(pythonCmd: string) {
+ // First, try to run the command to install python3-venv
+ let [stdout, stderr] = await runCommand(`${pythonCmd} --version`);
+ if (stderr) {
+ await vscode.window.showErrorMessage(
+ "Python3 is not installed. Please install from https://www.python.org/downloads, reload VS Code, and try again."
+ );
+ throw new Error(stderr);
+ }
+ const version = stdout.split(" ")[1].split(".")[1];
+ const installVenvCommand = `apt-get install python3.${version}-venv`;
+ await runCommand("apt-get update");
+ return `[Important] Continue needs to create a Python virtual environment, but python3.${version}-venv is not installed. Please run this command in your terminal: \`${installVenvCommand}\`, reload VS Code, and then try again.`;
+}
+async function createPythonVenv(pythonCmd: string) {
if (checkEnvExists()) {
console.log("Python env already exists, skipping...");
} else {
// Assemble the command to create the env
const createEnvCommand = [
- `cd "${path.join(getExtensionUri().fsPath, "scripts")}"`,
+ `cd "${serverPath()}"`,
`${pythonCmd} -m venv env`,
].join(" ; ");
@@ -194,31 +248,37 @@ async function setupPythonEnv() {
stderr &&
stderr.includes("running scripts is disabled on this system")
) {
- await vscode.window.showErrorMessage(
- "A Python virtual enviroment cannot be activated because running scripts is disabled for this user. Please enable signed scripts to run with this command in PowerShell: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`, reload VS Code, and then try again."
- );
+ console.log("Scripts disabled error when trying to create env");
+ await vscode.window.showErrorMessage(WINDOWS_REMOTE_SIGNED_SCRIPTS_ERROR);
throw new Error(stderr);
} else if (
stderr?.includes("On Debian/Ubuntu systems") ||
stdout?.includes("On Debian/Ubuntu systems")
) {
- // First, try to run the command to install python3-venv
- let [stdout, stderr] = await runCommand(`${pythonCmd} --version`);
- if (stderr) {
- throw new Error(stderr);
- }
- const version = stdout.split(" ")[1].split(".")[1];
- const installVenvCommand = `apt-get install python3.${version}-venv`;
- await runCommand("apt-get update");
- // Ask the user to run the command to install python3-venv (requires sudo, so we can't)
- // First, get the python version
- const msg = `[Important] Continue needs to create a Python virtual environment, but python3.${version}-venv is not installed. Please run this command in your terminal: \`${installVenvCommand}\`, reload VS Code, and then try again.`;
+ const msg = await getLinuxAptInstallError(pythonCmd);
console.log(msg);
await vscode.window.showErrorMessage(msg);
} else if (checkEnvExists()) {
- console.log(
- "Successfully set up python env at ",
- getExtensionUri().fsPath + "/scripts/env"
+ console.log("Successfully set up python env at ", `${serverPath()}/env`);
+ } else if (
+ stderr?.includes("Permission denied") &&
+ stderr?.includes("python.exe")
+ ) {
+ // This might mean that another window is currently using the python.exe file to install requirements
+ // So we want to wait and try again
+ let i = 0;
+ await new Promise((resolve, reject) =>
+ setInterval(() => {
+ if (i > 5) {
+ reject("Timed out waiting for other window to create env...");
+ }
+ if (checkEnvExists()) {
+ resolve(null);
+ } else {
+ console.log("Waiting for other window to create env...");
+ }
+ i++;
+ }, 5000)
);
} else {
const msg = [
@@ -230,13 +290,27 @@ async function setupPythonEnv() {
throw new Error(msg);
}
}
+}
+
+async function setupPythonEnv() {
+ console.log("Setting up python env for Continue extension...");
+
+ const [pythonCmd, pipCmd] = await getPythonPipCommands();
+ const [activateCmd, pipUpgradeCmd] = getActivateUpgradeCommands(
+ pythonCmd,
+ pipCmd
+ );
await retryThenFail(async () => {
- if (checkRequirementsInstalled()) {
+ // First, create the virtual environment
+ await createPythonVenv(pythonCmd);
+
+ // Install the requirements
+ if (await checkRequirementsInstalled()) {
console.log("Python requirements already installed, skipping...");
} else {
const installRequirementsCommand = [
- `cd "${path.join(getExtensionUri().fsPath, "scripts")}"`,
+ `cd "${serverPath()}"`,
activateCmd,
pipUpgradeCmd,
`${pipCmd} install -r requirements.txt`,
@@ -245,6 +319,8 @@ async function setupPythonEnv() {
if (stderr) {
throw new Error(stderr);
}
+ // Write the version number for which requirements were installed
+ fs.writeFileSync(requirementsVersionPath(), getExtensionVersion());
}
});
}
@@ -297,12 +373,50 @@ async function checkServerRunning(serverUrl: string): Promise<boolean> {
}
}
+export function getContinueGlobalPath(): string {
+ // This is ~/.continue on mac/linux
+ const continuePath = path.join(os.homedir(), ".continue");
+ if (!fs.existsSync(continuePath)) {
+ fs.mkdirSync(continuePath);
+ }
+ return continuePath;
+}
+
+function setupServerPath() {
+ const sPath = serverPath();
+ const extensionServerPath = path.join(getExtensionUri().fsPath, "server");
+ const files = fs.readdirSync(extensionServerPath);
+ files.forEach((file) => {
+ const filePath = path.join(extensionServerPath, file);
+ fs.copyFileSync(filePath, path.join(sPath, file));
+ });
+}
+
+function serverPath(): string {
+ const sPath = path.join(getContinueGlobalPath(), "server");
+ if (!fs.existsSync(sPath)) {
+ fs.mkdirSync(sPath);
+ }
+ return sPath;
+}
+
+export function devDataPath(): string {
+ const sPath = path.join(getContinueGlobalPath(), "dev_data");
+ if (!fs.existsSync(sPath)) {
+ fs.mkdirSync(sPath);
+ }
+ return sPath;
+}
+
function serverVersionPath(): string {
- const extensionPath = getExtensionUri().fsPath;
- return path.join(extensionPath, "server_version.txt");
+ return path.join(serverPath(), "server_version.txt");
}
-function getExtensionVersion() {
+function requirementsVersionPath(): string {
+ return path.join(serverPath(), "requirements_version.txt");
+}
+
+export function getExtensionVersion() {
const extension = vscode.extensions.getExtension("continue.continue");
return extension?.packageJSON.version || "";
}
@@ -314,39 +428,40 @@ export async function startContinuePythonServer() {
return;
}
+ setupServerPath();
+
return await retryThenFail(async () => {
- if (await checkServerRunning(serverUrl)) {
- // Kill the server if it is running an old version
- if (fs.existsSync(serverVersionPath())) {
- const serverVersion = fs.readFileSync(serverVersionPath(), "utf8");
- if (serverVersion === getExtensionVersion()) {
- return;
- }
+ // Kill the server if it is running an old version
+ if (fs.existsSync(serverVersionPath())) {
+ const serverVersion = fs.readFileSync(serverVersionPath(), "utf8");
+ if (
+ serverVersion === getExtensionVersion() &&
+ (await checkServerRunning(serverUrl))
+ ) {
+ // The current version is already up and running, no need to continue
+ return;
}
- console.log("Killing old server...");
- try {
- await fkill(":65432");
- } catch (e) {
- console.log(
- "Failed to kill old server, likely because it didn't exist:",
- e
- );
+ }
+ console.log("Killing old server...");
+ try {
+ await fkill(":65432");
+ } catch (e: any) {
+ if (!e.message.includes("Process doesn't exist")) {
+ console.log("Failed to kill old server:", e);
}
}
// Do this after above check so we don't have to waste time setting up the env
await setupPythonEnv();
+ // Spawn the server process on port 65432
const [pythonCmd] = await getPythonPipCommands();
const activateCmd =
process.platform == "win32"
? ".\\env\\Scripts\\activate"
: ". env/bin/activate";
- const command = `cd "${path.join(
- getExtensionUri().fsPath,
- "scripts"
- )}" && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`;
+ const command = `cd "${serverPath()}" && ${activateCmd} && cd .. && ${pythonCmd} -m server.run_continue_server`;
console.log("Starting Continue python server...");
@@ -359,7 +474,8 @@ export async function startContinuePythonServer() {
console.log(`stdout: ${data}`);
if (
data.includes("Uvicorn running on") || // Successfully started the server
- data.includes("address already in use") // The server is already running (probably a simultaneously opened VS Code window)
+ data.includes("only one usage of each socket address") || // [windows] The server is already running (probably a simultaneously opened VS Code window)
+ data.includes("address already in use") // [mac/linux] The server is already running (probably a simultaneously opened VS Code window)
) {
console.log("Successfully started Continue python server");
resolve(null);
@@ -392,8 +508,8 @@ export async function startContinuePythonServer() {
}
export function isPythonEnvSetup(): boolean {
- let pathToEnvCfg = getExtensionUri().fsPath + "/scripts/env/pyvenv.cfg";
- return fs.existsSync(path.join(pathToEnvCfg));
+ const pathToEnvCfg = path.join(serverPath(), "env", "pyvenv.cfg");
+ return fs.existsSync(pathToEnvCfg);
}
export async function downloadPython3() {
diff --git a/extension/src/bridge.ts b/extension/src/bridge.ts
index 92ba4044..7e6398be 100644
--- a/extension/src/bridge.ts
+++ b/extension/src/bridge.ts
@@ -4,14 +4,11 @@ import * as vscode from "vscode";
import {
Configuration,
DebugApi,
- RangeInFile,
- SerializedDebugContext,
UnittestApi,
} from "./client";
import { convertSingleToDoubleQuoteJSON } from "./util/util";
import { getExtensionUri } from "./util/vscode";
import { extensionContext } from "./activation/activate";
-const axios = require("axios").default;
const util = require("util");
const exec = util.promisify(require("child_process").exec);
@@ -36,21 +33,16 @@ const configuration = new Configuration({
export const debugApi = new DebugApi(configuration);
export const unittestApi = new UnittestApi(configuration);
-function get_python_path() {
- return path.join(getExtensionUri().fsPath, "..");
-}
-
export function get_api_url() {
- let extensionUri = getExtensionUri();
- let configFile = path.join(extensionUri.fsPath, "config/config.json");
- let config = require(configFile);
+ const extensionUri = getExtensionUri();
+ const configFile = path.join(extensionUri.fsPath, "config/config.json");
+ const config = require(configFile);
if (config.API_URL) {
return config.API_URL;
}
return "http://localhost:65432";
}
-const API_URL = get_api_url();
export function getContinueServerUrl() {
// If in debug mode, always use 8001
@@ -66,10 +58,6 @@ export function getContinueServerUrl() {
);
}
-function build_python_command(cmd: string): string {
- return `cd ${get_python_path()} && source env/bin/activate && ${cmd}`;
-}
-
function listToCmdLineArgs(list: string[]): string {
return list.map((el) => `"$(echo "${el}")"`).join(" ");
}
@@ -103,198 +91,3 @@ export async function runPythonScript(
}
}
}
-
-function parseStdout(
- stdout: string,
- key: string,
- until_end: boolean = false
-): string {
- const prompt = `${key}=`;
- let lines = stdout.split("\n");
-
- let value: string = "";
- for (let i = 0; i < lines.length; i++) {
- if (lines[i].startsWith(prompt)) {
- if (until_end) {
- return lines.slice(i).join("\n").substring(prompt.length);
- } else {
- return lines[i].substring(prompt.length);
- }
- }
- }
- return "";
-}
-
-export async function askQuestion(
- question: string,
- workspacePath: string
-): Promise<{ answer: string; range: vscode.Range; filename: string }> {
- const command = build_python_command(
- `python3 ${path.join(
- get_python_path(),
- "ask.py"
- )} ask ${workspacePath} "${question}"`
- );
-
- const { stdout, stderr } = await exec(command);
- if (stderr) {
- throw new Error(stderr);
- }
- // Use the output
- const answer = parseStdout(stdout, "Answer");
- const filename = parseStdout(stdout, "Filename");
- const startLineno = parseInt(parseStdout(stdout, "Start lineno"));
- const endLineno = parseInt(parseStdout(stdout, "End lineno"));
- const range = new vscode.Range(
- new vscode.Position(startLineno, 0),
- new vscode.Position(endLineno, 0)
- );
- if (answer && filename && startLineno && endLineno) {
- return { answer, filename, range };
- } else {
- throw new Error("Error: No answer found");
- }
-}
-
-export async function apiRequest(
- endpoint: string,
- options: {
- method?: string;
- query?: { [key: string]: any };
- body?: { [key: string]: any };
- }
-): Promise<any> {
- let defaults = {
- method: "GET",
- query: {},
- body: {},
- };
- options = Object.assign(defaults, options); // Second takes over first
- if (endpoint.startsWith("/")) endpoint = endpoint.substring(1);
- console.log("API request: ", options.body);
-
- let resp;
- try {
- resp = await axios({
- method: options.method,
- url: `${API_URL}/${endpoint}`,
- data: options.body,
- params: options.query,
- headers: {
- "x-vsc-machine-id": vscode.env.machineId,
- },
- });
- } catch (err) {
- console.log("Error: ", err);
- throw err;
- }
-
- return resp.data;
-}
-
-// Write a docstring for the most specific function or class at the current line in the given file
-export async function writeDocstringForFunction(
- filename: string,
- position: vscode.Position
-): Promise<{ lineno: number; docstring: string }> {
- let resp = await apiRequest("docstring/forline", {
- query: {
- filecontents: (
- await vscode.workspace.fs.readFile(vscode.Uri.file(filename))
- ).toString(),
- lineno: position.line.toString(),
- },
- });
-
- const lineno = resp.lineno;
- const docstring = resp.completion;
- if (lineno && docstring) {
- return { lineno, docstring };
- } else {
- throw new Error("Error: No docstring returned");
- }
-}
-
-export async function findSuspiciousCode(
- ctx: SerializedDebugContext
-): Promise<RangeInFile[]> {
- if (!ctx.traceback) return [];
- let files = await getFileContents(
- getFilenamesFromPythonStacktrace(ctx.traceback)
- );
- let resp = await debugApi.findSusCodeEndpointDebugFindPost({
- findBody: {
- traceback: ctx.traceback,
- description: ctx.description,
- filesystem: files,
- },
- });
- let ranges = resp.response;
- if (
- ranges.length <= 1 &&
- ctx.traceback &&
- ctx.traceback.includes("AssertionError")
- ) {
- let parsed_traceback =
- await debugApi.parseTracebackEndpointDebugParseTracebackGet({
- traceback: ctx.traceback,
- });
- let last_frame = parsed_traceback.frames[0];
- if (!last_frame) return [];
- ranges = (
- await runPythonScript("build_call_graph.py", [
- last_frame.filepath,
- last_frame.lineno.toString(),
- last_frame._function,
- ])
- ).value;
- }
-
- return ranges;
-}
-
-export async function writeUnitTestForFunction(
- filename: string,
- position: vscode.Position
-): Promise<string> {
- let resp = await apiRequest("unittest/forline", {
- method: "POST",
- body: {
- filecontents: (
- await vscode.workspace.fs.readFile(vscode.Uri.file(filename))
- ).toString(),
- lineno: position.line,
- userid: vscode.env.machineId,
- },
- });
-
- return resp.completion;
-}
-
-async function getFileContents(
- files: string[]
-): Promise<{ [key: string]: string }> {
- let contents = await Promise.all(
- files.map(async (file: string) => {
- return (
- await vscode.workspace.fs.readFile(vscode.Uri.file(file))
- ).toString();
- })
- );
- let fileContents: { [key: string]: string } = {};
- for (let i = 0; i < files.length; i++) {
- fileContents[files[i]] = contents[i];
- }
- return fileContents;
-}
-
-function getFilenamesFromPythonStacktrace(traceback: string): string[] {
- let filenames: string[] = [];
- for (let line of traceback.split("\n")) {
- let match = line.match(/File "(.*)", line/);
- if (match) {
- filenames.push(match[1]);
- }
- }
- return filenames;
-}
diff --git a/extension/src/commands.ts b/extension/src/commands.ts
index ffb67ab5..2b7f4c0c 100644
--- a/extension/src/commands.ts
+++ b/extension/src/commands.ts
@@ -16,40 +16,16 @@ import {
import { acceptDiffCommand, rejectDiffCommand } from "./diffs";
import * as bridge from "./bridge";
import { debugPanelWebview } from "./debugPanel";
-import { sendTelemetryEvent, TelemetryEvent } from "./telemetry";
import { ideProtocolClient } from "./activation/activate";
-// Copy everything over from extension.ts
-const commandsMap: { [command: string]: (...args: any) => any } = {
- "continue.askQuestion": (data: any, webviewView: vscode.WebviewView) => {
- if (!vscode.workspace.workspaceFolders) {
- return;
- }
-
- answerQuestion(
- data.question,
- vscode.workspace.workspaceFolders[0].uri.fsPath,
- webviewView.webview
- );
- },
- "continue.askQuestionFromInput": () => {
- vscode.window
- .showInputBox({ placeHolder: "Ask away!" })
- .then((question) => {
- if (!question || !vscode.workspace.workspaceFolders) {
- return;
- }
+let focusedOnContinueInput = false;
- sendTelemetryEvent(TelemetryEvent.UniversalPromptQuery, {
- query: question,
- });
+export const setFocusedOnContinueInput = (value: boolean) => {
+ focusedOnContinueInput = value;
+};
- answerQuestion(
- question,
- vscode.workspace.workspaceFolders[0].uri.fsPath
- );
- });
- },
+// Copy everything over from extension.ts
+const commandsMap: { [command: string]: (...args: any) => any } = {
"continue.suggestionDown": suggestionDownCommand,
"continue.suggestionUp": suggestionUpCommand,
"continue.acceptSuggestion": acceptSuggestionCommand,
@@ -58,11 +34,23 @@ const commandsMap: { [command: string]: (...args: any) => any } = {
"continue.rejectDiff": rejectDiffCommand,
"continue.acceptAllSuggestions": acceptAllSuggestionsCommand,
"continue.rejectAllSuggestions": rejectAllSuggestionsCommand,
+ "continue.quickFix": async (message: string, code: string, edit: boolean) => {
+ ideProtocolClient.sendMainUserInput(
+ `${
+ edit ? "/edit " : ""
+ }${code}\n\nHow do I fix this problem in the above code?: ${message}`
+ );
+ },
"continue.focusContinueInput": async () => {
- vscode.commands.executeCommand("continue.continueGUIView.focus");
- debugPanelWebview?.postMessage({
- type: "focusContinueInput",
- });
+ if (focusedOnContinueInput) {
+ vscode.commands.executeCommand("workbench.action.focusActiveEditorGroup");
+ } else {
+ vscode.commands.executeCommand("continue.continueGUIView.focus");
+ debugPanelWebview?.postMessage({
+ type: "focusContinueInput",
+ });
+ }
+ focusedOnContinueInput = !focusedOnContinueInput;
},
"continue.quickTextEntry": async () => {
const text = await vscode.window.showInputBox({
@@ -73,27 +61,6 @@ const commandsMap: { [command: string]: (...args: any) => any } = {
if (text) {
ideProtocolClient.sendMainUserInput(text);
}
- vscode.commands.executeCommand("continue.continueGUIView.focus");
- },
-};
-
-const textEditorCommandsMap: { [command: string]: (...args: any) => {} } = {
- "continue.writeDocstring": async (editor: vscode.TextEditor, _) => {
- sendTelemetryEvent(TelemetryEvent.GenerateDocstring);
- let gutterSpinnerKey = showGutterSpinner(
- editor,
- editor.selection.active.line
- );
-
- const { lineno, docstring } = await bridge.writeDocstringForFunction(
- editor.document.fileName,
- editor.selection.active
- );
- // Can't use the edit given above after an async call
- editor.edit((edit) => {
- edit.insert(new vscode.Position(lineno, 0), docstring);
- decorationManager.deleteDecoration(gutterSpinnerKey);
- });
},
};
@@ -103,44 +70,4 @@ export function registerAllCommands(context: vscode.ExtensionContext) {
vscode.commands.registerCommand(command, callback)
);
}
-
- for (const [command, callback] of Object.entries(textEditorCommandsMap)) {
- context.subscriptions.push(
- vscode.commands.registerTextEditorCommand(command, callback)
- );
- }
-}
-
-async function answerQuestion(
- question: string,
- workspacePath: string,
- webview: vscode.Webview | undefined = undefined
-) {
- vscode.window.withProgress(
- {
- location: vscode.ProgressLocation.Notification,
- title: "Anwering question...",
- cancellable: false,
- },
- async (progress, token) => {
- try {
- let resp = await bridge.askQuestion(question, workspacePath);
- // Send the answer back to the webview
- if (webview) {
- webview.postMessage({
- type: "answerQuestion",
- answer: resp.answer,
- });
- }
- showAnswerInTextEditor(resp.filename, resp.range, resp.answer);
- } catch (error: any) {
- if (webview) {
- webview.postMessage({
- type: "answerQuestion",
- answer: error,
- });
- }
- }
- }
- );
}
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index 679d94ba..fac0a227 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -1,10 +1,9 @@
-// import { ShowSuggestionRequest } from "../schema/ShowSuggestionRequest";
import {
editorSuggestionsLocked,
showSuggestion as showSuggestionInEditor,
SuggestionRanges,
} from "./suggestions";
-import { openEditorAndRevealRange, getRightViewColumn } from "./util/vscode";
+import { openEditorAndRevealRange } from "./util/vscode";
import { FileEdit } from "../schema/FileEdit";
import { RangeInFile } from "../schema/RangeInFile";
import * as vscode from "vscode";
@@ -15,9 +14,8 @@ import {
import { FileEditWithFullContents } from "../schema/FileEditWithFullContents";
import fs = require("fs");
import { WebsocketMessenger } from "./util/messenger";
-import * as path from "path";
-import * as os from "os";
import { diffManager } from "./diffs";
+import path = require("path");
class IdeProtocolClient {
private messenger: WebsocketMessenger | null = null;
@@ -27,17 +25,54 @@ class IdeProtocolClient {
private _highlightDebounce: NodeJS.Timeout | null = null;
- constructor(serverUrl: string, context: vscode.ExtensionContext) {
- this.context = context;
+ private _lastReloadTime: number = 16;
+ private _reconnectionTimeouts: NodeJS.Timeout[] = [];
+
+ private _sessionId: string | null = null;
+ private _serverUrl: string;
- let messenger = new WebsocketMessenger(serverUrl);
+ private _newWebsocketMessenger() {
+ const requestUrl =
+ this._serverUrl +
+ (this._sessionId ? `?session_id=${this._sessionId}` : "");
+ const messenger = new WebsocketMessenger(requestUrl);
this.messenger = messenger;
- messenger.onClose(() => {
+
+ const reconnect = () => {
+ console.log("Trying to reconnect IDE protocol websocket...");
this.messenger = null;
+
+ // Exponential backoff to reconnect
+ this._reconnectionTimeouts.forEach((to) => clearTimeout(to));
+
+ const timeout = setTimeout(() => {
+ if (this.messenger?.websocket?.readyState === 1) {
+ return;
+ }
+ this._newWebsocketMessenger();
+ }, this._lastReloadTime);
+
+ this._reconnectionTimeouts.push(timeout);
+ this._lastReloadTime = Math.min(2 * this._lastReloadTime, 5000);
+ };
+ messenger.onOpen(() => {
+ this._reconnectionTimeouts.forEach((to) => clearTimeout(to));
+ });
+ messenger.onClose(() => {
+ reconnect();
+ });
+ messenger.onError(() => {
+ reconnect();
});
messenger.onMessage((messageType, data, messenger) => {
this.handleMessage(messageType, data, messenger);
});
+ }
+
+ constructor(serverUrl: string, context: vscode.ExtensionContext) {
+ this.context = context;
+ this._serverUrl = serverUrl;
+ this._newWebsocketMessenger();
// Setup listeners for any file changes in open editors
// vscode.workspace.onDidChangeTextDocument((event) => {
@@ -69,8 +104,11 @@ class IdeProtocolClient {
// }
// });
- // Setup listeners for any file changes in open editors
+ // Setup listeners for any selection changes in open editors
vscode.window.onDidChangeTextEditorSelection((event) => {
+ if (this.editorIsTerminal(event.textEditor)) {
+ return;
+ }
if (this._highlightDebounce) {
clearTimeout(this._highlightDebounce);
}
@@ -131,6 +169,11 @@ class IdeProtocolClient {
openFiles: this.getOpenFiles(),
});
break;
+ case "visibleFiles":
+ messenger.send("visibleFiles", {
+ visibleFiles: this.getVisibleFiles(),
+ });
+ break;
case "readFile":
messenger.send("readFile", {
contents: this.readFile(data.filepath),
@@ -166,7 +209,7 @@ class IdeProtocolClient {
case "showDiff":
this.showDiff(data.filepath, data.replacement, data.step_index);
break;
- case "openGUI":
+ case "getSessionId":
case "connected":
break;
default:
@@ -279,10 +322,6 @@ class IdeProtocolClient {
// ------------------------------------ //
// Initiate Request
- async openGUI(asRightWebviewPanel: boolean = false) {
- // Open the webview panel
- }
-
async getSessionId(): Promise<string> {
await new Promise((resolve, reject) => {
// Repeatedly try to connect to the server
@@ -298,10 +337,10 @@ class IdeProtocolClient {
}
}, 1000);
});
- const resp = await this.messenger?.sendAndReceive("openGUI", {});
- const sessionId = resp.sessionId;
- console.log("New Continue session with ID: ", sessionId);
- return sessionId;
+ const resp = await this.messenger?.sendAndReceive("getSessionId", {});
+ // console.log("New Continue session with ID: ", sessionId);
+ this._sessionId = resp.sessionId;
+ return resp.sessionId;
}
acceptRejectSuggestion(accept: boolean, key: SuggestionRanges) {
@@ -315,38 +354,55 @@ class IdeProtocolClient {
// ------------------------------------ //
// Respond to request
+ private editorIsTerminal(editor: vscode.TextEditor) {
+ return (
+ !!path.basename(editor.document.uri.fsPath).match(/\d/) ||
+ (editor.document.languageId === "plaintext" &&
+ editor.document.getText() === "accessible-buffer-accessible-buffer-")
+ );
+ }
+
getOpenFiles(): string[] {
return vscode.window.visibleTextEditors
- .filter((editor) => {
- return !(
- editor.document.uri.fsPath.endsWith("/1") ||
- (editor.document.languageId === "plaintext" &&
- editor.document.getText() ===
- "accessible-buffer-accessible-buffer-")
- );
- })
+ .filter((editor) => !this.editorIsTerminal(editor))
+ .map((editor) => {
+ return editor.document.uri.fsPath;
+ });
+ }
+
+ getVisibleFiles(): string[] {
+ return vscode.window.visibleTextEditors
+ .filter((editor) => !this.editorIsTerminal(editor))
.map((editor) => {
return editor.document.uri.fsPath;
});
}
saveFile(filepath: string) {
- vscode.window.visibleTextEditors.forEach((editor) => {
- if (editor.document.uri.fsPath === filepath) {
- editor.document.save();
- }
- });
+ vscode.window.visibleTextEditors
+ .filter((editor) => !this.editorIsTerminal(editor))
+ .forEach((editor) => {
+ if (editor.document.uri.fsPath === filepath) {
+ editor.document.save();
+ }
+ });
}
readFile(filepath: string): string {
let contents: string | undefined;
- vscode.window.visibleTextEditors.forEach((editor) => {
- if (editor.document.uri.fsPath === filepath) {
- contents = editor.document.getText();
+ vscode.window.visibleTextEditors
+ .filter((editor) => !this.editorIsTerminal(editor))
+ .forEach((editor) => {
+ if (editor.document.uri.fsPath === filepath) {
+ contents = editor.document.getText();
+ }
+ });
+ if (typeof contents === "undefined") {
+ if (fs.existsSync(filepath)) {
+ contents = fs.readFileSync(filepath, "utf-8");
+ } else {
+ contents = "";
}
- });
- if (!contents) {
- contents = fs.readFileSync(filepath, "utf-8");
}
return contents;
}
@@ -380,25 +436,27 @@ class IdeProtocolClient {
getHighlightedCode(): RangeInFile[] {
// TODO
let rangeInFiles: RangeInFile[] = [];
- vscode.window.visibleTextEditors.forEach((editor) => {
- editor.selections.forEach((selection) => {
- // if (!selection.isEmpty) {
- rangeInFiles.push({
- filepath: editor.document.uri.fsPath,
- range: {
- start: {
- line: selection.start.line,
- character: selection.start.character,
- },
- end: {
- line: selection.end.line,
- character: selection.end.character,
+ vscode.window.visibleTextEditors
+ .filter((editor) => !this.editorIsTerminal(editor))
+ .forEach((editor) => {
+ editor.selections.forEach((selection) => {
+ // if (!selection.isEmpty) {
+ rangeInFiles.push({
+ filepath: editor.document.uri.fsPath,
+ range: {
+ start: {
+ line: selection.start.line,
+ character: selection.start.character,
+ },
+ end: {
+ line: selection.end.line,
+ character: selection.end.character,
+ },
},
- },
+ });
+ // }
});
- // }
});
- });
return rangeInFiles;
}
diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts
index 487bbedf..dd24a8d8 100644
--- a/extension/src/debugPanel.ts
+++ b/extension/src/debugPanel.ts
@@ -6,78 +6,9 @@ import {
openEditorAndRevealRange,
} from "./util/vscode";
import { RangeInFile } from "./client";
+import { setFocusedOnContinueInput } from "./commands";
const WebSocket = require("ws");
-class StreamManager {
- private _fullText: string = "";
- private _insertionPoint: vscode.Position | undefined;
-
- private _addToEditor(update: string) {
- let editor =
- vscode.window.activeTextEditor || vscode.window.visibleTextEditors[0];
-
- if (typeof this._insertionPoint === "undefined") {
- if (editor?.selection.isEmpty) {
- this._insertionPoint = editor?.selection.active;
- } else {
- this._insertionPoint = editor?.selection.end;
- }
- }
- editor?.edit((editBuilder) => {
- if (this._insertionPoint) {
- editBuilder.insert(this._insertionPoint, update);
- this._insertionPoint = this._insertionPoint.translate(
- Array.from(update.matchAll(/\n/g)).length,
- update.length
- );
- }
- });
- }
-
- public closeStream() {
- this._fullText = "";
- this._insertionPoint = undefined;
- this._codeBlockStatus = "closed";
- this._pendingBackticks = 0;
- }
-
- private _codeBlockStatus: "open" | "closed" | "language-descriptor" =
- "closed";
- private _pendingBackticks: number = 0;
- public onStreamUpdate(update: string) {
- let textToInsert = "";
- for (let i = 0; i < update.length; i++) {
- switch (this._codeBlockStatus) {
- case "closed":
- if (update[i] === "`" && this._fullText.endsWith("``")) {
- this._codeBlockStatus = "language-descriptor";
- }
- break;
- case "language-descriptor":
- if (update[i] === " " || update[i] === "\n") {
- this._codeBlockStatus = "open";
- }
- break;
- case "open":
- if (update[i] === "`") {
- if (this._fullText.endsWith("``")) {
- this._codeBlockStatus = "closed";
- this._pendingBackticks = 0;
- } else {
- this._pendingBackticks += 1;
- }
- } else {
- textToInsert += "`".repeat(this._pendingBackticks) + update[i];
- this._pendingBackticks = 0;
- }
- break;
- }
- this._fullText += update[i];
- }
- this._addToEditor(textToInsert);
- }
-}
-
let websocketConnections: { [url: string]: WebsocketConnection | undefined } =
{};
@@ -127,8 +58,6 @@ class WebsocketConnection {
}
}
-let streamManager = new StreamManager();
-
export let debugPanelWebview: vscode.Webview | undefined;
export function setupDebugPanel(
panel: vscode.WebviewPanel | vscode.WebviewView,
@@ -147,10 +76,7 @@ export function setupDebugPanel(
.toString();
const isProduction = true; // context?.extensionMode === vscode.ExtensionMode.Development;
- if (!isProduction) {
- scriptUri = "http://localhost:5173/src/main.tsx";
- styleMainUri = "http://localhost:5173/src/main.css";
- } else {
+ if (isProduction) {
scriptUri = debugPanelWebview
.asWebviewUri(
vscode.Uri.joinPath(extensionUri, "react-app/dist/assets/index.js")
@@ -161,6 +87,9 @@ export function setupDebugPanel(
vscode.Uri.joinPath(extensionUri, "react-app/dist/assets/index.css")
)
.toString();
+ } else {
+ scriptUri = "http://localhost:5173/src/main.tsx";
+ styleMainUri = "http://localhost:5173/src/main.css";
}
panel.webview.options = {
@@ -175,11 +104,11 @@ export function setupDebugPanel(
return;
}
- let rangeInFile: RangeInFile = {
+ const rangeInFile: RangeInFile = {
range: e.selections[0],
filepath: e.textEditor.document.fileName,
};
- let filesystem = {
+ const filesystem = {
[rangeInFile.filepath]: e.textEditor.document.getText(),
};
panel.webview.postMessage({
@@ -217,13 +146,19 @@ export function setupDebugPanel(
url,
});
};
- const connection = new WebsocketConnection(
- url,
- onMessage,
- onOpen,
- onClose
- );
- websocketConnections[url] = connection;
+ try {
+ const connection = new WebsocketConnection(
+ url,
+ onMessage,
+ onOpen,
+ onClose
+ );
+ websocketConnections[url] = connection;
+ resolve(null);
+ } catch (e) {
+ console.log("Caught it!: ", e);
+ reject(e);
+ }
});
}
@@ -292,13 +227,8 @@ export function setupDebugPanel(
openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One);
break;
}
- case "streamUpdate": {
- // Write code at the position of the cursor
- streamManager.onStreamUpdate(data.update);
- break;
- }
- case "closeStream": {
- streamManager.closeStream();
+ case "blurContinueInput": {
+ setFocusedOnContinueInput(false);
break;
}
case "withProgress": {
diff --git a/extension/src/diffs.ts b/extension/src/diffs.ts
index b9ef8384..0bab326a 100644
--- a/extension/src/diffs.ts
+++ b/extension/src/diffs.ts
@@ -2,22 +2,31 @@ import * as os from "os";
import * as path from "path";
import * as fs from "fs";
import * as vscode from "vscode";
-import { ideProtocolClient } from "./activation/activate";
+import { extensionContext, ideProtocolClient } from "./activation/activate";
+import { getMetaKeyLabel } from "./util/util";
+import { devDataPath } from "./activation/environmentSetup";
interface DiffInfo {
originalFilepath: string;
newFilepath: string;
editor?: vscode.TextEditor;
step_index: number;
+ range: vscode.Range;
}
-export const DIFF_DIRECTORY = path.join(os.homedir(), ".continue", "diffs");
+export const DIFF_DIRECTORY = path
+ .join(os.homedir(), ".continue", "diffs")
+ .replace(/^C:/, "c:");
class DiffManager {
// Create a temporary file in the global .continue directory which displays the updated version
// Doing this because virtual files are read-only
private diffs: Map<string, DiffInfo> = new Map();
+ diffAtNewFilepath(newFilepath: string): DiffInfo | undefined {
+ return this.diffs.get(newFilepath);
+ }
+
private setupDirectory() {
// Make sure the diff directory exists
if (!fs.existsSync(DIFF_DIRECTORY)) {
@@ -35,6 +44,10 @@ class DiffManager {
return filepath.replace(/\\/g, "_").replace(/\//g, "_");
}
+ private getNewFilepath(originalFilepath: string): string {
+ return path.join(DIFF_DIRECTORY, this.escapeFilepath(originalFilepath));
+ }
+
private openDiffEditor(
originalFilepath: string,
newFilepath: string
@@ -47,7 +60,7 @@ class DiffManager {
return undefined;
}
- const rightUri = vscode.Uri.parse(newFilepath);
+ const rightUri = vscode.Uri.file(newFilepath);
const leftUri = vscode.Uri.file(originalFilepath);
const title = "Continue Diff";
console.log(
@@ -70,9 +83,42 @@ class DiffManager {
.getConfiguration("diffEditor", editor.document.uri)
.update("codeLens", true, vscode.ConfigurationTarget.Global);
+ if (
+ extensionContext?.globalState.get<boolean>(
+ "continue.showDiffInfoMessage"
+ ) !== false
+ ) {
+ vscode.window
+ .showInformationMessage(
+ `Accept (${getMetaKeyLabel()}⇧↩) or reject (${getMetaKeyLabel()}⇧⌫) at the top of the file.`,
+ "Got it",
+ "Don't show again"
+ )
+ .then((selection) => {
+ if (selection === "Don't show again") {
+ // Get the global state
+ extensionContext?.globalState.update(
+ "continue.showDiffInfoMessage",
+ false
+ );
+ }
+ });
+ }
+
return editor;
}
+ private _findFirstDifferentLine(contentA: string, contentB: string): number {
+ const linesA = contentA.split("\n");
+ const linesB = contentB.split("\n");
+ for (let i = 0; i < linesA.length && i < linesB.length; i++) {
+ if (linesA[i] !== linesB[i]) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
writeDiff(
originalFilepath: string,
newContent: string,
@@ -81,18 +127,20 @@ class DiffManager {
this.setupDirectory();
// Create or update existing diff
- const newFilepath = path.join(
- DIFF_DIRECTORY,
- this.escapeFilepath(originalFilepath)
- );
+ const newFilepath = this.getNewFilepath(originalFilepath);
fs.writeFileSync(newFilepath, newContent);
// Open the diff editor if this is a new diff
if (!this.diffs.has(newFilepath)) {
+ // Figure out the first line that is different
+ const oldContent = ideProtocolClient.readFile(originalFilepath);
+ const line = this._findFirstDifferentLine(oldContent, newContent);
+
const diffInfo: DiffInfo = {
originalFilepath,
newFilepath,
step_index,
+ range: new vscode.Range(line, 0, line + 1, 0),
};
this.diffs.set(newFilepath, diffInfo);
}
@@ -104,6 +152,11 @@ class DiffManager {
this.diffs.set(newFilepath, diffInfo);
}
+ vscode.commands.executeCommand(
+ "workbench.action.files.revert",
+ vscode.Uri.file(newFilepath)
+ );
+
return newFilepath;
}
@@ -117,10 +170,38 @@ class DiffManager {
fs.unlinkSync(diffInfo.newFilepath);
}
+ private inferNewFilepath() {
+ const activeEditorPath =
+ vscode.window.activeTextEditor?.document.uri.fsPath;
+ if (activeEditorPath && path.dirname(activeEditorPath) === DIFF_DIRECTORY) {
+ return activeEditorPath;
+ }
+ const visibleEditors = vscode.window.visibleTextEditors.map(
+ (editor) => editor.document.uri.fsPath
+ );
+ for (const editorPath of visibleEditors) {
+ if (path.dirname(editorPath) === DIFF_DIRECTORY) {
+ for (const otherEditorPath of visibleEditors) {
+ if (
+ path.dirname(otherEditorPath) !== DIFF_DIRECTORY &&
+ this.getNewFilepath(otherEditorPath) === editorPath
+ ) {
+ return editorPath;
+ }
+ }
+ }
+ }
+
+ if (this.diffs.size === 1) {
+ return Array.from(this.diffs.keys())[0];
+ }
+ return undefined;
+ }
+
acceptDiff(newFilepath?: string) {
- // If no newFilepath is provided and there is only one in the dictionary, use that
- if (!newFilepath && this.diffs.size === 1) {
- newFilepath = Array.from(this.diffs.keys())[0];
+ // When coming from a keyboard shortcut, we have to infer the newFilepath from visible text editors
+ if (!newFilepath) {
+ newFilepath = this.inferNewFilepath();
}
if (!newFilepath) {
console.log("No newFilepath provided to accept the diff");
@@ -132,20 +213,32 @@ class DiffManager {
console.log("No corresponding diffInfo found for newFilepath");
return;
}
- fs.writeFileSync(
- diffInfo.originalFilepath,
- fs.readFileSync(diffInfo.newFilepath)
- );
- this.cleanUpDiff(diffInfo);
+
+ // Save the right-side file, then copy over to original
+ vscode.workspace.textDocuments
+ .find((doc) => doc.uri.fsPath === newFilepath)
+ ?.save()
+ .then(() => {
+ fs.writeFileSync(
+ diffInfo.originalFilepath,
+ fs.readFileSync(diffInfo.newFilepath)
+ );
+ this.cleanUpDiff(diffInfo);
+ });
+
+ recordAcceptReject(true, diffInfo);
}
rejectDiff(newFilepath?: string) {
// If no newFilepath is provided and there is only one in the dictionary, use that
- if (!newFilepath && this.diffs.size === 1) {
- newFilepath = Array.from(this.diffs.keys())[0];
+ if (!newFilepath) {
+ newFilepath = this.inferNewFilepath();
}
if (!newFilepath) {
- console.log("No newFilepath provided to reject the diff");
+ console.log(
+ "No newFilepath provided to reject the diff, diffs.size was",
+ this.diffs.size
+ );
return;
}
const diffInfo = this.diffs.get(newFilepath);
@@ -157,12 +250,56 @@ class DiffManager {
// Stop the step at step_index in case it is still streaming
ideProtocolClient.deleteAtIndex(diffInfo.step_index);
- this.cleanUpDiff(diffInfo);
+ vscode.workspace.textDocuments
+ .find((doc) => doc.uri.fsPath === newFilepath)
+ ?.save()
+ .then(() => {
+ this.cleanUpDiff(diffInfo);
+ });
+
+ recordAcceptReject(false, diffInfo);
}
}
export const diffManager = new DiffManager();
+function recordAcceptReject(accepted: boolean, diffInfo: DiffInfo) {
+ const collectOn = vscode.workspace
+ .getConfiguration("continue")
+ .get<boolean>("dataSwitch");
+
+ if (collectOn) {
+ const devDataDir = devDataPath();
+ const suggestionsPath = path.join(devDataDir, "suggestions.json");
+
+ // Initialize suggestions list
+ let suggestions = [];
+
+ // Check if suggestions.json exists
+ if (fs.existsSync(suggestionsPath)) {
+ const rawData = fs.readFileSync(suggestionsPath, "utf-8");
+ suggestions = JSON.parse(rawData);
+ }
+
+ // Add the new suggestion to the list
+ suggestions.push({
+ accepted,
+ timestamp: Date.now(),
+ suggestion: diffInfo.originalFilepath,
+ });
+
+ // Send the suggestion to the server
+ ideProtocolClient.sendAcceptRejectSuggestion(accepted);
+
+ // Write the updated suggestions back to the file
+ fs.writeFileSync(
+ suggestionsPath,
+ JSON.stringify(suggestions, null, 4),
+ "utf-8"
+ );
+ }
+}
+
export async function acceptDiffCommand(newFilepath?: string) {
diffManager.acceptDiff(newFilepath);
ideProtocolClient.sendAcceptRejectDiff(true);
diff --git a/extension/src/extension.ts b/extension/src/extension.ts
index de8f55e3..6959ec05 100644
--- a/extension/src/extension.ts
+++ b/extension/src/extension.ts
@@ -3,17 +3,17 @@
*/
import * as vscode from "vscode";
-import {
- isPythonEnvSetup,
- startContinuePythonServer,
-} from "./activation/environmentSetup";
-async function dynamicImportAndActivate(
- context: vscode.ExtensionContext,
- showTutorial: boolean
-) {
+async function dynamicImportAndActivate(context: vscode.ExtensionContext) {
const { activateExtension } = await import("./activation/activate");
- await activateExtension(context, showTutorial);
+ try {
+ await activateExtension(context);
+ } catch (e) {
+ console.log("Error activating extension: ", e);
+ vscode.window.showInformationMessage(
+ "Error activating the Continue extension."
+ );
+ }
}
export function activate(context: vscode.ExtensionContext) {
@@ -25,7 +25,7 @@ export function activate(context: vscode.ExtensionContext) {
cancellable: false,
},
async () => {
- dynamicImportAndActivate(context, true);
+ dynamicImportAndActivate(context);
}
);
}
diff --git a/extension/src/lang-server/codeActions.ts b/extension/src/lang-server/codeActions.ts
new file mode 100644
index 00000000..07cf5f4e
--- /dev/null
+++ b/extension/src/lang-server/codeActions.ts
@@ -0,0 +1,53 @@
+import * as vscode from "vscode";
+
+class ContinueQuickFixProvider implements vscode.CodeActionProvider {
+ public static readonly providedCodeActionKinds = [
+ vscode.CodeActionKind.QuickFix,
+ ];
+
+ provideCodeActions(
+ document: vscode.TextDocument,
+ range: vscode.Range | vscode.Selection,
+ context: vscode.CodeActionContext,
+ token: vscode.CancellationToken
+ ): vscode.ProviderResult<(vscode.Command | vscode.CodeAction)[]> {
+ if (context.diagnostics.length === 0) {
+ return [];
+ }
+
+ const createQuickFix = (edit: boolean) => {
+ const diagnostic = context.diagnostics[0];
+ const quickFix = new vscode.CodeAction(
+ edit ? "Fix with Continue" : "Ask Continue",
+ vscode.CodeActionKind.QuickFix
+ );
+ quickFix.isPreferred = false;
+ const surroundingRange = new vscode.Range(
+ range.start.translate(-3, 0),
+ range.end.translate(3, 0)
+ );
+ quickFix.command = {
+ command: "continue.quickFix",
+ title: "Continue Quick Fix",
+ arguments: [
+ diagnostic.message,
+ document.getText(surroundingRange),
+ edit,
+ ],
+ };
+ return quickFix;
+ };
+ return [createQuickFix(true), createQuickFix(false)];
+ }
+}
+
+export default function registerQuickFixProvider() {
+ // In your extension's activate function:
+ vscode.languages.registerCodeActionsProvider(
+ { language: "*" },
+ new ContinueQuickFixProvider(),
+ {
+ providedCodeActionKinds: ContinueQuickFixProvider.providedCodeActionKinds,
+ }
+ );
+}
diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts
index 79126eaa..ba80e557 100644
--- a/extension/src/lang-server/codeLens.ts
+++ b/extension/src/lang-server/codeLens.ts
@@ -2,11 +2,12 @@ import * as vscode from "vscode";
import { editorToSuggestions, editorSuggestionsLocked } from "../suggestions";
import * as path from "path";
import * as os from "os";
-import { DIFF_DIRECTORY } from "../diffs";
+import { DIFF_DIRECTORY, diffManager } from "../diffs";
+import { getMetaKeyLabel } from "../util/util";
class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {
public provideCodeLenses(
document: vscode.TextDocument,
- token: vscode.CancellationToken
+ _: vscode.CancellationToken
): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> {
const suggestions = editorToSuggestions.get(document.uri.toString());
if (!suggestions) {
@@ -35,7 +36,7 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {
if (codeLenses.length === 2) {
codeLenses.push(
new vscode.CodeLens(range, {
- title: "(⌘⇧↩/⌘⇧⌫ to accept/reject all)",
+ title: `(${getMetaKeyLabel()}⇧↩/${getMetaKeyLabel()}⇧⌫ to accept/reject all)`,
command: "",
})
);
@@ -44,40 +45,28 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {
return codeLenses;
}
-
- onDidChangeCodeLenses?: vscode.Event<void> | undefined;
-
- constructor(emitter?: vscode.EventEmitter<void>) {
- if (emitter) {
- this.onDidChangeCodeLenses = emitter.event;
- this.onDidChangeCodeLenses(() => {
- if (vscode.window.activeTextEditor) {
- this.provideCodeLenses(
- vscode.window.activeTextEditor.document,
- new vscode.CancellationTokenSource().token
- );
- }
- });
- }
- }
}
class DiffViewerCodeLensProvider implements vscode.CodeLensProvider {
public provideCodeLenses(
document: vscode.TextDocument,
- token: vscode.CancellationToken
+ _: vscode.CancellationToken
): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> {
if (path.dirname(document.uri.fsPath) === DIFF_DIRECTORY) {
const codeLenses: vscode.CodeLens[] = [];
- const range = new vscode.Range(0, 0, 1, 0);
+ let range = new vscode.Range(0, 0, 1, 0);
+ const diffInfo = diffManager.diffAtNewFilepath(document.uri.fsPath);
+ if (diffInfo) {
+ range = diffInfo.range;
+ }
codeLenses.push(
new vscode.CodeLens(range, {
- title: "Accept ✅ (⌘⇧↩)",
+ title: `Accept All ✅ (${getMetaKeyLabel()}⇧↩)`,
command: "continue.acceptDiff",
arguments: [document.uri.fsPath],
}),
new vscode.CodeLens(range, {
- title: "Reject ❌ (⌘⇧⌫)",
+ title: `Reject All ❌ (${getMetaKeyLabel()}⇧⌫)`,
command: "continue.rejectDiff",
arguments: [document.uri.fsPath],
})
@@ -87,22 +76,6 @@ class DiffViewerCodeLensProvider implements vscode.CodeLensProvider {
return [];
}
}
-
- onDidChangeCodeLenses?: vscode.Event<void> | undefined;
-
- constructor(emitter?: vscode.EventEmitter<void>) {
- if (emitter) {
- this.onDidChangeCodeLenses = emitter.event;
- this.onDidChangeCodeLenses(() => {
- if (vscode.window.activeTextEditor) {
- this.provideCodeLenses(
- vscode.window.activeTextEditor.document,
- new vscode.CancellationTokenSource().token
- );
- }
- });
- }
- }
}
let diffsCodeLensDisposable: vscode.Disposable | undefined = undefined;
diff --git a/extension/src/suggestions.ts b/extension/src/suggestions.ts
index 6e5a444f..c2373223 100644
--- a/extension/src/suggestions.ts
+++ b/extension/src/suggestions.ts
@@ -1,9 +1,7 @@
import * as vscode from "vscode";
import { sendTelemetryEvent, TelemetryEvent } from "./telemetry";
import { openEditorAndRevealRange } from "./util/vscode";
-import { translate, readFileAtRange } from "./util/vscode";
-import * as fs from "fs";
-import * as path from "path";
+import { translate } from "./util/vscode";
import { registerAllCodeLensProviders } from "./lang-server/codeLens";
import { extensionContext, ideProtocolClient } from "./activation/activate";
@@ -214,62 +212,6 @@ function selectSuggestion(
: suggestion.newRange;
}
- let workspaceDir = vscode.workspace.workspaceFolders
- ? vscode.workspace.workspaceFolders[0]?.uri.fsPath
- : undefined;
-
- let collectOn = vscode.workspace
- .getConfiguration("continue")
- .get<boolean>("dataSwitch");
-
- if (workspaceDir && collectOn) {
- let continueDir = path.join(workspaceDir, ".continue");
-
- // Check if .continue directory doesn't exists
- if (!fs.existsSync(continueDir)) {
- fs.mkdirSync(continueDir);
- }
-
- let suggestionsPath = path.join(continueDir, "suggestions.json");
-
- // Initialize suggestions list
- let suggestions = [];
-
- // Check if suggestions.json exists
- if (fs.existsSync(suggestionsPath)) {
- let rawData = fs.readFileSync(suggestionsPath, "utf-8");
- suggestions = JSON.parse(rawData);
- }
-
- const accepted =
- accept === "new" || (accept === "selected" && suggestion.newSelected);
- suggestions.push({
- accepted,
- timestamp: Date.now(),
- suggestion: suggestion.newContent,
- });
- ideProtocolClient.sendAcceptRejectSuggestion(accepted);
-
- // Write the updated suggestions back to the file
- fs.writeFileSync(
- suggestionsPath,
- JSON.stringify(suggestions, null, 4),
- "utf-8"
- );
-
- // If it's not already there, add .continue to .gitignore
- const gitignorePath = path.join(workspaceDir, ".gitignore");
- if (fs.existsSync(gitignorePath)) {
- const gitignoreData = fs.readFileSync(gitignorePath, "utf-8");
- const gitIgnoreLines = gitignoreData.split("\n");
- if (!gitIgnoreLines.includes(".continue")) {
- fs.appendFileSync(gitignorePath, "\n.continue\n");
- }
- } else {
- fs.writeFileSync(gitignorePath, ".continue\n");
- }
- }
-
rangeToDelete = new vscode.Range(
rangeToDelete.start,
new vscode.Position(rangeToDelete.end.line, 0)
diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts
index b1df161b..3044898e 100644
--- a/extension/src/util/messenger.ts
+++ b/extension/src/util/messenger.ts
@@ -16,6 +16,8 @@ export abstract class Messenger {
abstract onClose(callback: () => void): void;
+ abstract onError(callback: () => void): void;
+
abstract sendAndReceive(messageType: string, data: any): Promise<any>;
}
@@ -26,6 +28,7 @@ export class WebsocketMessenger extends Messenger {
} = {};
private onOpenListeners: (() => void)[] = [];
private onCloseListeners: (() => void)[] = [];
+ private onErrorListeners: (() => void)[] = [];
private serverUrl: string;
_newWebsocket(): WebSocket {
@@ -43,6 +46,9 @@ export class WebsocketMessenger extends Messenger {
for (const listener of this.onCloseListeners) {
this.onClose(listener);
}
+ for (const listener of this.onErrorListeners) {
+ this.onError(listener);
+ }
for (const messageType in this.onMessageListeners) {
for (const listener of this.onMessageListeners[messageType]) {
this.onMessageType(messageType, listener);
@@ -151,4 +157,8 @@ export class WebsocketMessenger extends Messenger {
onClose(callback: () => void): void {
this.websocket.addEventListener("close", callback);
}
+
+ onError(callback: () => void): void {
+ this.websocket.addEventListener("error", callback);
+ }
}
diff --git a/extension/src/util/util.ts b/extension/src/util/util.ts
index d33593e1..dfc10c90 100644
--- a/extension/src/util/util.ts
+++ b/extension/src/util/util.ts
@@ -1,5 +1,6 @@
import { RangeInFile, SerializedDebugContext } from "../client";
import * as fs from "fs";
+const os = require("os");
function charIsEscapedAtIndex(index: number, str: string): boolean {
if (index === 0) return false;
@@ -113,3 +114,31 @@ export function debounced(delay: number, fn: Function) {
}, delay);
};
}
+
+type Platform = "mac" | "linux" | "windows" | "unknown";
+
+function getPlatform(): Platform {
+ const platform = os.platform();
+ if (platform === "darwin") {
+ return "mac";
+ } else if (platform === "linux") {
+ return "linux";
+ } else if (platform === "win32") {
+ return "windows";
+ } else {
+ return "unknown";
+ }
+}
+
+export function getMetaKeyLabel() {
+ const platform = getPlatform();
+ switch (platform) {
+ case "mac":
+ return "⌘";
+ case "linux":
+ case "windows":
+ return "^";
+ default:
+ return "⌘";
+ }
+}