Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3166e7ef2 | |||
| 17e7a5b152 | |||
| f100db9175 | |||
| 1ee8980fe1 | |||
| 74650cb867 | |||
| 5aaa1604e1 | |||
| ff717cad16 | |||
| 7a974f2dff | |||
| cbb52dec28 | |||
| d948fc5950 | |||
| ee39ee94b1 | |||
| a7eb54a09b | |||
| 34e3413dca | |||
| 607a61c068 | |||
| 9732f74ba7 | |||
| 9031215001 | |||
| 3fa95be625 | |||
| 1183cd3ad4 | |||
| ac1aba825f | |||
| 867df38364 | |||
| c90f707070 | |||
| 0424e7d0d3 | |||
| 42c50413f5 | |||
| b5ae6d7a96 | |||
| 453f392f01 | |||
| b5a41840a0 | |||
| 24add9286c | |||
| 8b3b118319 | |||
| b739e99a1e | |||
| ffdbb214f9 | |||
| d1adb8b0fc | |||
| 8233c59f2b | |||
| b00d77610d | |||
| 940155ac80 | |||
| ffbfba8d4f | |||
| da425ab0fe | |||
| 1a922133da | |||
| bf062146c9 | |||
| 2f283b503a | |||
| 797c8e7096 |
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import annotations
|
||||
import os, sys, json, datetime, pathlib, textwrap, requests
|
||||
from openai import OpenAI
|
||||
|
||||
REPO = "voideditor/void"
|
||||
CACHE_FILE = pathlib.Path(".github/triage_cache.json")
|
||||
STAMP_FILE = pathlib.Path(".github/last_triage.txt")
|
||||
|
||||
THEMES_MD = textwrap.dedent("""\
|
||||
1. 🔗 LLM Integration & Provider Support
|
||||
2. 🖥 App Build & Platform Compatibility
|
||||
3. 🎯 Prompt, Token, and Cost Management
|
||||
4. 🧩 Editor UX & Interaction Design
|
||||
5. 🤖 Agent & Automation Features
|
||||
6. ⚙️ System Config & Environment Setup
|
||||
7. 🗃 Meta: Feature Comparison, Structure, and Naming
|
||||
""").strip()
|
||||
|
||||
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
||||
headers = {"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}"}
|
||||
|
||||
|
||||
# ───────── helpers ────────────────────────────────────────────────────────
|
||||
def utc_iso_now() -> str:
|
||||
return datetime.datetime.utcnow().replace(microsecond=0, tzinfo=datetime.timezone.utc).isoformat()
|
||||
|
||||
def read_stamp() -> str:
|
||||
return STAMP_FILE.read_text().strip() if STAMP_FILE.exists() else "1970-01-01T00:00:00Z"
|
||||
|
||||
def save_stamp():
|
||||
STAMP_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
STAMP_FILE.write_text(utc_iso_now())
|
||||
|
||||
def load_cache() -> dict[int, str]:
|
||||
return json.loads(CACHE_FILE.read_text()) if CACHE_FILE.exists() else {}
|
||||
|
||||
def save_cache(d: dict[int, str]):
|
||||
CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
CACHE_FILE.write_text(json.dumps(d, indent=2))
|
||||
|
||||
def fetch_open_issues(since_iso: str | None = None) -> list[dict]:
|
||||
issues, page = [], 1
|
||||
while True:
|
||||
url = (
|
||||
f"https://api.github.com/repos/{REPO}/issues"
|
||||
f"?state=open&per_page=100&page={page}"
|
||||
+ (f"&since={since_iso}" if since_iso else "")
|
||||
)
|
||||
chunk = requests.get(url, headers=headers).json()
|
||||
if not chunk or (isinstance(chunk, dict) and chunk.get("message")):
|
||||
break
|
||||
issues.extend(i for i in chunk if "pull_request" not in i)
|
||||
page += 1
|
||||
return issues
|
||||
|
||||
|
||||
# ───────── main ───────────────────────────────────────────────────────────
|
||||
last_stamp = read_stamp()
|
||||
changed = fetch_open_issues(since_iso=last_stamp)
|
||||
|
||||
# Fallback if **nothing** changed AND we have *no* existing output
|
||||
if not changed:
|
||||
cache_exists = CACHE_FILE.exists()
|
||||
wiki_exists = pathlib.Path("wiki/Issue-Categories.md").exists()
|
||||
if not cache_exists or not wiki_exists:
|
||||
# first run or someone wiped the wiki → build from scratch
|
||||
print("⏩ First run or empty wiki — fetching ALL open issues.", file=sys.stderr)
|
||||
changed = fetch_open_issues() # full list
|
||||
else:
|
||||
print(f"✅ No issues updated since {last_stamp}. Nothing to classify.", file=sys.stderr)
|
||||
save_stamp()
|
||||
sys.exit(0)
|
||||
|
||||
# ---------------------------------------------------------------- prompt
|
||||
issue_lines = "\n".join(f"- {i['title']} ({i['html_url']})" for i in changed)
|
||||
prompt = textwrap.dedent(f"""\
|
||||
You are an AI assistant helping triage GitHub issues into exactly 7 predefined themes.
|
||||
|
||||
Each issue must go into exactly one of the themes below:
|
||||
|
||||
{THEMES_MD}
|
||||
|
||||
Format your output in Markdown like:
|
||||
## 🎯 Prompt, Token, and Cost Management
|
||||
- [#123](https://github.com/org/repo/issues/123) – Title here
|
||||
|
||||
Classify these issues:
|
||||
{issue_lines}
|
||||
""")
|
||||
|
||||
resp = client.chat.completions.create(
|
||||
model="gpt-4.1",
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
temperature=0.2,
|
||||
)
|
||||
|
||||
md = resp.choices[0].message.content
|
||||
|
||||
# ---------------------------------------------------------------- parse GPT
|
||||
new_map: dict[int, str] = {}
|
||||
current = None
|
||||
for ln in md.splitlines():
|
||||
if ln.startswith("##"):
|
||||
current = ln.lstrip("# ").strip()
|
||||
elif ln.lstrip().startswith("- [#"):
|
||||
try:
|
||||
num = int(ln.split("[#")[1].split("]")[0])
|
||||
new_map[num] = current
|
||||
except Exception:
|
||||
pass # ignore malformed lines
|
||||
|
||||
cache = load_cache()
|
||||
cache.update(new_map)
|
||||
save_cache(cache)
|
||||
save_stamp()
|
||||
|
||||
# ---------------------------------------------------------------- rebuild wiki
|
||||
order = [
|
||||
"🔗 LLM Integration & Provider Support",
|
||||
"🖥 App Build & Platform Compatibility",
|
||||
"🎯 Prompt, Token, and Cost Management",
|
||||
"🧩 Editor UX & Interaction Design",
|
||||
"🤖 Agent & Automation Features",
|
||||
"⚙️ System Config & Environment Setup",
|
||||
"🗃 Meta: Feature Comparison, Structure, and Naming",
|
||||
]
|
||||
|
||||
sections: dict[str, list[int]] = {t: [] for t in order}
|
||||
|
||||
# ── fetch ALL current open issues once (PRs filtered out) ────────────────
|
||||
title_map: dict[int, tuple[str, str]] = {}
|
||||
open_now: set[int] = set()
|
||||
|
||||
page = 1
|
||||
while True:
|
||||
batch = fetch_open_issues(since_iso=None) if page == 1 else []
|
||||
if not batch:
|
||||
break
|
||||
for it in batch:
|
||||
num = it["number"]
|
||||
title_map[num] = (it["title"], it["html_url"])
|
||||
open_now.add(num)
|
||||
page += 1
|
||||
|
||||
# 🧹 drop any cached IDs that are no longer open issues (e.g., became a PR or were closed)
|
||||
for stale in set(cache) - open_now:
|
||||
del cache[stale]
|
||||
save_cache(cache) # persist cleaned cache
|
||||
|
||||
# build sections from cleaned cache
|
||||
for num, theme in cache.items():
|
||||
if theme in sections: # extra safety
|
||||
sections[theme].append(num)
|
||||
|
||||
# ---------------------------------------------------------------- print roadmap
|
||||
for theme in order:
|
||||
issues = sections[theme]
|
||||
if issues:
|
||||
print(f"## {theme}")
|
||||
for n in sorted(issues):
|
||||
title, url = title_map.get(n, ("(missing)", f"https://github.com/{REPO}/issues/{n}"))
|
||||
print(f"- [#{n}]({url}) – {title}")
|
||||
print()
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Issue Triage to Wiki
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 */6 * * *' # every 6 hrs (UTC)
|
||||
|
||||
jobs:
|
||||
roadmap:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# 1️⃣ Check out code (so the script and cache files are present)
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1 # shallow clone
|
||||
|
||||
# 2️⃣ Set up Python
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
# 3️⃣ Install dependencies
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
pip install openai requests
|
||||
|
||||
# 4️⃣ Clone your fork’s Wiki
|
||||
- name: Clone your fork's Wiki
|
||||
run: |
|
||||
git clone https://x-access-token:${{ secrets.WIKI_TOKEN }}@github.com/${{ github.repository }}.wiki.git wiki
|
||||
|
||||
# 5️⃣ (Optional) Show repo tree for debugging
|
||||
- name: Show repo tree (debug)
|
||||
run: |
|
||||
echo "PWD: $(pwd)"
|
||||
ls -al
|
||||
ls -al .github/scripts || true
|
||||
ls -al void/.github/scripts || true
|
||||
|
||||
# 6️⃣ Generate roadmap and push only if it changed
|
||||
- name: Generate roadmap directly into wiki
|
||||
run: |
|
||||
python .github/scripts/issue_triage.py > wiki/_new.md
|
||||
if ! cmp -s wiki/_new.md wiki/Issue-Categories.md ; then
|
||||
mv wiki/_new.md wiki/Issue-Categories.md
|
||||
cd wiki
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add Issue-Categories.md
|
||||
git commit -m "Auto-update Issue-Categories.md from GPT triage"
|
||||
git push
|
||||
else
|
||||
echo "No content change – skipping wiki update"
|
||||
fi
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.WIKI_TOKEN }}
|
||||
+49
-62
@@ -20,14 +20,15 @@ Most of Void's code lives in the folder `src/vs/workbench/contrib/void/`.
|
||||
|
||||
|
||||
|
||||
## Editing Void's Code
|
||||
|
||||
## Building Void
|
||||
If you're making changes to Void's code as a contributor, you'll want to run a local version of Void to make sure your changes worked. Developer mode lets you do this. Here's how to use it.
|
||||
|
||||
### a. Mac - Build Prerequisites
|
||||
### a. Mac - Prerequisites
|
||||
|
||||
If you're using a Mac, you need Python and XCode. You probably have these by default.
|
||||
|
||||
### b. Windows - Build Prerequisites
|
||||
### b. Windows - Prerequisites
|
||||
|
||||
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.
|
||||
|
||||
@@ -42,34 +43,52 @@ Go to the "Individual Components" tab and select:
|
||||
|
||||
Finally, click Install.
|
||||
|
||||
### c. Linux - Build Prerequisites
|
||||
### c. Linux - Prerequisites
|
||||
|
||||
First, run `npm install -g node-gyp`. Then:
|
||||
|
||||
- Debian (Ubuntu, etc): `sudo apt-get install build-essential g++ libx11-dev libxkbfile-dev libsecret-1-dev libkrb5-dev python-is-python3`.
|
||||
- Red Hat (Fedora, etc): `sudo dnf install @development-tools gcc gcc-c++ make libsecret-devel krb5-devel libX11-devel libxkbfile-devel`.
|
||||
- SUSE (openSUSE, etc): `sudo zypper install patterns-devel-C-C++-devel_C_C++ krb5-devel libsecret-devel libxkbfile-devel libX11-devel`.
|
||||
- Others: see [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute).
|
||||
|
||||
### d. Building Void from inside VSCode
|
||||
### Developer Mode Instructions
|
||||
|
||||
Here's how to start changing Void's code. These steps cover everything from cloning Void, to opening a Developer Mode window where you can play around with your updates.
|
||||
|
||||
1. `git clone https://github.com/voideditor/void` to clone the repo.
|
||||
2. `npm install` to install all dependencies.
|
||||
3. To build Void, open VSCode. Then:
|
||||
3. Open Void or VSCode, and initialize Developer Mode (this can take ~5 min to finish, it's done when 2 of the 3 spinners turn to check marks):
|
||||
- Windows: Press <kbd>Ctrl+Shift+B</kbd>.
|
||||
- Mac: Press <kbd>Cmd+Shift+B</kbd>.
|
||||
- Linux: Press <kbd>Ctrl+Shift+B</kbd>.
|
||||
- This step can take ~5 min. The build is done when you see two check marks (one of the items will continue spinning indefinitely - it compiles our React code).
|
||||
4. To run Void:
|
||||
4. Open the Void Developer Mode window:
|
||||
- Windows: `./scripts/code.bat`.
|
||||
- Mac: `./scripts/code.sh`.
|
||||
- Linux: `./scripts/code.sh`.
|
||||
5. Nice-to-knows.
|
||||
- You can always press <kbd>Ctrl+R</kbd> (<kbd>Cmd+R</kbd>) inside the new window to reload and see your new changes. It's faster than <kbd>Ctrl+Shift+P</kbd> and `Reload Window`.
|
||||
- You might want to add the flags `--user-data-dir ./.tmp/user-data --extensions-dir ./.tmp/extensions` to the above run command, which lets you delete the `.tmp` folder to reset any IDE changes you made when testing.
|
||||
- You can kill any of the build scripts by pressing `Ctrl+D` in VSCode terminal. If you press `Ctrl+C` the script will close but will keep running in the background (to open all background scripts, just re-build).
|
||||
5. You're good to start editing Void's code!
|
||||
- You won't see your changes unless you press <kbd>Ctrl+R</kbd> (<kbd>Cmd+R</kbd>) inside the new window to reload. Alternatively, press <kbd>Ctrl+Shift+P</kbd> and `Reload Window`.
|
||||
- You might want to add the flags `--user-data-dir ./.tmp/user-data --extensions-dir ./.tmp/extensions` to the command in step 4, which lets you reset any IDE changes you made by deleting the `.tmp` folder.
|
||||
- You can kill any of the build scripts by pressing `Ctrl+D` in its terminal. If you press `Ctrl+C` the script will close but will keep running in the background.
|
||||
|
||||
If you get any errors, scroll down for common fixes.
|
||||
|
||||
#### Common Fixes
|
||||
|
||||
- Make sure you followed the prerequisite steps above.
|
||||
- Make sure you have Node version `20.18.2` (the version in `.nvmrc`).
|
||||
- You can do this without changing your global Node version using [nvm](https://github.com/nvm-sh/nvm): run `nvm install`, followed by `nvm use` to install the version in `.nvmrc` locally.
|
||||
- Make sure the path to your Void folder does not have any spaces in it.
|
||||
- If you get `"TypeError: Failed to fetch dynamically imported module"`, make sure all imports end with `.js`.
|
||||
- If you get an error with React, try running `NODE_OPTIONS="--max-old-space-size=8192" npm run buildreact`.
|
||||
- If you see missing styles, wait a few seconds and then reload.
|
||||
- If you get errors like `npm error libtool: error: unrecognised option: '-static'`, when running ./scripts/code.sh, make sure you have GNU libtool instead of BSD libtool (BSD is the default in macos)
|
||||
- If you get errors like `The SUID sandbox helper binary was found, but is not configured correctly` when running ./scripts/code.sh, run
|
||||
`sudo chown root:root .build/electron/chrome-sandbox && sudo chmod 4755 .build/electron/chrome-sandbox` and then run `./scripts/code.sh` again.
|
||||
- If you have any other questions, feel free to [submit an issue](https://github.com/voideditor/void/issues/new). You can also refer to VSCode's complete [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) page.
|
||||
|
||||
|
||||
|
||||
#### Building Void from Terminal
|
||||
|
||||
To build Void from the terminal instead of from inside VSCode, follow the steps above, but instead of pressing <kbd>Cmd+Shift+B</kbd>, run `npm run watch`. The build is done when you see something like this:
|
||||
@@ -82,50 +101,47 @@ To build Void from the terminal instead of from inside VSCode, follow the steps
|
||||
```
|
||||
|
||||
|
||||
#### Common Fixes
|
||||
|
||||
- Make sure you followed the prerequisite steps above.
|
||||
- Make sure you have Node version `20.18.2` (the version in `.nvmrc`)!
|
||||
- You can do this easily without touching your base installation with [nvm](https://github.com/nvm-sh/nvm). Simply run `nvm install`, followed by `nvm use` and it will automatically install and use the version specified in `nvmrc`.
|
||||
- Make sure that the path to your Void folder does not have any spaces in it.
|
||||
- If you get `"TypeError: Failed to fetch dynamically imported module"`, make sure all imports end with `.js`.
|
||||
- If you get an error with React, try running `NODE_OPTIONS="--max-old-space-size=8192" npm run buildreact`.
|
||||
- If you see missing styles, wait a few seconds and then reload.
|
||||
- If you get errors like `npm error libtool: error: unrecognised option: '-static'`, when running ./scripts/code.sh, make sure you have GNU libtool instead of BSD libtool (BSD is the default in macos)
|
||||
- If you get erorrs like `The SUID sandbox helper binary was found, but is not configured correctly` when running ./scripts/code.sh, run
|
||||
`sudo chown root:root .build/electron/chrome-sandbox && sudo chmod 4755 .build/electron/chrome-sandbox` and then run `./scripts/code.sh` again.
|
||||
- If you have any other questions, feel free to [submit an issue](https://github.com/voideditor/void/issues/new). You can also refer to VSCode's complete [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) page.
|
||||
### Distributing
|
||||
Void's maintainers distribute Void on our website and in releases. Our build pipeline is a fork of VSCodium, and it works by running GitHub Actions which create the downloadables. The build repo with more instructions lives [here](https://github.com/voideditor/void-builder).
|
||||
|
||||
If you want to completely control Void's build pipeline for your own internal usage, which comes with a lot of time cost (and is typically not recommended), see our [`void-builder`](https://github.com/voideditor/void-builder) repo which builds Void and contains a few important notes about auto-updating and rebasing.
|
||||
|
||||
|
||||
## Packaging
|
||||
#### Building a Local Executible
|
||||
We don't usually recommend building a local executible of Void - typically you should follow the steps above to distribute a complete executible with the advantages of VSCodium baked-in, or you should just use Developer Mode to run Void locally which is much faster. If you're certain this is what you want, see details below.
|
||||
|
||||
We don't usually recommend packaging. Instead, you should probably just build. If you're sure you want to package Void into an executable app, make sure you've built first, then run one of the following commands. This will create a folder named `VSCode-darwin-arm64` or similar outside of the void/ repo (see below). Be patient - packaging can take ~25 minutes.
|
||||
<details>
|
||||
<summary> Building Locally (not recommended)</summary>
|
||||
If you're certain you want to build a local executible of Void, follow these steps. It can take ~25 minutes.
|
||||
|
||||
Make sure you've already entered Developer Mode with Void first, then run one of the following commands. This will create a folder named `VSCode-darwin-arm64` or similar outside of the void/ repo (see below).
|
||||
|
||||
|
||||
### Mac
|
||||
##### Mac
|
||||
- `npm run gulp vscode-darwin-arm64` - most common (Apple Silicon)
|
||||
- `npm run gulp vscode-darwin-x64` (Intel)
|
||||
|
||||
### Windows
|
||||
##### Windows
|
||||
- `npm run gulp vscode-win32-x64` - most common
|
||||
- `npm run gulp vscode-win32-arm64`
|
||||
|
||||
### Linux
|
||||
##### Linux
|
||||
- `npm run gulp vscode-linux-x64` - most common
|
||||
- `npm run gulp vscode-linux-arm64`
|
||||
|
||||
|
||||
### Output
|
||||
##### Local Executible Output
|
||||
|
||||
This will generate a folder outside of `void/`:
|
||||
The local executible will be located in a folder outside of `void/`:
|
||||
```bash
|
||||
workspace/
|
||||
├── void/ # Your Void fork
|
||||
└── VSCode-darwin-arm64/ # Generated output
|
||||
```
|
||||
|
||||
### Distributing
|
||||
Void's maintainers distribute Void on our website and in releases. Our build pipeline is a fork of VSCodium, and it works by running GitHub Actions which create the downloadables. The build repo with more instructions lives [here](https://github.com/voideditor/void-builder).
|
||||
</details>
|
||||
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
@@ -137,32 +153,3 @@ Void's maintainers distribute Void on our website and in releases. Our build pip
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
# Relevant files
|
||||
|
||||
We keep track of all the files we've changed with Void so it's easy to rebase:
|
||||
|
||||
Edit: far too many changes to track... this is old
|
||||
|
||||
- README.md
|
||||
- CONTRIBUTING.md
|
||||
- VOID_USEFUL_LINKS.md
|
||||
- product.json
|
||||
- package.json
|
||||
|
||||
- src/vs/workbench/api/common/{extHost.api.impl.ts | extHostApiCommands.ts}
|
||||
- src/vs/workbench/workbench.common.main.ts
|
||||
- src/vs/workbench/contrib/void/\*
|
||||
- extensions/void/\*
|
||||
|
||||
- .github/\*
|
||||
- .vscode/settings/\*
|
||||
- .eslintrc.json
|
||||
- build/hygiene.js
|
||||
- build/lib/i18n.resources.json
|
||||
- build/npm/dirs.js
|
||||
|
||||
- vscode.proposed.editorInsets.d.ts - not modified, but code copied
|
||||
|
||||
-->
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
## Void is now deprecated.
|
||||
Void is deprecated and no longer accepting contributions.
|
||||
Thank you to everyone who contributed, both with lines of code and support from the community. Void remains open source and is still one of the best references to use when forking VS Code.
|
||||
|
||||
|
||||
## Download
|
||||
|
||||
To view a list of newer Void forks, see [Void Forks](http://github.com/voideditor/void-forks/).
|
||||
|
||||
To download an old version of Void, see [Releases](https://github.com/voideditor/void/releases).
|
||||
|
||||
## Forking VS Code
|
||||
|
||||
If you're forking VS Code, you might still want to reference Void's logic, and see our [Codebase Guide](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md) and [How to Contribute](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md).
|
||||
|
||||
- We mount React + Tailwind. This is not possible in plain VS Code, and required extending the build pipeline to compile React and [scope](https://github.com/andrewpareles/scope-tailwind) Tailwind ourselves.
|
||||
|
||||
- You can copy our GitHub Actions to package, sign, and auto-update Void. VS Code's build pipeline is private, so this is normally very hard.
|
||||
|
||||
- Our AI provider code is built from scratch, allowing us to support autocomplete (FIM) and other custom responses. We expose grammars for common `<thinking>` tags, tool tags, etc. Feel free to reference our architecture for using IPC and satisfying CSP.
|
||||
|
||||
- Use our custom services to edit files. EditCodeService lets you show diffs as code streams in, even token by token. VoidModelService lets you edit files in the background and syncs OS files with your text buffers.
|
||||
|
||||
- Everything we've done is 100% open source. See [repos](https://github.com/orgs/voideditor/repositories) for a complete picture of all the repos that make up Void.
|
||||
|
||||
|
||||
|
||||
# Welcome to Void.
|
||||
|
||||
<div align="center">
|
||||
@@ -9,29 +36,26 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
Void is the open-source Cursor alternative.
|
||||
|
||||
Use AI agents on your codebase, checkpoint and visualize changes, and bring any model or host locally. Void sends messages directly to providers without retaining your data.
|
||||
|
||||
This repo contains the full sourcecode for Void. If you're new, welcome!
|
||||
This repo contains the full sourcecode for Void's Desktop app. If you're new, welcome!
|
||||
|
||||
- 🧭 [Website](https://voideditor.com)
|
||||
|
||||
- 👋 [Discord](https://discord.gg/RSNjgaugJs)
|
||||
- 🚙 [Roadmap](https://github.com/orgs/voideditor/projects/2)
|
||||
|
||||
- 🚙 [Project Board](https://github.com/orgs/voideditor/projects/2)
|
||||
- 🔨 [Contribute](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md)
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
1. To get started working on Void, check out our Project Board! You can also see [HOW_TO_CONTRIBUTE](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md).
|
||||
|
||||
2. Feel free to attend a casual weekly meeting in our Discord channel!
|
||||
|
||||
|
||||
## Reference
|
||||
|
||||
Void is a fork of the [vscode](https://github.com/microsoft/vscode) repository. For a guide to the codebase, see [VOID_CODEBASE_GUIDE](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md).
|
||||
Void is a fork of the [vscode](https://github.com/microsoft/vscode) repository. For a guide to our codebase, see [VOID_CODEBASE_GUIDE](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md).
|
||||
|
||||
For a guide on how to develop your own version of Void, see [HOW_TO_CONTRIBUTE](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md) and [void-builder](https://github.com/voideditor/void-builder).
|
||||
|
||||
|
||||
|
||||
## Support
|
||||
You can always reach us in our Discord server or contact us via email: hello@voideditor.com.
|
||||
You can always reach us in our [Discord server](https://discord.gg/RSNjgaugJs) or contact us via email at hello@voideditor.com.
|
||||
|
||||
@@ -52,7 +52,10 @@ Here's some terminology you might want to know about when working inside VSCode:
|
||||
- You can run actions as a user by pressing Cmd+Shift+P (opens the command pallete), or you can run them internally by using the commandService to call them by ID. We use actions to register keybinding listeners like Cmd+L, Cmd+K, etc. The nice thing about actions is the user can change the keybindings.
|
||||
|
||||
|
||||
### Internal LLM Message Pipeline
|
||||
|
||||
|
||||
|
||||
### Void's LLM Message Pipeline
|
||||
|
||||
Here's a picture of all the dependencies that are relevent between the time you first send a message through Void's sidebar, and the time a request is sent to your provider.
|
||||
Sending LLM messages from the main process avoids CSP issues with local providers and lets us use node_modules more easily.
|
||||
@@ -134,7 +137,7 @@ If you want to know how our build pipeline works, see our build repo [here](http
|
||||
|
||||
For additional references, the Void team put together this list of links to get up and running with VSCode.
|
||||
<details>
|
||||
|
||||
|
||||
|
||||
#### Links for Beginners
|
||||
|
||||
|
||||
@@ -664,81 +664,89 @@ export const SelectedFiles = (
|
||||
key={thisKey}
|
||||
className={`flex flex-col space-y-[1px]`}
|
||||
>
|
||||
{/* summarybox */}
|
||||
<div
|
||||
className={`
|
||||
flex items-center gap-1 relative
|
||||
px-1
|
||||
w-fit h-fit
|
||||
select-none
|
||||
text-xs text-nowrap
|
||||
border rounded-sm
|
||||
${isThisSelectionProspective ? 'bg-void-bg-1 text-void-fg-3 opacity-80' : 'bg-void-bg-1 hover:brightness-95 text-void-fg-1'}
|
||||
${isThisSelectionProspective
|
||||
? 'border-void-border-2'
|
||||
: 'border-void-border-1'
|
||||
}
|
||||
hover:border-void-border-1
|
||||
transition-all duration-150
|
||||
`}
|
||||
onClick={() => {
|
||||
if (type !== 'staging') return; // (never)
|
||||
if (isThisSelectionProspective) { // add prospective selection to selections
|
||||
setSelections([...selections, selection])
|
||||
}
|
||||
else if (selection.type === 'File') { // open files
|
||||
voidOpenFileFn(selection.uri, accessor);
|
||||
|
||||
const wasAddedAsCurrentFile = selection.state.wasAddedAsCurrentFile
|
||||
if (wasAddedAsCurrentFile) {
|
||||
// make it so the file is added permanently, not just as the current file
|
||||
const newSelection: StagingSelectionItem = { ...selection, state: { ...selection.state, wasAddedAsCurrentFile: false } }
|
||||
setSelections([
|
||||
...selections.slice(0, i),
|
||||
newSelection,
|
||||
...selections.slice(i + 1)
|
||||
])
|
||||
}
|
||||
}
|
||||
else if (selection.type === 'CodeSelection') {
|
||||
voidOpenFileFn(selection.uri, accessor, selection.range);
|
||||
}
|
||||
else if (selection.type === 'Folder') {
|
||||
// TODO!!! reveal in tree
|
||||
}
|
||||
}}
|
||||
{/* tooltip for file path */}
|
||||
<span className="truncate overflow-hidden text-ellipsis"
|
||||
data-tooltip-id='void-tooltip'
|
||||
data-tooltip-content={getRelative(selection.uri, accessor)}
|
||||
data-tooltip-place='top'
|
||||
data-tooltip-delay-show={3000}
|
||||
>
|
||||
{<SelectionIcon size={10} />}
|
||||
{/* summarybox */}
|
||||
<div
|
||||
className={`
|
||||
flex items-center gap-1 relative
|
||||
px-1
|
||||
w-fit h-fit
|
||||
select-none
|
||||
text-xs text-nowrap
|
||||
border rounded-sm
|
||||
${isThisSelectionProspective ? 'bg-void-bg-1 text-void-fg-3 opacity-80' : 'bg-void-bg-1 hover:brightness-95 text-void-fg-1'}
|
||||
${isThisSelectionProspective
|
||||
? 'border-void-border-2'
|
||||
: 'border-void-border-1'
|
||||
}
|
||||
hover:border-void-border-1
|
||||
transition-all duration-150
|
||||
`}
|
||||
onClick={() => {
|
||||
if (type !== 'staging') return; // (never)
|
||||
if (isThisSelectionProspective) { // add prospective selection to selections
|
||||
setSelections([...selections, selection])
|
||||
}
|
||||
else if (selection.type === 'File') { // open files
|
||||
voidOpenFileFn(selection.uri, accessor);
|
||||
|
||||
{ // file name and range
|
||||
getBasename(selection.uri.fsPath)
|
||||
+ (selection.type === 'CodeSelection' ? ` (${selection.range[0]}-${selection.range[1]})` : '')
|
||||
}
|
||||
const wasAddedAsCurrentFile = selection.state.wasAddedAsCurrentFile
|
||||
if (wasAddedAsCurrentFile) {
|
||||
// make it so the file is added permanently, not just as the current file
|
||||
const newSelection: StagingSelectionItem = { ...selection, state: { ...selection.state, wasAddedAsCurrentFile: false } }
|
||||
setSelections([
|
||||
...selections.slice(0, i),
|
||||
newSelection,
|
||||
...selections.slice(i + 1)
|
||||
])
|
||||
}
|
||||
}
|
||||
else if (selection.type === 'CodeSelection') {
|
||||
voidOpenFileFn(selection.uri, accessor, selection.range);
|
||||
}
|
||||
else if (selection.type === 'Folder') {
|
||||
// TODO!!! reveal in tree
|
||||
}
|
||||
}}
|
||||
>
|
||||
{<SelectionIcon size={10} />}
|
||||
|
||||
{selection.type === 'File' && selection.state.wasAddedAsCurrentFile && messageIdx === undefined && currentURI?.fsPath === selection.uri.fsPath ?
|
||||
<span className={`text-[8px] 'void-opacity-60 text-void-fg-4`}>
|
||||
{`(Current File)`}
|
||||
</span>
|
||||
: null
|
||||
}
|
||||
{ // file name and range
|
||||
getBasename(selection.uri.fsPath)
|
||||
+ (selection.type === 'CodeSelection' ? ` (${selection.range[0]}-${selection.range[1]})` : '')
|
||||
}
|
||||
|
||||
{type === 'staging' && !isThisSelectionProspective ? // X button
|
||||
<div // box for making it easier to click
|
||||
className='cursor-pointer z-1 self-stretch flex items-center justify-center'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(); // don't open/close selection
|
||||
if (type !== 'staging') return;
|
||||
setSelections([...selections.slice(0, i), ...selections.slice(i + 1)])
|
||||
}}
|
||||
>
|
||||
<IconX
|
||||
className='stroke-[2]'
|
||||
size={10}
|
||||
/>
|
||||
</div>
|
||||
: <></>
|
||||
}
|
||||
</div>
|
||||
{selection.type === 'File' && selection.state.wasAddedAsCurrentFile && messageIdx === undefined && currentURI?.fsPath === selection.uri.fsPath ?
|
||||
<span className={`text-[8px] 'void-opacity-60 text-void-fg-4`}>
|
||||
{`(Current File)`}
|
||||
</span>
|
||||
: null
|
||||
}
|
||||
|
||||
{type === 'staging' && !isThisSelectionProspective ? // X button
|
||||
<div // box for making it easier to click
|
||||
className='cursor-pointer z-1 self-stretch flex items-center justify-center'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(); // don't open/close selection
|
||||
if (type !== 'staging') return;
|
||||
setSelections([...selections.slice(0, i), ...selections.slice(i + 1)])
|
||||
}}
|
||||
>
|
||||
<IconX
|
||||
className='stroke-[2]'
|
||||
size={10}
|
||||
/>
|
||||
</div>
|
||||
: <></>
|
||||
}
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
})}
|
||||
@@ -1121,7 +1129,7 @@ const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, curr
|
||||
if (e.key === 'Escape') {
|
||||
onCloseEdit()
|
||||
}
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
|
||||
onSubmit()
|
||||
}
|
||||
}
|
||||
@@ -3048,7 +3056,7 @@ export const SidebarChat = () => {
|
||||
setInstructionsAreEmpty(!newStr)
|
||||
}, [setInstructionsAreEmpty])
|
||||
const onKeyDown = useCallback((e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
|
||||
onSubmit()
|
||||
} else if (e.key === 'Escape' && isRunning) {
|
||||
onAbort()
|
||||
|
||||
@@ -985,7 +985,24 @@ const mistralModelOptions = { // https://mistral.ai/products/la-plateforme#prici
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
|
||||
'magistral-medium-latest': {
|
||||
contextWindow: 256_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0.30, output: 0.90 }, // TODO: check this
|
||||
supportsFIM: true,
|
||||
downloadable: { sizeGb: 13 },
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: { supportsReasoning: true, canIOReasoning: true, canTurnOffReasoning: false, openSourceThinkTags: ['<think>', '</think>'] },
|
||||
},
|
||||
'magistral-small-latest': {
|
||||
contextWindow: 40_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0.30, output: 0.90 }, // TODO: check this
|
||||
supportsFIM: true,
|
||||
downloadable: { sizeGb: 13 },
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: { supportsReasoning: true, canIOReasoning: true, canTurnOffReasoning: false, openSourceThinkTags: ['<think>', '</think>'] },
|
||||
},
|
||||
'devstral-small-latest': { //https://openrouter.ai/mistralai/devstral-small:free
|
||||
contextWindow: 131_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
@@ -995,7 +1012,6 @@ const mistralModelOptions = { // https://mistral.ai/products/la-plateforme#prici
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
|
||||
'ministral-8b-latest': { // ollama 'mistral'
|
||||
contextWindow: 131_000,
|
||||
reservedOutputTokenSpace: 4_096,
|
||||
|
||||
@@ -575,11 +575,24 @@ export const messageOfSelection = async (
|
||||
) => {
|
||||
const lineNumAddition = (range: [number, number]) => ` (lines ${range[0]}:${range[1]})`
|
||||
|
||||
if (s.type === 'File' || s.type === 'CodeSelection') {
|
||||
if (s.type === 'CodeSelection') {
|
||||
const { val } = await readFile(opts.fileService, s.uri, DEFAULT_FILE_SIZE_LIMIT)
|
||||
const lineNumAdd = s.type === 'CodeSelection' ? lineNumAddition(s.range) : ''
|
||||
const content = val === null ? 'null' : `${tripleTick[0]}${s.language}\n${val}\n${tripleTick[1]}`
|
||||
const str = `${s.uri.fsPath}${lineNumAdd}:\n${content}`
|
||||
const lines = val?.split('\n')
|
||||
|
||||
const innerVal = lines?.slice(s.range[0] - 1, s.range[1]).join('\n')
|
||||
const content = !lines ? ''
|
||||
: `${tripleTick[0]}${s.language}\n${innerVal}\n${tripleTick[1]}`
|
||||
const str = `${s.uri.fsPath}${lineNumAddition(s.range)}:\n${content}`
|
||||
return str
|
||||
}
|
||||
else if (s.type === 'File') {
|
||||
const { val } = await readFile(opts.fileService, s.uri, DEFAULT_FILE_SIZE_LIMIT)
|
||||
|
||||
const innerVal = val
|
||||
const content = val === null ? ''
|
||||
: `${tripleTick[0]}${s.language}\n${innerVal}\n${tripleTick[1]}`
|
||||
|
||||
const str = `${s.uri.fsPath}:\n${content}`
|
||||
return str
|
||||
}
|
||||
else if (s.type === 'Folder') {
|
||||
|
||||
Reference in New Issue
Block a user