Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ab5245d2d9 | |||
| 70b6af34e1 | |||
| 119462f663 | |||
| 8ee665cd57 | |||
| ace3f181ed | |||
| d3962acd30 | |||
| 18557c8f6e | |||
| 29b037726e | |||
| 59dc193f97 | |||
| bd778d259b | |||
| 605cf236a5 | |||
| 8bc3a0d277 | |||
| d80c88bc38 | |||
| dd34a5be57 | |||
| f4be80b724 | |||
| b40c58b78a | |||
| 64468b6f4d | |||
| 8f83b063f1 | |||
| f56772a07a | |||
| 38662d2824 | |||
| a51873c06a | |||
| a29543e4a7 | |||
| d4a2ac2796 | |||
| 4b5e09c124 | |||
| f8f48fb98b |
+67
-44
@@ -1,15 +1,13 @@
|
||||
# Contributing to Void
|
||||
|
||||
Welcome! 👋 This is a guide on how to contribute to Void. We want to make it as easy as possible to contribute, so if you have any questions or comments, reach out via email or discord!
|
||||
Welcome! 👋 This is the official guide on how to contribute to Void. We want to make it as easy as possible to contribute, so if you have any questions or comments, reach out via email or discord!
|
||||
|
||||
There are two main ways to contribute:
|
||||
There are a few ways to contribute:
|
||||
|
||||
- Suggest New Features ([discord](https://discord.gg/RSNjgaugJs))
|
||||
- Build New Features ([project](https://github.com/orgs/voideditor/projects/2/views/3))
|
||||
- Suggest New Features ([Discord](https://discord.gg/RSNjgaugJs))
|
||||
- Build New Features ([Project](https://github.com/orgs/voideditor/projects/2/views/3))
|
||||
- Submit Issues/Docs/Bugs ([Issues](https://github.com/voideditor/void/issues))
|
||||
|
||||
We use a [VSCode extension](https://code.visualstudio.com/api/get-started/your-first-extension) to implement most of Void's functionality. Scroll down to see 1. How to build/contribute to the Extension, or 2. How to build/contribute to the full IDE (for more native changes).
|
||||
|
||||
For some useful links we've compiled see [`VOID_USEFUL_LINKS.md`](https://github.com/voideditor/void/blob/main/VOID_USEFUL_LINKS.md).
|
||||
|
||||
## 1. Building the Extension
|
||||
|
||||
@@ -29,23 +27,51 @@ git clone https://github.com/voideditor/void
|
||||
npm install
|
||||
```
|
||||
|
||||
4. Compile the React by running `npm run build`. We created this build command to convert `sidebar/index.tsx` into `dist/`.
|
||||
1. Compile the React files by running `npm run build`. This build command converts all the Tailwind/React entrypoint files into raw .css and .js files in `dist/`.
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
5. Run the project by pressing <kbd>F5</kbd>.
|
||||
5. Run the extension in a new window by pressing <kbd>F5</kbd>.
|
||||
|
||||
This will start a new instance of VSCode with the extension enabled. If this doesn't work, you can press <kbd>Ctrl+Shift+P</kbd>, select "Debug: Start Debugging", and select "VSCode Extension Development".
|
||||
|
||||
## 2. Building the full IDE
|
||||
|
||||
If you want to work on the full IDE, please follow the steps below. If you have any questions/issues, you can refer to VSCode's full [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) page, which is where the steps below come from. Also feel free to submit an issue or get in touch with us with any build errors.
|
||||
If you want to work on the full IDE, please follow the steps below. If you have any questions/issues, you can refer to VSCode's full [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) page. Also feel free to submit an issue or get in touch with us with any build errors.
|
||||
|
||||
### a. Building on a Mac
|
||||
<!-- TODO say whether you can build each distribution on any Operating System, or if you need to build Windows on Windows, etc -->
|
||||
|
||||
To build on a Mac, open `void/` in VSCode. Make sure you've built the extension by following the steps above (or just run `cd ./extensions/void && npm install && npm run build && npm run compile && cd ../..`). Also make sure you have Python and XCode installed on your system (you probably do by default).
|
||||
### a. Build Prerequisites - Mac
|
||||
|
||||
If you're using a Mac, make sure you have Python and XCode installed (you probably do by default).
|
||||
|
||||
### b. Build Prerequisites - Windows
|
||||
|
||||
If you're using a Windows computer, first get [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) (recommended) or [VS Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) (not recommended). If you already have both, you might need to run the next few steps on both of them.
|
||||
|
||||
Find the box for Visual Studio 2022 (or VS Build Tools) and click Install/Modify.
|
||||
|
||||
Under Workloads, select "Desktop development with C++" and "Node.js build tools".
|
||||
|
||||
Under Individual components, select every item under:
|
||||
|
||||
- `MSVC v143 - VS 2022 C++ x64/x86 Spectre-mitigated libs (Latest)`,
|
||||
- `C++ ATL for latest build tools with Spectre Mitigations`,
|
||||
- `C++ MFC for latest build tools with Spectre Mitigations`.
|
||||
|
||||
Finally, click Install/Modify.
|
||||
|
||||
### c. Build Prerequisites - Linux
|
||||
|
||||
We haven't created prerequisite steps for building on Linux yet, but you can follow [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute).
|
||||
|
||||
### Build instructions
|
||||
|
||||
Before building Void, please follow the prerequisite steps above for your operating system. Also make sure you've already built the Void extension (or just run `cd ./extensions/void && npm install && npm run build && npm run compile && cd ../..`).
|
||||
|
||||
First, open `void/` in VSCode. Then:
|
||||
|
||||
1. Install all dependencies.
|
||||
|
||||
@@ -53,9 +79,13 @@ To build on a Mac, open `void/` in VSCode. Make sure you've built the extension
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Run `npm run watch`.
|
||||
2. Press <kbd>Ctrl+Shift+B</kbd>, or if you prefer using the terminal run `npm run watch`.
|
||||
|
||||
This can take ~5 min. It's done when you see something like:
|
||||
This can take ~5 min.
|
||||
|
||||
If you ran <kbd>Ctrl+Shift+B</kbd>, the build is done when you see two check marks.
|
||||
|
||||
If you ran `npm run watch`, the build is done when you see something like this:
|
||||
|
||||
```
|
||||
[watch-extensions] [00:37:39] Finished compilation extensions with 0 errors after 19303 ms
|
||||
@@ -66,48 +96,40 @@ This can take ~5 min. It's done when you see something like:
|
||||
|
||||
<!-- 3. Press <kbd>Ctrl+Shift+B</kbd> to start the build process. -->
|
||||
|
||||
3. In a new terminal, run `./scripts/code.sh`.
|
||||
1. In a new terminal, run `./scripts/code.sh` (Mac/Linux) or `/.scripts/code.bat` (Windows). This should open up the built IDE!
|
||||
|
||||
This should open up the built IDE after loading for some time. To see new changes without restarting the build, use <kbd>Ctrl+Shift+P</kbd> and run "Reload Window".
|
||||
|
||||
To bundle the IDE, run `npm run gulp vscode-darwin-arm64`. Here are the full options: `vscode-{win32-ia32 | win32-x64 | darwin-x64 | darwin-arm64 | linux-ia32 | linux-x64 | linux-arm}(-min)`
|
||||
To see new changes without restarting the build, press <kbd>Ctrl+Shift+P</kbd> and run "Reload Window" inside the new window.
|
||||
|
||||
Now that you're set up, feel free to check out our [Issues](https://github.com/voideditor/void/issues) page!
|
||||
|
||||
**Common Fixes:**
|
||||
|
||||
### Common Fixes
|
||||
|
||||
- Make sure you have the same NodeJS version as `.nvmrc`.
|
||||
|
||||
- If you see `X [ERROR] Cannot start service: Host version "0.23.1" does not match binary version "0.23.0"`, run `npm i -D esbuild@0.23.0`
|
||||
|
||||
### b. Building on Windows
|
||||
|
||||
To build on Windows, please refer to [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute). We recommend building on Mac; we're Windows users who switch to Mac to build right now.
|
||||
|
||||
<!-- Get [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community). Also find the boxes for "Desktop development with C++" and "Node.js development" and get those, too.
|
||||
|
||||
If you get a node-gyp error in the next few steps, you should also get [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools), find the Visual Studio Build Tools box, click Install (or Modify), then in Individual Components:
|
||||
check every item under `MSVC v143 - VS 2022 C++ x64/x86 Spectre-mitigated libs (Latest)`, `C++ ATL for latest build tools with Spectre Mitigations`, and `C++ MFC for latest build tools with Spectre Mitigations`.
|
||||
- If you see `[ERROR] Cannot start service: Host version "0.23.1" does not match binary version "0.23.0"`, run `npm i -D esbuild@0.23.0` or do a clean install of your npm dependencies.
|
||||
|
||||
|
||||
```
|
||||
npm config set msvs_version 2022
|
||||
```
|
||||
-->
|
||||
## Bundling
|
||||
|
||||
To bundle the IDE into an executable, run `npm run gulp vscode-darwin-arm64`.
|
||||
|
||||
Here are the full options: `vscode-{win32-ia32 | win32-x64 | darwin-x64 | darwin-arm64 | linux-ia32 | linux-x64 | linux-arm}(-min)`
|
||||
|
||||
|
||||
## Roadmap
|
||||
|
||||
Here are the most important topics on our Roadmap. More ⭐'s = more important.
|
||||
|
||||
These sometimes get outdated - please refer to our Issues page for the latest issues.
|
||||
These sometimes get outdated - please refer to our [Issues](https://github.com/voideditor/void/issues) page for the latest issues.
|
||||
|
||||
## ⭐⭐⭐ Make History work well.
|
||||
|
||||
When the user submits a response or presses the apply/accept/reject button, we should add these events to the history, allowing the user to undo/redo them. Right now there is unexpected behavior if the user tries to undo or redo their changes.
|
||||
|
||||
## ⭐⭐⭐ Build Cursor-style quick edits (ctrl+k).
|
||||
## ⭐⭐⭐ Build Cursor-style quick edits (Ctrl+K).
|
||||
|
||||
When the user presses ctrl+k, an input box should appear inline with the code that they were selecting. This is somewhat difficult to do because an extension alone cannot do this, and it requires creating a new component in the IDE. We think you can modify vscode's built-in "codelens" or "zone widget" components, but we are open to alternatives.
|
||||
When the user presses Ctrl+K, an input box should appear inline with the code that they were selecting. This is somewhat difficult to do because an extension alone cannot do this, and it requires creating a new component in the IDE. We think you can modify vscode's built-in "codelens" or "zone widget" components, but we are open to alternatives.
|
||||
|
||||
## ⭐⭐⭐ Creative.
|
||||
|
||||
@@ -117,21 +139,17 @@ Eventually, we want to build a convenient API for creating AI tools. The API wil
|
||||
|
||||
## ⭐ One-stars.
|
||||
|
||||
⭐ Let the user accept / reject all Diffs in an entire file via the sidebar.
|
||||
⭐ Let the user Accept / Reject all Diffs in an entire file via the sidebar.
|
||||
|
||||
# Guidelines
|
||||
|
||||
Please don't make big refactors without speaking with us first. We'd like to keep the codebase similar to vscode so we can periodically rebase, and if we have big changes that gets complicated.
|
||||
We're always glad to talk about new ideas, help you get set up, and make sure your changes align with our vision for the project. Feel free to shoot us a message in the #general channel of the [Discord](https://discord.gg/RSNjgaugJs) for any reason. Please check in especially if you want to make a lot of changes or build a large new feature.
|
||||
|
||||
# Submitting a Pull Request
|
||||
|
||||
Please submit a pull request once you've made a change. Here are a few guidelines:
|
||||
|
||||
- A PR should be about one _single_ feature change. The fewer items you change, the more likely the PR is to be accepted.
|
||||
## Submitting a Pull Request
|
||||
|
||||
- Your PR should contain a description that first explains at a high level what you did, and then describes the exact changes you made (and to which files). Please don't use vague statements like "refactored code" or "improved types" (instead, describe what code you refactored, or what types you changed).
|
||||
|
||||
- Try to avoid refactoring and making feature changes in the same PR.
|
||||
Please submit a pull request once you've made a change. You don't need to submit an issue.
|
||||
|
||||
# Relevant files
|
||||
|
||||
@@ -156,3 +174,8 @@ We keep track of all the files we've changed with Void so it's easy to rebase:
|
||||
- build/npm/dirs.js
|
||||
|
||||
- vscode.proposed.editorInsets.d.ts - not modified, but code copied
|
||||
|
||||
|
||||
## References
|
||||
|
||||
For some useful links we've compiled on VSCode, see [`VOID_USEFUL_LINKS.md`](https://github.com/voideditor/void/blob/main/VOID_USEFUL_LINKS.md).
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
const tailwindcss = require('tailwindcss')
|
||||
const autoprefixer = require('autoprefixer')
|
||||
const postcss = require('postcss')
|
||||
const fs = require('fs')
|
||||
|
||||
const from = 'src/sidebar/styles.css'
|
||||
const to = 'dist/sidebar/styles.css'
|
||||
|
||||
const original_css_contents = fs.readFileSync(from, 'utf8')
|
||||
|
||||
postcss([
|
||||
tailwindcss, // this compiles tailwind of all the files specified in tailwind.config.json
|
||||
autoprefixer,
|
||||
])
|
||||
.process(original_css_contents, { from, to })
|
||||
.then(processed_css_contents => { fs.writeFileSync(to, processed_css_contents.css) })
|
||||
.catch(error => {
|
||||
console.error('Error in build-css:', error)
|
||||
})
|
||||
@@ -1,13 +0,0 @@
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
// Build JS
|
||||
esbuild.build({
|
||||
entryPoints: ['src/sidebar/index.tsx'],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
outfile: 'dist/sidebar/index.js',
|
||||
format: 'iife', // apparently iife is safe for browsers (safer than cjs)
|
||||
platform: 'browser',
|
||||
external: ['vscode'],
|
||||
}).catch(() => process.exit(1));
|
||||
@@ -0,0 +1,59 @@
|
||||
const tailwindcss = require('tailwindcss')
|
||||
const autoprefixer = require('autoprefixer')
|
||||
const postcss = require('postcss')
|
||||
const fs = require('fs')
|
||||
|
||||
const convertTailwindToCSS = ({ from, to }) => {
|
||||
console.log('converting ', from, ' --> ', to)
|
||||
|
||||
const original_css_contents = fs.readFileSync(from, 'utf8')
|
||||
|
||||
return postcss([
|
||||
tailwindcss, // this compiles tailwind of all the files specified in tailwind.config.json
|
||||
autoprefixer,
|
||||
])
|
||||
.process(original_css_contents, { from, to })
|
||||
.then(processed_css_contents => { fs.writeFileSync(to, processed_css_contents.css) })
|
||||
.catch(error => {
|
||||
console.error('Error in build-css:', error)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
const convertTSXtoJS = async ({ from, to }) => {
|
||||
console.log('converting ', from, ' --> ', to)
|
||||
|
||||
return esbuild.build({
|
||||
entryPoints: [from],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
outfile: to,
|
||||
format: 'iife', // apparently iife is safe for browsers (safer than cjs)
|
||||
platform: 'browser',
|
||||
external: ['vscode'],
|
||||
}).catch(() => process.exit(1));
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// convert tsx to js
|
||||
await convertTSXtoJS({
|
||||
from: 'src/webviews/sidebar/index.tsx',
|
||||
to: 'dist/webviews/sidebar/index.js',
|
||||
})
|
||||
|
||||
await convertTSXtoJS({
|
||||
from: 'src/webviews/ctrlk/index.tsx',
|
||||
to: 'dist/webviews/ctrlk/index.js',
|
||||
})
|
||||
|
||||
// convert tailwind to css
|
||||
await convertTailwindToCSS({
|
||||
from: 'src/webviews/styles.css',
|
||||
to: 'dist/webviews/styles.css',
|
||||
})
|
||||
|
||||
})()
|
||||
|
||||
Generated
-8
@@ -6013,14 +6013,6 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.52.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.0.tgz",
|
||||
"integrity": "sha512-OeWhNpABLCeTqubfqLMXGsqf6OmPU6pHM85kF3dhy6kq5hnhuVS1p3VrEW/XhWHc71P2tHyS5JFySD8mgs1crw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [],
|
||||
"main": "./out/extension.js",
|
||||
"main": "./out/extension/extension.js",
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"title": "Void",
|
||||
@@ -104,7 +104,7 @@
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"build": "rimraf dist && node build-tsx.js && node build-css.js",
|
||||
"build": "rimraf dist && node build/build.js",
|
||||
"pretest": "tsc -p ./ && eslint src --ext ts",
|
||||
"test": "vscode-test"
|
||||
},
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
// renders the code from `src/sidebar`
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
function generateNonce() {
|
||||
let text = "";
|
||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||
public static readonly viewId = 'void.viewnumberone';
|
||||
|
||||
public webview: Promise<vscode.Webview> // used to send messages to the webview, resolved by _res in resolveWebviewView
|
||||
private _res: (c: vscode.Webview) => void // used to resolve the webview
|
||||
|
||||
private readonly _extensionUri: vscode.Uri
|
||||
|
||||
// private _webviewView?: vscode.WebviewView;
|
||||
private _webviewDeps: string[] = [];
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
// const extensionPath = context.extensionPath // the directory where the extension is installed, might be useful later... was included in webviewProvider code
|
||||
this._extensionUri = context.extensionUri
|
||||
|
||||
let temp_res: typeof this._res | undefined = undefined
|
||||
this.webview = new Promise((res, rej) => { temp_res = res })
|
||||
if (!temp_res) throw new Error("Void sidebar provider: resolver was undefined")
|
||||
this._res = temp_res
|
||||
}
|
||||
|
||||
// called by us
|
||||
updateWebviewHTML(webview: vscode.Webview) {
|
||||
this._webviewDeps = []
|
||||
|
||||
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/index.js'));
|
||||
const stylesUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'dist/sidebar/styles.css'));
|
||||
const rootUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri));
|
||||
const nonce = generateNonce();
|
||||
|
||||
const webviewHTML = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Custom View</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="img-src vscode-resource: https:; script-src 'nonce-${nonce}'; style-src vscode-resource: 'unsafe-inline' http: https: data:;">
|
||||
<base href="${rootUri}/">
|
||||
<link href="${stylesUri}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="ctrlkroot"></div>
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
webview.html = webviewHTML;
|
||||
}
|
||||
|
||||
|
||||
// called internally by vscode
|
||||
resolveWebviewView(
|
||||
webviewView: vscode.WebviewView,
|
||||
context: vscode.WebviewViewResolveContext,
|
||||
token: vscode.CancellationToken,
|
||||
) {
|
||||
|
||||
const webview = webviewView.webview;
|
||||
|
||||
webview.options = {
|
||||
enableScripts: true,
|
||||
localResourceRoots: [this._extensionUri]
|
||||
};
|
||||
|
||||
this.updateWebviewHTML(webview);
|
||||
|
||||
// resolve webview and _webviewView
|
||||
this._res(webview);
|
||||
// this._webviewView = webviewView;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { AbortRef, OnFinalMessage, OnText, sendLLMMessage } from "./sendLLMMessage"
|
||||
import { VoidConfig } from '../sidebar/contextForConfig';
|
||||
import { findDiffs } from '../findDiffs';
|
||||
import { VoidConfig } from '../webviews/common/contextForConfig';
|
||||
import { searchDiffChunkInstructions, writeFileWithDiffInstructions } from './systemPrompts';
|
||||
import { throttle } from 'lodash';
|
||||
import { readFileContentOfUri } from './readFileContentOfUri';
|
||||
import { readFileContentOfUri } from '../extension/extensionLib/readFileContentOfUri';
|
||||
|
||||
type Res<T> = ((value: T) => void)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import OpenAI from 'openai';
|
||||
import { Ollama } from 'ollama/browser'
|
||||
import { VoidConfig } from '../sidebar/contextForConfig';
|
||||
import { VoidConfig } from '../webviews/common/contextForConfig'
|
||||
|
||||
export type AbortRef = { current: (() => void) | null }
|
||||
|
||||
@@ -39,6 +39,13 @@ type SendLLMMessageFnTypeExternal = (params: {
|
||||
|
||||
|
||||
|
||||
const parseMaxTokensStr = (maxTokensStr: string) => {
|
||||
// parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN
|
||||
let int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr)
|
||||
if (Number.isNaN(int))
|
||||
return undefined
|
||||
return int
|
||||
}
|
||||
|
||||
// Anthropic
|
||||
const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig }) => {
|
||||
@@ -113,9 +120,11 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal
|
||||
let openai: OpenAI
|
||||
let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
||||
|
||||
let maxTokens = parseMaxTokensStr(voidConfig.default.maxTokens)
|
||||
|
||||
if (voidConfig.default.whichApi === 'openAI') {
|
||||
openai = new OpenAI({ apiKey: voidConfig.openAI.apikey, dangerouslyAllowBrowser: true });
|
||||
options = { model: voidConfig.openAI.model, messages: messages, stream: true, max_completion_tokens: parseInt(voidConfig.default.maxTokens) }
|
||||
options = { model: voidConfig.openAI.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
}
|
||||
else if (voidConfig.default.whichApi === 'openRouter') {
|
||||
openai = new OpenAI({
|
||||
@@ -125,11 +134,11 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal
|
||||
"X-Title": 'Void Editor', // Optional. Shows in rankings on openrouter.ai.
|
||||
},
|
||||
});
|
||||
options = { model: voidConfig.openRouter.model, messages: messages, stream: true, max_completion_tokens: parseInt(voidConfig.default.maxTokens) }
|
||||
options = { model: voidConfig.openRouter.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
}
|
||||
else if (voidConfig.default.whichApi === 'openAICompatible') {
|
||||
openai = new OpenAI({ baseURL: voidConfig.openAICompatible.endpoint, apiKey: voidConfig.openAICompatible.apikey, dangerouslyAllowBrowser: true })
|
||||
options = { model: voidConfig.openAICompatible.model, messages: messages, stream: true, max_completion_tokens: parseInt(voidConfig.default.maxTokens) }
|
||||
options = { model: voidConfig.openAICompatible.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
}
|
||||
else {
|
||||
console.error(`sendOpenAIMsg: invalid whichApi: ${voidConfig.default.whichApi}`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { PartialVoidConfig } from '../sidebar/contextForConfig';
|
||||
import { PartialVoidConfig } from '../webviews/common/contextForConfig'
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { DiffProvider } from './DiffProvider';
|
||||
|
||||
import { DiffArea, ChatThreads, MessageFromSidebar, MessageToSidebar } from './common/shared_types';
|
||||
import { SidebarWebviewProvider } from './SidebarWebviewProvider';
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { applyDiffLazily } from './common/ctrlL';
|
||||
import { getVoidConfig } from './sidebar/contextForConfig';
|
||||
import { readFileContentOfUri } from './common/readFileContentOfUri';
|
||||
import { AbortRef } from './common/sendLLMMessage';
|
||||
import { DiffProvider } from './extension/DiffProvider';
|
||||
import { SidebarWebviewProvider } from './extension/providers/SidebarWebviewProvider';
|
||||
import { getVoidConfigFromPartial } from './webviews/common/contextForConfig';
|
||||
import { applyDiffLazily } from './extension/ctrlL';
|
||||
import { readFileContentOfUri } from './extension/extensionLib/readFileContentOfUri';
|
||||
|
||||
// this comes from vscode.proposed.editorInsets.d.ts
|
||||
declare module 'vscode' {
|
||||
@@ -153,7 +154,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
const docUri = editor.document.uri
|
||||
const fileStr = await readFileContentOfUri(docUri)
|
||||
const voidConfig = getVoidConfig(context.globalState.get('partialVoidConfig') ?? {})
|
||||
const voidConfig = getVoidConfigFromPartial(context.globalState.get('partialVoidConfig') ?? {})
|
||||
|
||||
await applyDiffLazily({ docUri, oldFileStr: fileStr, diffRepr: m.diffRepr, voidConfig, diffProvider, diffArea, abortRef: abortApplyRef })
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { findDiffs } from './findDiffs';
|
||||
import { Diff, DiffArea, BaseDiff, } from './common/shared_types';
|
||||
import { readFileContentOfUri } from './common/readFileContentOfUri';
|
||||
import { throttle } from 'lodash';
|
||||
import { DiffArea, BaseDiff, Diff } from '../common/shared_types';
|
||||
import { readFileContentOfUri } from './extensionLib/readFileContentOfUri';
|
||||
|
||||
|
||||
const THROTTLE_TIME = 100
|
||||
@@ -1,19 +1,13 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { AbortRef, OnFinalMessage, OnText, sendLLMMessage } from "./sendLLMMessage"
|
||||
import { VoidConfig } from '../sidebar/contextForConfig';
|
||||
import { findDiffs } from '../findDiffs';
|
||||
import { searchDiffChunkInstructions, writeFileWithDiffInstructions } from './systemPrompts';
|
||||
import { throttle } from 'lodash';
|
||||
import { readFileContentOfUri } from './readFileContentOfUri';
|
||||
import { DiffProvider } from '../DiffProvider';
|
||||
import { DiffArea } from './shared_types';
|
||||
import { AbortRef, sendLLMMessage } from '../common/sendLLMMessage';
|
||||
import { DiffArea } from '../common/shared_types';
|
||||
import { writeFileWithDiffInstructions, searchDiffChunkInstructions } from '../common/systemPrompts';
|
||||
import { VoidConfig } from '../webviews/common/contextForConfig';
|
||||
import { DiffProvider } from './DiffProvider';
|
||||
import { readFileContentOfUri } from './extensionLib/readFileContentOfUri';
|
||||
|
||||
const LINES_PER_CHUNK = 20 // number of lines to search at a time
|
||||
|
||||
// const THRTOTLE_TIME = 100 // minimum time between edits
|
||||
// throttle(
|
||||
// THRTOTLE_TIME, { trailing: true }
|
||||
// )
|
||||
|
||||
type CompetedReturn = { isFinished: true, } | { isFinished?: undefined, }
|
||||
const streamChunk = ({ diffProvider, docUri, oldFileStr, completedStr, diffRepr, diffArea, voidConfig, abortRef }: { diffProvider: DiffProvider, docUri: vscode.Uri, oldFileStr: string, completedStr: string, diffRepr: string, voidConfig: VoidConfig, diffArea: DiffArea, abortRef: AbortRef }) => {
|
||||
@@ -0,0 +1,203 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { applyDiffLazily } from './ctrlL';
|
||||
import { readFileContentOfUri } from './extensionLib/readFileContentOfUri';
|
||||
import { MessageToSidebar, MessageFromSidebar, DiffArea, ChatThreads } from '../common/shared_types';
|
||||
import { DiffProvider } from './DiffProvider';
|
||||
import { getVoidConfigFromPartial } from '../webviews/common/contextForConfig';
|
||||
import { CtrlKWebviewProvider } from './providers/CtrlKWebviewProvider';
|
||||
import { SidebarWebviewProvider } from './providers/SidebarWebviewProvider';
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { AbortRef } from '../common/sendLLMMessage';
|
||||
|
||||
// this comes from vscode.proposed.editorInsets.d.ts
|
||||
declare module 'vscode' {
|
||||
export interface WebviewEditorInset {
|
||||
readonly editor: vscode.TextEditor;
|
||||
readonly line: number;
|
||||
readonly height: number;
|
||||
readonly webview: vscode.Webview;
|
||||
readonly onDidDispose: Event<void>;
|
||||
dispose(): void;
|
||||
}
|
||||
export namespace window {
|
||||
export function createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options?: vscode.WebviewOptions): WebviewEditorInset;
|
||||
}
|
||||
}
|
||||
|
||||
const roundRangeToLines = (selection: vscode.Selection) => {
|
||||
return new vscode.Range(selection.start.line, 0, selection.end.line, Number.MAX_SAFE_INTEGER)
|
||||
}
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
// 1. Mount the chat sidebar
|
||||
const sidebarWebviewProvider = new SidebarWebviewProvider(context);
|
||||
context.subscriptions.push(
|
||||
vscode.window.registerWebviewViewProvider(SidebarWebviewProvider.viewId, sidebarWebviewProvider, { webviewOptions: { retainContextWhenHidden: true } })
|
||||
);
|
||||
|
||||
// 1.5
|
||||
const ctrlKWebviewProvider = new CtrlKWebviewProvider(context)
|
||||
|
||||
|
||||
// 2. ctrl+l
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('void.ctrl+l', () => {
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor) return
|
||||
|
||||
// show the sidebar
|
||||
vscode.commands.executeCommand('workbench.view.extension.voidViewContainer');
|
||||
// vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar
|
||||
|
||||
// get the range of the selection
|
||||
const selectionRange = roundRangeToLines(editor.selection);
|
||||
|
||||
// get the text the user is selecting
|
||||
const selectionStr = editor.document.getText(selectionRange);
|
||||
|
||||
// get the file the user is in
|
||||
const filePath = editor.document.uri;
|
||||
|
||||
// send message to the webview (Sidebar.tsx)
|
||||
sidebarWebviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+l', selection: { selectionStr, selectionRange, filePath } } satisfies MessageToSidebar));
|
||||
})
|
||||
);
|
||||
|
||||
// 2.5: ctrl+k
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('void.ctrl+k', () => {
|
||||
console.log('CTRLK PRESSED')
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor) return
|
||||
|
||||
// get the range of the selection
|
||||
const selectionRange = roundRangeToLines(editor.selection);
|
||||
|
||||
// get the text the user is selecting
|
||||
const selectionStr = editor.document.getText(selectionRange);
|
||||
|
||||
// get the file the user is in
|
||||
const filePath = editor.document.uri;
|
||||
|
||||
// send message to the webview (Sidebar.tsx)
|
||||
ctrlKWebviewProvider.onPressCtrlK()
|
||||
})
|
||||
);
|
||||
|
||||
// 3. Show an approve/reject codelens above each change
|
||||
const diffProvider = new DiffProvider();
|
||||
context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', diffProvider));
|
||||
|
||||
// 4. Add approve/reject commands
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.acceptDiff', async (params) => {
|
||||
diffProvider.acceptDiff(params)
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.rejectDiff', async (params) => {
|
||||
diffProvider.rejectDiff(params)
|
||||
}));
|
||||
|
||||
// 5. Receive messages from sidebar
|
||||
sidebarWebviewProvider.webview.then(
|
||||
webview => {
|
||||
|
||||
// top navigation bar commands
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.startNewThread', async () => {
|
||||
webview.postMessage({ type: 'startNewThread' } satisfies MessageToSidebar)
|
||||
}))
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.toggleThreadSelector', async () => {
|
||||
webview.postMessage({ type: 'toggleThreadSelector' } satisfies MessageToSidebar)
|
||||
}))
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.toggleSettings', async () => {
|
||||
webview.postMessage({ type: 'toggleSettings' } satisfies MessageToSidebar)
|
||||
}));
|
||||
|
||||
// Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`)
|
||||
webview.onDidReceiveMessage(async (m: MessageFromSidebar) => {
|
||||
|
||||
const abortApplyRef: AbortRef = { current: null }
|
||||
|
||||
if (m.type === 'requestFiles') {
|
||||
|
||||
// get contents of all file paths
|
||||
const files = await Promise.all(
|
||||
m.filepaths.map(async (filepath) => ({ filepath, content: await readFileContentOfUri(filepath) }))
|
||||
)
|
||||
|
||||
// send contents to webview
|
||||
webview.postMessage({ type: 'files', files, } satisfies MessageToSidebar)
|
||||
|
||||
}
|
||||
else if (m.type === 'applyChanges') {
|
||||
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor) {
|
||||
vscode.window.showInformationMessage('No active editor!')
|
||||
return
|
||||
}
|
||||
// create an area to show diffs
|
||||
const partialDiffArea: Omit<DiffArea, 'diffareaid'> = {
|
||||
startLine: 0, // in ctrl+L the start and end lines are the full document
|
||||
endLine: editor.document.lineCount,
|
||||
originalStartLine: 0,
|
||||
originalEndLine: editor.document.lineCount,
|
||||
sweepIndex: null,
|
||||
}
|
||||
const diffArea = diffProvider.createDiffArea(editor.document.uri, partialDiffArea, await readFileContentOfUri(editor.document.uri))
|
||||
|
||||
const docUri = editor.document.uri
|
||||
const fileStr = await readFileContentOfUri(docUri)
|
||||
const voidConfig = getVoidConfigFromPartial(context.globalState.get('partialVoidConfig') ?? {})
|
||||
|
||||
await applyDiffLazily({ docUri, oldFileStr: fileStr, diffRepr: m.diffRepr, voidConfig, diffProvider, diffArea, abortRef: abortApplyRef })
|
||||
}
|
||||
else if (m.type === 'getPartialVoidConfig') {
|
||||
const partialVoidConfig = context.globalState.get('partialVoidConfig') ?? {}
|
||||
webview.postMessage({ type: 'partialVoidConfig', partialVoidConfig } satisfies MessageToSidebar)
|
||||
}
|
||||
else if (m.type === 'persistPartialVoidConfig') {
|
||||
const partialVoidConfig = m.partialVoidConfig
|
||||
context.globalState.update('partialVoidConfig', partialVoidConfig)
|
||||
}
|
||||
else if (m.type === 'getAllThreads') {
|
||||
const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {}
|
||||
webview.postMessage({ type: 'allThreads', threads } satisfies MessageToSidebar)
|
||||
}
|
||||
else if (m.type === 'persistThread') {
|
||||
const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {}
|
||||
const updatedThreads: ChatThreads = { ...threads, [m.thread.id]: m.thread }
|
||||
context.workspaceState.update('allThreads', updatedThreads)
|
||||
}
|
||||
else if (m.type === 'getDeviceId') {
|
||||
let deviceId = context.globalState.get('void_deviceid')
|
||||
if (!deviceId || typeof deviceId !== 'string') {
|
||||
deviceId = uuidv4()
|
||||
context.globalState.update('void_deviceid', deviceId)
|
||||
}
|
||||
webview.postMessage({ type: 'deviceId', deviceId: deviceId as string } satisfies MessageToSidebar)
|
||||
}
|
||||
else {
|
||||
console.error('unrecognized command', m)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
// Gets called when user presses ctrl + k (mounts ctrl+k-style codelens)
|
||||
// TODO need to build this
|
||||
// const ctrlKCodeLensProvider = new CtrlKCodeLensProvider();
|
||||
// context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', ctrlKCodeLensProvider));
|
||||
// context.subscriptions.push(
|
||||
// vscode.commands.registerCommand('void.ctrl+k', () => {
|
||||
// const editor = vscode.window.activeTextEditor;
|
||||
// if (!editor)
|
||||
// return
|
||||
// ctrlKCodeLensProvider.addNewCodeLens(editor.document, editor.selection);
|
||||
// // vscode.commands.executeCommand('editor.action.showHover'); // apparently this refreshes the codelenses by having the internals call provideCodeLenses
|
||||
// })
|
||||
// )
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import * as vscode from 'vscode'
|
||||
|
||||
function generateNonce() {
|
||||
let text = "";
|
||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
// call this when you have access to the webview to set its html
|
||||
export const updateWebviewHTML = (webview: vscode.Webview, extensionUri: vscode.Uri, { jsOutLocation, cssOutLocation }: { jsOutLocation: string, cssOutLocation: string }, props?: object) => {
|
||||
|
||||
// 'dist/sidebar/index.js'
|
||||
// 'dist/sidebar/styles.css'
|
||||
|
||||
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, jsOutLocation));
|
||||
const stylesUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, cssOutLocation));
|
||||
const rootUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri));
|
||||
const nonce = generateNonce();
|
||||
|
||||
const webviewHTML = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Custom View</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="img-src vscode-resource: https:; script-src 'nonce-${nonce}'; style-src vscode-resource: 'unsafe-inline' http: https: data:;">
|
||||
<base href="${rootUri}/">
|
||||
<link href="${stylesUri}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" ${props ? `data-void-props="${encodeURIComponent(JSON.stringify(props))}"` : ''}></div>
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
webview.html = webviewHTML
|
||||
|
||||
webview.options = {
|
||||
enableScripts: true,
|
||||
localResourceRoots: [extensionUri]
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
|
||||
import { Range } from 'vscode';
|
||||
import { diffLines, Change } from 'diff';
|
||||
import { BaseDiff } from './common/shared_types';
|
||||
import { BaseDiff } from '../common/shared_types';
|
||||
|
||||
|
||||
|
||||
// class Range {
|
||||
@@ -0,0 +1,57 @@
|
||||
// renders the code from `src/sidebar`
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { updateWebviewHTML as _updateWebviewHTML, updateWebviewHTML } from '../extensionLib/updateWebviewHTML';
|
||||
|
||||
// this comes from vscode.proposed.editorInsets.d.ts
|
||||
declare module 'vscode' {
|
||||
export interface WebviewEditorInset {
|
||||
readonly editor: vscode.TextEditor;
|
||||
readonly line: number;
|
||||
readonly height: number;
|
||||
readonly webview: vscode.Webview;
|
||||
readonly onDidDispose: Event<void>;
|
||||
dispose(): void;
|
||||
}
|
||||
export namespace window {
|
||||
export function createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options?: vscode.WebviewOptions): WebviewEditorInset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class CtrlKWebviewProvider {
|
||||
|
||||
private readonly _extensionUri: vscode.Uri
|
||||
|
||||
private _idPool = 0
|
||||
|
||||
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
this._extensionUri = context.extensionUri
|
||||
}
|
||||
|
||||
onPressCtrlK() {
|
||||
|
||||
// // TODO if currently selecting a ctrl k element, just focus it and do nothing
|
||||
|
||||
|
||||
// const inset = vscode.window.createWebviewTextEditorInset(editor, line, height);
|
||||
|
||||
|
||||
// const newCtrlKId = this._idPool++
|
||||
// updateWebviewHTML(inset.webview, this._extensionUri, { jsOutLocation: 'dist/webviews/ctrlk/index.js', cssOutLocation: 'dist/webviews/styles.css' },
|
||||
// { id: newCtrlKId }
|
||||
// )
|
||||
|
||||
// ctrlKWebviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+k', selection: { selectionStr, selectionRange, filePath } } satisfies MessageToSidebar));
|
||||
|
||||
|
||||
}
|
||||
|
||||
onDisposeCtrlK() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// renders the code from `src/sidebar`
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { updateWebviewHTML as _updateWebviewHTML } from '../extensionLib/updateWebviewHTML';
|
||||
|
||||
export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||
public static readonly viewId = 'void.viewnumberone';
|
||||
|
||||
public webview: Promise<vscode.Webview> // used to send messages to the webview, resolved by _res in resolveWebviewView
|
||||
private _res: (c: vscode.Webview) => void // used to resolve the webview
|
||||
|
||||
private readonly _extensionUri: vscode.Uri
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
// const extensionPath = context.extensionPath // the directory where the extension is installed, might be useful later... was included in webviewProvider code
|
||||
this._extensionUri = context.extensionUri
|
||||
|
||||
let temp_res: typeof this._res | undefined = undefined
|
||||
this.webview = new Promise((res, rej) => { temp_res = res })
|
||||
if (!temp_res) throw new Error("Void sidebar provider: resolver was undefined")
|
||||
this._res = temp_res
|
||||
}
|
||||
|
||||
// called internally by vscode
|
||||
resolveWebviewView(webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, token: vscode.CancellationToken,) {
|
||||
const webview = webviewView.webview;
|
||||
_updateWebviewHTML(webview, this._extensionUri, { jsOutLocation: 'dist/webviews/sidebar/index.js', cssOutLocation: 'dist/webviews/styles.css' })
|
||||
this._res(webview); // resolve webview and _webviewView
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import * as React from "react"
|
||||
import { useEffect } from "react"
|
||||
import * as ReactDOM from "react-dom/client"
|
||||
import Sidebar from "./Sidebar"
|
||||
import { CtrlK } from "./CtrlK"
|
||||
import { ThreadsProvider } from "./contextForThreads"
|
||||
import { ConfigProvider } from "./contextForConfig"
|
||||
import { MessageToSidebar } from "../common/shared_types"
|
||||
import { awaitVSCodeResponse, getVSCodeAPI, onMessageFromVSCode } from "./getVscodeApi"
|
||||
import { identifyUser, initPosthog } from "./metrics/posthog"
|
||||
|
||||
if (typeof document === "undefined") {
|
||||
console.log("index.tsx error: document was undefined")
|
||||
}
|
||||
|
||||
|
||||
const CommonEffects = () => {
|
||||
// initialize posthog
|
||||
useEffect(() => {
|
||||
initPosthog()
|
||||
}, [])
|
||||
|
||||
// when we get the deviceid, identify the user
|
||||
useEffect(() => {
|
||||
getVSCodeAPI().postMessage({ type: 'getDeviceId' });
|
||||
awaitVSCodeResponse('deviceId').then((m => {
|
||||
identifyUser(m.deviceId)
|
||||
}))
|
||||
}, [])
|
||||
|
||||
// Receive messages from the VSCode extension
|
||||
useEffect(() => {
|
||||
const listener = (event: MessageEvent) => {
|
||||
const m = event.data as MessageToSidebar;
|
||||
onMessageFromVSCode(m)
|
||||
}
|
||||
window.addEventListener('message', listener);
|
||||
return () => window.removeEventListener('message', listener)
|
||||
}, [])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
(() => {
|
||||
// mount the sidebar on the id="root" element
|
||||
const rootElement = document.getElementById("root")!
|
||||
console.log("Void root Element:", rootElement)
|
||||
|
||||
const sidebar = (<>
|
||||
<CommonEffects />
|
||||
|
||||
<ThreadsProvider>
|
||||
<ConfigProvider>
|
||||
<Sidebar />
|
||||
</ConfigProvider>
|
||||
</ThreadsProvider>
|
||||
|
||||
<ConfigProvider>
|
||||
<CtrlK />
|
||||
</ConfigProvider>
|
||||
|
||||
</>)
|
||||
const root = ReactDOM.createRoot(rootElement)
|
||||
root.render(sidebar)
|
||||
})();
|
||||
|
||||
+7
-14
@@ -46,15 +46,8 @@ const voidConfigInfo: Record<
|
||||
configFields,
|
||||
),
|
||||
|
||||
maxTokens: configEnum(
|
||||
"Max number of tokens to output.",
|
||||
'1024',
|
||||
[
|
||||
"1024",
|
||||
"2048",
|
||||
"4096",
|
||||
"8192"
|
||||
] as const,
|
||||
maxTokens: configString(
|
||||
"Max number of tokens to output. Must be a number.", ''
|
||||
),
|
||||
|
||||
},
|
||||
@@ -185,18 +178,18 @@ export type VoidConfig = {
|
||||
|
||||
|
||||
|
||||
export const getVoidConfig = (currentConfig: PartialVoidConfig): VoidConfig => {
|
||||
export const getVoidConfigFromPartial = (partialVoidConfig: PartialVoidConfig): VoidConfig => {
|
||||
const config = {} as PartialVoidConfig
|
||||
for (let field of [...configFields, 'default'] as const) {
|
||||
config[field] = {}
|
||||
for (let prop in voidConfigInfo[field]) {
|
||||
config[field][prop] = currentConfig[field]?.[prop] || voidConfigInfo[field][prop].defaultVal
|
||||
config[field][prop] = partialVoidConfig[field]?.[prop]?.trim() || voidConfigInfo[field][prop].defaultVal
|
||||
}
|
||||
}
|
||||
return config as VoidConfig
|
||||
}
|
||||
|
||||
const defaultVoidConfig: VoidConfig = getVoidConfig({})
|
||||
const defaultVoidConfig: VoidConfig = getVoidConfigFromPartial({})
|
||||
|
||||
// const [stateRef, setState] = useInstantState(initVal)
|
||||
// setState instantly changes the value of stateRef instead of having to wait until the next render
|
||||
@@ -234,7 +227,7 @@ export function ConfigProvider({ children }: { children: ReactNode }) {
|
||||
getVSCodeAPI().postMessage({ type: 'getPartialVoidConfig' })
|
||||
awaitVSCodeResponse('partialVoidConfig').then((m) => {
|
||||
setPartialVoidConfig(m.partialVoidConfig)
|
||||
const newFullConfig = getVoidConfig(m.partialVoidConfig)
|
||||
const newFullConfig = getVoidConfigFromPartial(m.partialVoidConfig)
|
||||
setVoidConfig(newFullConfig)
|
||||
})
|
||||
}, [setPartialVoidConfig])
|
||||
@@ -254,7 +247,7 @@ export function ConfigProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
}
|
||||
setPartialVoidConfig(newPartialConfig)
|
||||
const newFullConfig = getVoidConfig(newPartialConfig)
|
||||
const newFullConfig = getVoidConfigFromPartial(newPartialConfig)
|
||||
setVoidConfig(newFullConfig)
|
||||
getVSCodeAPI().postMessage({ type: 'persistPartialVoidConfig', partialVoidConfig: newPartialConfig })
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import React, { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState, } from "react"
|
||||
|
||||
type PropsType = { [s: string]: any } | null
|
||||
|
||||
type PropsValue = { props: PropsType }
|
||||
|
||||
const PropsContext = createContext<PropsValue>(undefined as unknown as PropsValue)
|
||||
|
||||
// provider for whatever came in data-void-props
|
||||
export function PropsProvider({ children, props }: { children: ReactNode, props: PropsType }) {
|
||||
return (
|
||||
<PropsContext.Provider value={{ props }}>
|
||||
{children}
|
||||
</PropsContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useVoidProps(): PropsValue {
|
||||
const context = useContext<PropsValue>(PropsContext)
|
||||
if (context === undefined) {
|
||||
throw new Error("useVoidProps missing Provider")
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState, } from "react"
|
||||
import { ChatMessage, ChatThreads } from "../common/shared_types"
|
||||
import { ChatMessage, ChatThreads } from "../../common/shared_types"
|
||||
import { awaitVSCodeResponse, getVSCodeAPI } from "./getVscodeApi"
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from "react";
|
||||
import { MessageFromSidebar, MessageToSidebar, } from "../common/shared_types";
|
||||
import { MessageFromSidebar, MessageToSidebar, } from "../../common/shared_types";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import React, { useEffect } from "react";
|
||||
import * as ReactDOM from "react-dom/client"
|
||||
import { MessageToSidebar } from "../../common/shared_types";
|
||||
import { getVSCodeAPI, awaitVSCodeResponse, onMessageFromVSCode } from "./getVscodeApi";
|
||||
import { initPosthog, identifyUser } from "./posthog";
|
||||
import { ThreadsProvider } from "./contextForThreads";
|
||||
import { ConfigProvider } from "./contextForConfig";
|
||||
import { PropsProvider } from "./contextForProps";
|
||||
|
||||
const ListenersAndTracking = () => {
|
||||
// initialize posthog
|
||||
useEffect(() => {
|
||||
initPosthog()
|
||||
}, [])
|
||||
|
||||
// when we get the deviceid, identify the user
|
||||
useEffect(() => {
|
||||
getVSCodeAPI().postMessage({ type: 'getDeviceId' });
|
||||
awaitVSCodeResponse('deviceId').then((m => {
|
||||
identifyUser(m.deviceId)
|
||||
}))
|
||||
}, [])
|
||||
|
||||
// Receive messages from the VSCode extension
|
||||
useEffect(() => {
|
||||
const listener = (event: MessageEvent) => {
|
||||
const m = event.data as MessageToSidebar;
|
||||
onMessageFromVSCode(m)
|
||||
}
|
||||
window.addEventListener('message', listener);
|
||||
return () => window.removeEventListener('message', listener)
|
||||
}, [])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export const mount = (children: React.ReactNode) => {
|
||||
|
||||
if (typeof document === "undefined") {
|
||||
console.error("index.tsx error: document was undefined")
|
||||
return
|
||||
}
|
||||
|
||||
// mount the sidebar on the id="root" element
|
||||
const rootElement = document.getElementById("root")!
|
||||
console.log("Void root Element:", rootElement)
|
||||
|
||||
let props = rootElement.getAttribute("data-void-props")
|
||||
let propsObj: object | null = null
|
||||
if (props !== null) {
|
||||
propsObj = JSON.parse(decodeURIComponent(props))
|
||||
}
|
||||
|
||||
const content = (<>
|
||||
<ListenersAndTracking />
|
||||
|
||||
<PropsProvider props={propsObj}>
|
||||
<ThreadsProvider>
|
||||
<ConfigProvider>
|
||||
{children}
|
||||
</ConfigProvider>
|
||||
</ThreadsProvider>
|
||||
</PropsProvider>
|
||||
</>)
|
||||
|
||||
const root = ReactDOM.createRoot(rootElement)
|
||||
root.render(content);
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useOnVSCodeMessage } from './getVscodeApi';
|
||||
import { useOnVSCodeMessage } from '../common/getVscodeApi';
|
||||
|
||||
|
||||
export const CtrlK = () => {
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from "react"
|
||||
import { mount } from "../common/mount"
|
||||
import { CtrlK } from "./CtrlK"
|
||||
|
||||
// this is the entry point that mounts ctrlk
|
||||
mount(<CtrlK />)
|
||||
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
import React, { useState, useEffect, useRef, useCallback, FormEvent } from "react"
|
||||
import { CodeSelection, ChatMessage, MessageToSidebar } from "../common/shared_types"
|
||||
import { awaitVSCodeResponse, getVSCodeAPI, onMessageFromVSCode, useOnVSCodeMessage } from "./getVscodeApi"
|
||||
import { CodeSelection, ChatMessage, MessageToSidebar } from "../../common/shared_types"
|
||||
import { awaitVSCodeResponse, getVSCodeAPI, onMessageFromVSCode, useOnVSCodeMessage } from "../common/getVscodeApi"
|
||||
|
||||
import { SidebarThreadSelector } from "./SidebarThreadSelector";
|
||||
import { SidebarChat } from "./SidebarChat";
|
||||
import { SidebarSettings } from './SidebarSettings';
|
||||
import { identifyUser } from "./metrics/posthog";
|
||||
import { SidebarSettings } from "./SidebarSettings";
|
||||
import { identifyUser } from "../common/posthog";
|
||||
|
||||
|
||||
const Sidebar = () => {
|
||||
+7
-7
@@ -4,14 +4,14 @@ import React, { FormEvent, useCallback, useEffect, useRef, useState } from "reac
|
||||
import { marked } from 'marked';
|
||||
import MarkdownRender from "./markdown/MarkdownRender";
|
||||
import BlockCode from "./markdown/BlockCode";
|
||||
import { File, ChatMessage, CodeSelection } from "../common/shared_types";
|
||||
import { File, ChatMessage, CodeSelection } from "../../common/shared_types";
|
||||
import * as vscode from 'vscode'
|
||||
import { awaitVSCodeResponse, getVSCodeAPI, onMessageFromVSCode, useOnVSCodeMessage } from "./getVscodeApi";
|
||||
import { useThreads } from "./contextForThreads";
|
||||
import { sendLLMMessage } from "../common/sendLLMMessage";
|
||||
import { useVoidConfig } from "./contextForConfig";
|
||||
import { generateDiffInstructions } from "../common/systemPrompts";
|
||||
import { captureEvent } from "./metrics/posthog";
|
||||
import { awaitVSCodeResponse, getVSCodeAPI, onMessageFromVSCode, useOnVSCodeMessage } from "../common/getVscodeApi";
|
||||
import { useThreads } from "../common/contextForThreads";
|
||||
import { sendLLMMessage } from "../../common/sendLLMMessage";
|
||||
import { useVoidConfig } from "../common/contextForConfig";
|
||||
import { captureEvent } from "../common/posthog";
|
||||
import { generateDiffInstructions } from "../../common/systemPrompts";
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { configFields, useVoidConfig, VoidConfigField } from "./contextForConfig";
|
||||
import { configFields, useVoidConfig, VoidConfigField } from "../common/contextForConfig";
|
||||
|
||||
|
||||
const SettingOfFieldAndParam = ({ field, param }: { field: VoidConfigField, param: string }) => {
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { ThreadsProvider, useThreads } from "./contextForThreads";
|
||||
import { ThreadsProvider, useThreads } from "../common/contextForThreads";
|
||||
|
||||
|
||||
const truncate = (s: string) => {
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from "react"
|
||||
import Sidebar from "./Sidebar"
|
||||
import { mount } from "../common/mount"
|
||||
|
||||
// this is the entry point that mounts the sidebar
|
||||
mount(<Sidebar />)
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import React, { JSX, useCallback, useEffect, useState } from "react"
|
||||
import { marked, MarkedToken, Token, TokensList } from "marked"
|
||||
import BlockCode from "./BlockCode"
|
||||
import { getVSCodeAPI } from "../getVscodeApi"
|
||||
import { getVSCodeAPI } from "../../common/getVscodeApi"
|
||||
|
||||
|
||||
enum CopyButtonState {
|
||||
@@ -1,3 +1,5 @@
|
||||
/* all the styles are shared right now between all webviews */
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// inject user's vscode theme colors: https://code.visualstudio.com/api/extension-guides/webview#theming-webview-content
|
||||
module.exports = {
|
||||
content: ["./src/sidebar/**/*.{html,js,ts,jsx,tsx}"],
|
||||
content: ["./src/webviews/**/*.{html,js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
|
||||
Reference in New Issue
Block a user