Compare commits

...

68 Commits

Author SHA1 Message Date
Andrew Pareles 7925e438c6 header update 2025-01-09 18:51:40 -08:00
Andrew Pareles 2f5e954ebf update 2025-01-09 18:49:05 -08:00
Andrew Pareles 6a8ac1a68e apache 2.0 2025-01-09 18:48:52 -08:00
Andrew Pareles c5b6fd9b75 Create temp 2025-01-09 18:48:15 -08:00
Raz Luvaton 5083b8e971 fix react build.js to properly find the base folder (#203) 2025-01-05 18:00:40 -08:00
Andrew Pareles 85c2c80db0 Update README.md 2025-01-04 23:55:52 -08:00
Andrew Pareles b677d3dfc1 Update CONTRIBUTING.md 2025-01-03 17:18:18 -08:00
Andrew Pareles cc177b9cd4 update package 2025-01-03 14:14:37 -08:00
Andrew Pareles dd204978e5 update lock 2025-01-03 13:31:54 -08:00
Andrew Pareles fb48c4e67e Update VOID_USEFUL_LINKS.md 2025-01-03 13:15:55 -08:00
Andrew Pareles 74fa5cb505 Merge pull request #202 from PraveenAsh/patch-1
Update CONTRIBUTING.md
2025-01-03 12:44:33 -08:00
Andrew Pareles fae03c3060 Update CONTRIBUTING.md 2025-01-03 12:44:12 -08:00
Praveen Naik 68722ee5c3 Update CONTRIBUTING.md
Added details about Bundling output. Most of us open only the repo without workspace to save memory. It's easy to miss out on where the bundle is located. Added clarity
2025-01-03 21:04:54 +05:30
Andrew Pareles 4a993a716b Update README.md 2025-01-02 23:34:37 -08:00
Andrew Pareles 23d1d4169a update CONTRIBUTING.md 2025-01-02 23:20:36 -08:00
Andrew Pareles f688e1ffae Update LICENSE-VS-Code.txt 2025-01-02 23:06:06 -08:00
Andrew Pareles a443315c14 Merge branch 'main' of https://github.com/voideditor/void 2025-01-02 23:02:05 -08:00
Andrew Pareles eeacb2d3c9 separate licenses + header 2025-01-02 23:00:59 -08:00
Andrew Pareles cdbdf3c5c7 License update (#199) 2025-01-02 22:58:05 -08:00
Andrew Pareles 825c40bfff license 2025-01-02 22:40:43 -08:00
Andrew Pareles a3d3309b9d Merge pull request #197 from voideditor/model-selection
Fix line indexing for CtrlK
2025-01-02 18:59:08 -08:00
Andrew Pareles 32bfe9a111 fix line indexing when streaming 2025-01-02 18:58:44 -08:00
Andrew Pareles a9adcad560 misc 2025-01-02 17:57:38 -08:00
Mathew Pareles 80f7511eee default settings instructions 2025-01-02 17:34:31 -08:00
Andrew Pareles 7fe8f5c47d ctrlK line numbers are right 2025-01-02 17:26:28 -08:00
Mathew Pareles b31882565b fix watchreact 2025-01-02 17:11:32 -08:00
Andrew Pareles 6aaf425419 Settings update + ctrlK stream runs (but is broken) 2025-01-02 17:10:38 -08:00
Andrew Pareles b0d21dfdb1 Merge pull request #196 from voideditor/model-selection
misc necessary updates
2025-01-02 16:41:33 -08:00
Andrew Pareles 2087948789 misc necessary updates 2025-01-02 16:40:55 -08:00
Andrew Pareles 6feb713682 Merge pull request #192 from jcommaret/main
Fix - bundle - ajv missing deps
2025-01-02 16:31:45 -08:00
Andrew Pareles 667dc8bd7e merge 2025-01-02 16:31:29 -08:00
Andrew Pareles bdb0c65346 Merge pull request #195 from voideditor/model-selection
Merge model selection branch (ctrlK, model selection, UI)
2025-01-02 16:29:10 -08:00
Andrew Pareles df14b80d8b not sure where this came from 2025-01-02 16:28:33 -08:00
Andrew Pareles 86decf8ed9 add (c) headers 2025-01-02 16:27:06 -08:00
Andrew Pareles bd9b9a9058 minor updates 2025-01-02 16:20:19 -08:00
Mathew Pareles 83c1f3430a fix assistant chat 2025-01-02 15:51:00 -08:00
Andrew Pareles 02de8937ac update copy 2025-01-02 01:28:19 -08:00
Mathew Pareles 78f3819c33 move extractCodeFromResult 2025-01-01 23:19:16 -08:00
Mathew Pareles b9cea4906e fix chat bubble direction 2025-01-01 23:08:32 -08:00
Mathew Pareles 4a4677b5e2 center button; fix single line button 2025-01-01 22:35:48 -08:00
Andrew Pareles 5dab1a6a77 update product.json 2025-01-01 22:05:57 -08:00
Mathew Pareles 9b01d5ba7c minor changes 2025-01-01 21:28:44 -08:00
Mathew Pareles dcb708758d fix input resize 2025-01-01 21:28:44 -08:00
Andrew Pareles dd4548466a minor chagnes 2025-01-01 20:01:10 -08:00
Andrew Pareles cda489e3e0 small fixes 2025-01-01 05:04:51 -08:00
Andrew Pareles 13594ef06c rework copy 2025-01-01 04:57:10 -08:00
Andrew Pareles c2b8df06eb desc 2025-01-01 04:47:41 -08:00
Andrew Pareles 33809ec48b finish adding css 2025-01-01 04:44:43 -08:00
Andrew Pareles b4820abc9e css 2025-01-01 04:31:44 -08:00
Andrew Pareles 0e79dec0d7 VOID ICONS 2024-12-31 22:03:38 -08:00
Andrew Pareles 659f01413f misc + track down icons to change 2024-12-31 20:27:31 -08:00
Andrew Pareles 110534df30 memo model dropdown + add ability to remove ctrlK zone 2024-12-31 16:18:18 -08:00
Andrew Pareles d3b61867f0 ctrlK now switches tabs + UI + remove async 2024-12-31 15:41:24 -08:00
Andrew Pareles 954e611fda Merge branch 'new-services' into model-selection 2024-12-31 13:56:07 -08:00
Mathew Pareles 198adaaf18 selection ui 2024-12-30 04:57:43 -05:00
Mathew Pareles 2df7fa9ebc better model selection 2024-12-30 04:36:31 -05:00
Mathew Pareles 312942b004 ollama instructions 2024-12-30 03:39:17 -05:00
Mathew Pareles 0a444f6ff8 mark if model is detected locally 2024-12-30 01:45:02 -05:00
Mathew Pareles 0c10cb73f6 fix polling enable bug 2024-12-29 23:45:46 -05:00
Mathew Pareles 3a3bc24fef better polling state 2024-12-29 05:07:52 -05:00
Jérôme Commaret 9543bf8eb3 Fix : Ajv missing deps at bundle 2024-12-28 22:19:16 +01:00
Jérôme Commaret d85a316536 Merge branch 'voideditor:main' into main 2024-12-27 13:59:49 +01:00
Mathew Pareles 076e0add44 prepare large merge 2024-12-25 19:21:16 -08:00
Mathew Pareles 8a17c9246b language logic 2024-12-24 17:34:18 -08:00
mp 1afb39b155 minor changes 2024-12-24 02:22:45 -08:00
mp f08fd04af5 autodetect language 2024-12-23 16:21:37 -08:00
Jérôme Commaret 2d80a29275 Merge branch 'voideditor:main' into main 2024-12-12 01:03:51 +01:00
Jérôme Commaret d8e9b50890 Advanced builder section added 2024-12-07 14:49:28 +01:00
81 changed files with 1511 additions and 656 deletions
+18 -6
View File
@@ -8,7 +8,7 @@ There are a few ways to contribute:
- 💡 Make suggestions in our [Discord](https://discord.gg/RSNjgaugJs).
- ⭐️ If you want to build your AI tool into Void, feel free to get in touch! It's very easy to extend Void, and the UX you create will be much more natural than a VSCode Extension.
Most of Void's code lives in `src/vs/workbench/contrib/void/browser/` and `src/vs/platform/void/`.
Void's code lives in `src/vs/workbench/contrib/void/browser/` and `src/vs/platform/void/`.
@@ -43,19 +43,19 @@ First, run `npm install -g node-gyp`. Then:
### Building Void
To build Void, open `void/` inside VSCode. Then:
To build Void, open `void/` inside VSCode. Then open your terminal and run:
1. `npm install` to install all dependencies.
2. `npm run watchreact` to build Void's browser dependencies like React.
3. Build.
3. Build Void.
- Press <kbd>Cmd+Shift+B</kbd> (Mac).
- Press <kbd>Ctrl+Shift+B</kbd> (Windows/Linux).
- This step can take ~5 min. The build is done when you see two check marks.
4. Run.
4. Run Void.
- Run `./scripts/code.sh` (Mac/Linux).
- Run `./scripts/code.bat` (Windows).
- This command should open up the built IDE. You can always press <kbd>Ctrl+R</kbd> (<kbd>Cmd+R</kbd>) inside the new window to see changes without re-building, or press or <kbd>Ctrl+Shift+P</kbd> in the new window and run "Reload Window".
- If you are actively developing Void, we strongly recommend adding the flags `--user-data-dir ./.tmp/user-data --extensions-dir ./.tmp/extensions` to the above run command (just append them at the end of the string). This will save all data and extensions to the `.tmp` folder. You can delete this folder to reset any IDE changes you made when testing.
#### Building Void from Terminal
@@ -75,6 +75,7 @@ Alternatively, if you want to build Void from the terminal, instead of pressing
- Make sure you follow the prerequisite steps.
- Make sure you have the same NodeJS version as `.nvmrc`.
- Make sure your `npm run watchreact` is running if you change any React files, or else you'll need to re-build.
- If you see missing styles, go to `src2/styles.css` and re-save the file.
- If you get `"TypeError: Failed to fetch dynamically imported module: vscode-file://vscode-app/.../workbench.desktop.main.js", source: file:///.../bootstrap-window.js`, make sure all imports end with `.js`.
- If you have any questions, feel free to [submit an issue](https://github.com/voideditor/void/issues/new). For building questions, you can also refer to VSCode's full [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) page.
@@ -82,7 +83,8 @@ Alternatively, if you want to build Void from the terminal, instead of pressing
## Bundling
We don't usually recommend bundling. Instead, you should probably just build. If you're sure you want to bundle 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) in the repo's parent's directory. Be patient - compiling can take ~25 minutes.
We don't usually recommend bundling. Instead, you should probably just build. If you're sure you want to bundle 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 - compiling can take ~25 minutes.
### Mac
- `npm run gulp vscode-darwin-arm64` - most common (Apple Silicon)
@@ -98,6 +100,16 @@ We don't usually recommend bundling. Instead, you should probably just build. If
- `npm run gulp vscode-linux-ia32`
### Output
This will generate a folder outside of `void/`:
```bash
workspace/
├── void/ # Your Void fork
└── VSCode-darwin-arm64/ # Generated output
```
# Guidelines
+26
View File
@@ -0,0 +1,26 @@
Void is a fork of VS Code, which is licensed under the MIT License (below).
Void's additions and modifications are licensed under the Apache 2.0 License (see LICENSE.txt).
--------------------
MIT License
Copyright (c) 2015 - present Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+197 -17
View File
@@ -1,21 +1,201 @@
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2015 - present Microsoft Corporation
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2025 Glass Devtools, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+1 -1
View File
@@ -13,4 +13,4 @@ To build and run Void, follow the steps in [`CONTRIBUTING.md`](https://github.co
Void is a fork of the of [vscode](https://github.com/microsoft/vscode) repository. For some useful links on VSCode, see [`VOID_USEFUL_LINKS.md`](https://github.com/voideditor/void/blob/main/VOID_USEFUL_LINKS.md).
## Support
Feel free to reach out in our [Discord](https://discord.gg/RSNjgaugJs) or contact us via email.
Feel free to reach out in our [Discord](https://discord.gg/RSNjgaugJs) server or contact us via email.
-41
View File
@@ -1,41 +0,0 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.9 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->
+1 -1
View File
@@ -32,6 +32,6 @@ Void is no longer an extension, so these links are no longer required, but they
- [The Full VSCode Extension API](https://code.visualstudio.com/api/references/vscode-api) - look on the right side for organization. The [bottom](https://code.visualstudio.com/api/references/vscode-api#api-patterns) of the page is easy to miss but is useful - cancellation tokens, events, disposables.
- [Activation events](https://code.visualstudio.com/api/references/activation-events) you can define in `package.json` (not the most useful)
- [Activation events](https://code.visualstudio.com/api/references/activation-events) you can define in `package.json` (not the most useful).
+4 -4
View File
@@ -425,15 +425,15 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
'resources/win32/vue.ico',
'resources/win32/xml.ico',
'resources/win32/yaml.ico',
'resources/win32/code_70x70.png',
'resources/win32/code_150x150.png'
'resources/win32/code_70x70.png', // <-- Void icon
'resources/win32/code_150x150.png' // <-- Void icon
], { base: '.' }));
} else if (platform === 'linux') {
all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' }));
all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' })); // <-- Void icon
} else if (platform === 'darwin') {
const shortcut = gulp.src('resources/darwin/bin/code.sh')
.pipe(replace('@@APPNAME@@', product.applicationName))
.pipe(rename('bin/code'));
.pipe(rename('bin/code')); // <-- Void icon
all = es.merge(all, shortcut);
}
+2 -2
View File
@@ -54,7 +54,7 @@ function darwinBundleDocumentType(extensions, icon, nameOrSuffix, utis) {
role: 'Editor',
ostypes: ['TEXT', 'utxt', 'TUTX', '****'],
extensions,
iconFile: 'resources/darwin/' + icon.toLowerCase() + '.icns',
iconFile: 'resources/darwin/' + icon.toLowerCase() + '.icns', // <-- Void icon code.icns
utis
};
}
@@ -179,7 +179,7 @@ exports.config = {
darwinForceDarkModeSupport: true,
darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : undefined,
linuxExecutableName: product.applicationName,
winIcon: 'resources/win32/code.ico',
winIcon: 'resources/win32/code.ico', // <-- Void icon
token: process.env['GITHUB_TOKEN'],
repo: product.electronRepository || undefined,
validateChecksum: true,
+2 -2
View File
@@ -68,7 +68,7 @@ function darwinBundleDocumentType(extensions: string[], icon: string, nameOrSuff
role: 'Editor',
ostypes: ['TEXT', 'utxt', 'TUTX', '****'],
extensions,
iconFile: 'resources/darwin/' + icon.toLowerCase() + '.icns',
iconFile: 'resources/darwin/' + icon.toLowerCase() + '.icns', // <-- Void icon code.icns
utis
};
}
@@ -196,7 +196,7 @@ export const config = {
darwinForceDarkModeSupport: true,
darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : undefined,
linuxExecutableName: product.applicationName,
winIcon: 'resources/win32/code.ico',
winIcon: 'resources/win32/code.ico', // <-- Void icon
token: process.env['GITHUB_TOKEN'],
repo: product.electronRepository || undefined,
validateChecksum: true,
+5 -2
View File
@@ -4,6 +4,7 @@
? ('; LicenseFile: "' + RepoDir + '\licenses\LICENSE-' + Language + '.rtf"') \
: '; LicenseFile: "' + RepoDir + '\' + RootLicenseFileName + '"'
[Setup]
AppId={#AppId}
AppName={#NameLong}
@@ -20,8 +21,10 @@ Compression=lzma
SolidCompression=yes
AppMutex={code:GetAppMutex}
SetupMutex={#AppMutex}setup
WizardImageFile="{#RepoDir}\resources\win32\inno-big-100.bmp,{#RepoDir}\resources\win32\inno-big-125.bmp,{#RepoDir}\resources\win32\inno-big-150.bmp,{#RepoDir}\resources\win32\inno-big-175.bmp,{#RepoDir}\resources\win32\inno-big-200.bmp,{#RepoDir}\resources\win32\inno-big-225.bmp,{#RepoDir}\resources\win32\inno-big-250.bmp"
WizardSmallImageFile="{#RepoDir}\resources\win32\inno-small-100.bmp,{#RepoDir}\resources\win32\inno-small-125.bmp,{#RepoDir}\resources\win32\inno-small-150.bmp,{#RepoDir}\resources\win32\inno-small-175.bmp,{#RepoDir}\resources\win32\inno-small-200.bmp,{#RepoDir}\resources\win32\inno-small-225.bmp,{#RepoDir}\resources\win32\inno-small-250.bmp"
; this is a Void icon comment. Old: WizardImageFile="{#RepoDir}\resources\win32\inno-big-100.bmp,{#RepoDir}\resources\win32\inno-big-125.bmp,{#RepoDir}\resources\win32\inno-big-150.bmp,{#RepoDir}\resources\win32\inno-big-175.bmp,{#RepoDir}\resources\win32\inno-big-200.bmp,{#RepoDir}\resources\win32\inno-big-225.bmp,{#RepoDir}\resources\win32\inno-big-250.bmp"
; this is a Void icon comment. Old: WizardSmallImageFile="{#RepoDir}\resources\win32\inno-small-100.bmp,{#RepoDir}\resources\win32\inno-small-125.bmp,{#RepoDir}\resources\win32\inno-small-150.bmp,{#RepoDir}\resources\win32\inno-small-175.bmp,{#RepoDir}\resources\win32\inno-small-200.bmp,{#RepoDir}\resources\win32\inno-small-225.bmp,{#RepoDir}\resources\win32\inno-small-250.bmp"
WizardImageFile="{#RepoDir}\resources\win32\inno-void.bmp"
WizardSmallImageFile="{#RepoDir}\resources\win32\inno-void.bmp"
SetupIconFile={#RepoDir}\resources\win32\code.ico
UninstallDisplayIcon={app}\{#ExeBasename}.exe
ChangesEnvironment=true
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "code-oss-dev",
"name": "void-dev",
"version": "1.94.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "code-oss-dev",
"name": "void-dev",
"version": "1.94.0",
"hasInstallScript": true,
"license": "MIT",
+6 -3
View File
@@ -1,9 +1,12 @@
{
"name": "code-oss-dev",
"name": "void-dev",
"productName": "Void",
"version": "1.94.0",
"distro": "ffcc24343ac46468a625666e5b9e673971dd1a1f",
"distro": "this is a commit number if we want to publish on npm",
"homepage": "https://voideditor.com",
"author": {
"name": "Microsoft Corporation"
"name": "Glass Devtools, Inc.",
"email": "andrew@voideditor.com"
},
"license": "MIT",
"main": "./out/main",
+23 -24
View File
@@ -1,36 +1,35 @@
{
"nameShort": "Void",
"nameLong": "Void",
"applicationName": "code-oss",
"dataFolderName": ".vscode-oss",
"win32MutexName": "vscodeoss",
"applicationName": "void",
"dataFolderName": ".void-editor",
"win32MutexName": "voideditor",
"licenseName": "MIT",
"licenseUrl": "https://github.com/microsoft/vscode/blob/main/LICENSE.txt",
"serverLicenseUrl": "https://github.com/microsoft/vscode/blob/main/LICENSE.txt",
"licenseUrl": "https://github.com/voideditor/void/blob/main/LICENSE.txt",
"serverLicenseUrl": "https://github.com/voideditor/void/blob/main/LICENSE.txt",
"serverGreeting": [],
"serverLicense": [],
"serverLicensePrompt": "",
"serverApplicationName": "code-server-oss",
"serverDataFolderName": ".vscode-server-oss",
"tunnelApplicationName": "code-tunnel-oss",
"win32DirName": "Microsoft Code OSS",
"win32NameVersion": "Microsoft Code OSS",
"win32RegValueName": "CodeOSS",
"win32x64AppId": "{{D77B7E06-80BA-4137-BCF4-654B95CCEBC5}",
"win32arm64AppId": "{{D1ACE434-89C5-48D1-88D3-E2991DF85475}",
"win32x64UserAppId": "{{CC6B787D-37A0-49E8-AE24-8559A032BE0C}",
"win32arm64UserAppId": "{{3AEBF0C8-F733-4AD4-BADE-FDB816D53D7B}",
"win32AppUserModelId": "Microsoft.CodeOSS",
"win32ShellNameShort": "C&ode - OSS",
"win32TunnelServiceMutex": "vscodeoss-tunnelservice",
"win32TunnelMutex": "vscodeoss-tunnel",
"darwinBundleIdentifier": "com.visualstudio.code.oss",
"linuxIconName": "code-oss",
"serverApplicationName": "void-server",
"serverDataFolderName": ".void-server",
"tunnelApplicationName": "void-tunnel",
"win32DirName": "Void",
"win32NameVersion": "Void",
"win32RegValueName": "VoidEditor",
"win32x64AppId": "{{9D394D01-1728-45A7-B997-A6C82C5452C3}",
"win32arm64AppId": "{{0668DD58-2BDE-4101-8CDA-40252DF8875D}",
"win32x64UserAppId": "{{8BED5DC1-6C55-46E6-9FE6-18F7E6F7C7F1}",
"win32arm64UserAppId": "{{F6C87466-BC82-4A8F-B0FF-18CA366BA4D8}",
"win32AppUserModelId": "Void.Editor",
"win32ShellNameShort": "V&oid",
"win32TunnelServiceMutex": "void-tunnelservice",
"win32TunnelMutex": "void-tunnel",
"darwinBundleIdentifier": "com.voideditor.code",
"linuxIconName": "void-editor",
"licenseFileName": "LICENSE.txt",
"reportIssueUrl": "https://github.com/microsoft/vscode/issues/new",
"reportIssueUrl": "https://github.com/voideditor/void/issues/new",
"nodejsRepository": "https://nodejs.org",
"urlProtocol": "code-oss",
"webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/out/vs/workbench/contrib/webview/browser/pre/",
"urlProtocol": "void-editor",
"extensionsGallery": {
"serviceUrl": "https://open-vsx.org/vscode/gallery",
"itemUrl": "https://open-vsx.org/vscode/item"
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 813 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

+1 -1
View File
@@ -159,7 +159,7 @@ export class InputBox extends Widget {
this.scrollableElement = new ScrollableElement(this.element, { vertical: ScrollbarVisibility.Auto });
if (this.options.flexibleWidth) {
this.input.setAttribute('wrap', 'off');
this.input.setAttribute('wrap', 'on');
this.mirror.style.whiteSpace = 'pre';
this.mirror.style.wordWrap = 'initial';
}
@@ -7,7 +7,10 @@ import { IRange } from '../../../common/core/range.js';
import { EditorOption } from '../../../common/config/editorOptions.js';
// THIS FILE IS OLD!!!
// THIS FILE IS OLD + UNUSED!!!
// SEE inlineDiffsService.ts INSTEAD.
export interface IInlineDiffService {
readonly _serviceBrand: undefined;
addDiff(editor: ICodeEditor, originalText: string, modifiedRange: IRange): void;
@@ -1,4 +1,7 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
// ---------- common ----------
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, ServiceSendLLMMessageParams, MainLLMMessageParams, MainLLMMessageAbortParams, ServiceModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, MainModelListParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from './llmMessageTypes.js';
import { IChannel } from '../../../base/parts/ipc/common/ipc.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { IRange } from '../../../editor/common/core/range'
import { ProviderName, SettingsOfProvider } from './voidSettingsTypes.js'
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { createDecorator } from '../../instantiation/common/instantiation.js';
import { ProxyChannel } from '../../../base/parts/ipc/common/ipc.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { createDecorator } from '../../instantiation/common/instantiation.js';
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
@@ -22,7 +22,10 @@ type RefreshableState = ({
state: 'refreshing',
timeoutId: NodeJS.Timeout | null, // the timeoutId of the most recent call to refreshModels
} | {
state: 'success',
state: 'finished',
timeoutId: null,
} | {
state: 'finished_invisible',
timeoutId: null,
})
@@ -82,7 +85,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ
for (const providerName of refreshableProviderNames) {
const { _enabled: enabled } = this.voidSettingsService.state.settingsOfProvider[providerName]
this.refreshModels(providerName, !enabled)
this.refreshModels(providerName, !enabled, { isPolling: true, isInternal: true })
// every time providerName.enabled changes, refresh models too, like a useEffect
let relevantVals = () => refreshBasedOn[providerName].map(settingName => this.voidSettingsService.state.settingsOfProvider[providerName][settingName])
@@ -98,7 +101,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ
// if it was just enabled, or there was a change and it wasn't to the enabled state, refresh
if ((enabled && !prevEnabled) || (!enabled && !prevEnabled)) {
// if user just clicked enable, refresh
this.refreshModels(providerName, !enabled)
this.refreshModels(providerName, !enabled, { isPolling: false, isInternal: true })
}
else {
// else if user just clicked disable, don't refresh
@@ -131,11 +134,16 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ
// start listening for models (and don't stop until success)
async refreshModels(providerName: RefreshableProviderName, enableProviderOnSuccess?: boolean) {
async refreshModels(providerName: RefreshableProviderName, enableProviderOnSuccess?: boolean, options?: { isPolling?: boolean, isInternal?: boolean }) {
const { isPolling, isInternal } = options ?? {}
console.log(`refreshModels, isInternal ${isInternal} isPolling ${isPolling}`)
this._clearProviderTimeout(providerName)
// start loading models
this._setRefreshState(providerName, 'refreshing')
if (!isInternal) this._setRefreshState(providerName, 'refreshing')
const fn = providerName === 'ollama' ? this.llmMessageService.ollamaList
: providerName === 'openAICompatible' ? this.llmMessageService.openAICompatibleList
@@ -153,15 +161,22 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ
this.voidSettingsService.setSettingOfProvider(providerName, '_enabled', true)
}
this._setRefreshState(providerName, 'success')
if (!isInternal) this._setRefreshState(providerName, 'finished')
},
onError: ({ error }) => {
// poll
console.log('retrying list models:', providerName, error)
const timeoutId = setTimeout(() => this.refreshModels(providerName, enableProviderOnSuccess), REFRESH_INTERVAL)
this._setTimeoutId(providerName, timeoutId)
}
})
if (isInternal) this._setRefreshState(providerName, 'finished_invisible')
// check if we should poll
// if it was originally called as `isPolling` and if the `autoRefreshModels` flag is enabled
if (isPolling && this.voidSettingsService.state.featureFlagSettings.autoRefreshModels) {
const timeoutId = setTimeout(() => this.refreshModels(providerName, enableProviderOnSuccess, options), REFRESH_INTERVAL)
this._setTimeoutId(providerName, timeoutId)
}
}
_clearAllTimeouts() {
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Emitter, Event } from '../../../base/common/event.js';
import { Disposable } from '../../../base/common/lifecycle.js';
@@ -222,7 +222,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
setDefaultModels(providerName: ProviderName, newDefaultModelNames: string[]) {
const { models } = this.state.settingsOfProvider[providerName]
const newDefaultModels = modelInfoOfDefaultNames(newDefaultModelNames)
const newDefaultModels = modelInfoOfDefaultNames(newDefaultModelNames, { isAutodetected: true, existingModels: models })
const newModels = [
...newDefaultModels,
...models.filter(m => !m.isDefault), // keep any non-default models
@@ -1,8 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
@@ -11,12 +11,32 @@ export type VoidModelInfo = {
modelName: string,
isDefault: boolean, // whether or not it's a default for its provider
isHidden: boolean, // whether or not the user is hiding it
isAutodetected?: boolean, // whether the model was autodetected by polling
}
type ModelInfoOfDefaultNamesOptions = { isAutodetected: true, existingModels: VoidModelInfo[] } // | { isOtherOption: true, ...otherOptions }
export const modelInfoOfDefaultNames = (modelNames: string[], options?: ModelInfoOfDefaultNamesOptions): VoidModelInfo[] => {
export const modelInfoOfDefaultNames = (modelNames: string[]): VoidModelInfo[] => {
const { isAutodetected, existingModels } = options ?? {}
const isDefault = true
const isHidden = modelNames.length >= 10 // hide all models if there are a ton of them, and make user enable them individually
return modelNames.map((modelName, i) => ({ modelName, isDefault: true, isHidden }))
if (!existingModels) {
return modelNames.map((modelName, i) => ({ modelName, isDefault, isAutodetected, isHidden, }))
} else {
// keep existing `isHidden` property
const existingModelsMap: Record<string, VoidModelInfo> = {}
for (const em of existingModels) {
existingModelsMap[em.modelName] = em
}
return modelNames.map((modelName, i) => ({ modelName, isDefault, isAutodetected, isHidden: !!existingModelsMap[modelName]?.isHidden, }))
}
}
// https://docs.anthropic.com/en/docs/about-claude/models
@@ -121,11 +141,11 @@ export const defaultProviderSettings = {
}
} as const
export type ProviderName = keyof typeof defaultProviderSettings
export const providerNames = Object.keys(defaultProviderSettings) as ProviderName[]
export const localProviderNames = ['ollama', 'openAICompatible'] satisfies ProviderName[] // all local names
export const nonlocalProviderNames = providerNames.filter((name) => !(localProviderNames as string[]).includes(name)) // all non-local names
type CustomSettingName = UnionOfKeys<typeof defaultProviderSettings[ProviderName]>
type CustomProviderSettings<providerName extends ProviderName> = {
@@ -183,7 +203,7 @@ export const displayInfoOfProviderName = (providerName: ProviderName): DisplayIn
}
else if (providerName === 'openAICompatible') {
return {
title: 'OpenAI-Compatible',
title: 'Other',
}
}
else if (providerName === 'gemini') {
@@ -222,7 +242,7 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
providerName === 'openRouter' ? 'Get your [API Key here](https://openrouter.ai/settings/keys).' :
providerName === 'gemini' ? 'Get your [API Key here](https://aistudio.google.com/apikey).' :
providerName === 'groq' ? 'Get your [API Key here](https://console.groq.com/keys).' :
providerName === 'openAICompatible' ? undefined :
providerName === 'openAICompatible' ? 'Add any OpenAI-Compatible endpoint.' :
undefined,
}
}
@@ -236,7 +256,7 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
: providerName === 'openAICompatible' ? 'https://my-website.com/v1'
: '(never)',
subTextMd: providerName === 'ollama' ? 'Read about Ollama [Endpoints here](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-expose-ollama-on-my-network).' :
subTextMd: providerName === 'ollama' ? 'Read about advanced [Endpoints here](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-expose-ollama-on-my-network).' :
undefined,
}
}
@@ -359,7 +379,7 @@ export const featureNames = ['Ctrl+L', 'Ctrl+K', 'Autocomplete'] as const
// the models of these can be refreshed (in theory all can, but not all should)
export const refreshableProviderNames = ['ollama', 'openAICompatible'] satisfies ProviderName[]
export const refreshableProviderNames = localProviderNames
export type RefreshableProviderName = typeof refreshableProviderNames[number]
@@ -385,7 +405,7 @@ type FeatureFlagDisplayInfo = {
export const displayInfoOfFeatureFlag = (featureFlag: FeatureFlagName): FeatureFlagDisplayInfo => {
if (featureFlag === 'autoRefreshModels') {
return {
description: `Automatically scan for and enable local models.`, // ${`refreshableProviderNames.map(providerName => titleOfProviderName(providerName)).join(', ')`}
description: `Automatically detect local providers and models (${refreshableProviderNames.map(providerName => displayInfoOfProviderName(providerName).title).join(', ')}).`,
}
}
throw new Error(`featureFlagInfo: Unknown feature flag: "${featureFlag}"`)
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import Anthropic from '@anthropic-ai/sdk';
import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Content, GoogleGenerativeAI, GoogleGenerativeAIFetchError } from '@google/generative-ai';
import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
// // Greptile
// // https://docs.greptile.com/api-reference/query
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import Groq from 'groq-sdk';
import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Ollama } from 'ollama';
import { _InternalModelListFnType, _InternalSendLLMMessageFnType, OllamaModelResponse } from '../../common/llmMessageTypes.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import OpenAI from 'openai';
import { _InternalModelListFnType, _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { LLMMMessageParams, OnText, OnFinalMessage, OnError } from '../../common/llmMessageTypes.js';
import { IMetricsService } from '../../common/metricsService.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
// registered in app.ts
// code convention is to make a service responsible for this stuff, and not a channel, but having fewer files is simpler...
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Disposable } from '../../../base/common/lifecycle.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
@@ -170,6 +170,9 @@ export class EditorGroupWatermark extends Disposable {
this.clear();
const box = append(this.shortcuts, $('.watermark-box'));
const boxBelow = append(this.shortcuts, $(''))
boxBelow.style.display = 'flex'
boxBelow.style.flex = 'row'
boxBelow.style.justifyContent = 'center'
const update = async () => {
@@ -184,9 +187,13 @@ export class EditorGroupWatermark extends Disposable {
// Void - if the workbench is empty, show open
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
// Open Folder
// Open a folder
const button = h('button')
button.root.textContent = 'Open Folder'
button.root.classList.add('void-watermark-button')
button.root.style.display = 'block'
button.root.style.marginLeft = 'auto'
button.root.style.marginRight = 'auto'
button.root.textContent = 'Open a folder'
button.root.onclick = () => {
this.commandService.executeCommand(isMacintosh && isNative ? OpenFileFolderAction.ID : OpenFolderAction.ID)
// if (this.contextKeyService.contextMatchesRules(ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace')))) {
@@ -197,58 +204,66 @@ export class EditorGroupWatermark extends Disposable {
}
box.appendChild(button.root);
// Recents
const recentlyOpened = await this.workspacesService.getRecentlyOpened()
.catch(() => ({ files: [], workspaces: [] })).then(w => w.workspaces);
if (recentlyOpened.length !== 0) {
box.append(
...recentlyOpened.map(w => {
const span = $('div')
span.textContent = 'Recent'
span.style.fontWeight = '500'
box.append(span)
let fullPath: string;
let windowOpenable: IWindowOpenable;
if (isRecentFolder(w)) {
windowOpenable = { folderUri: w.folderUri };
fullPath = w.label || this.labelService.getWorkspaceLabel(w.folderUri, { verbose: Verbosity.LONG });
}
else {
return null
// fullPath = w.label || this.labelService.getWorkspaceLabel(w.workspace, { verbose: Verbosity.LONG });
// windowOpenable = { workspaceUri: w.workspace.configPath };
}
box.append(
...recentlyOpened.map(w => {
let fullPath: string;
let windowOpenable: IWindowOpenable;
if (isRecentFolder(w)) {
windowOpenable = { folderUri: w.folderUri };
fullPath = w.label || this.labelService.getWorkspaceLabel(w.folderUri, { verbose: Verbosity.LONG });
}
else {
return null
// fullPath = w.label || this.labelService.getWorkspaceLabel(w.workspace, { verbose: Verbosity.LONG });
// windowOpenable = { workspaceUri: w.workspace.configPath };
}
const { name, parentPath } = splitRecentLabel(fullPath);
const { name, parentPath } = splitRecentLabel(fullPath);
const li = $('li');
const link = $('span');
link.classList.add('void-link')
const li = $('li');
const link = $('button.button-link');
link.innerText = name;
link.title = fullPath;
link.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, parentPath));
link.addEventListener('click', e => {
this.hostService.openWindow([windowOpenable], {
forceNewWindow: e.ctrlKey || e.metaKey,
remoteAuthority: w.remoteAuthority || null // local window if remoteAuthority is not set or can not be deducted from the openable
link.innerText = name;
link.title = fullPath;
link.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, parentPath));
link.addEventListener('click', e => {
this.hostService.openWindow([windowOpenable], {
forceNewWindow: e.ctrlKey || e.metaKey,
remoteAuthority: w.remoteAuthority || null // local window if remoteAuthority is not set or can not be deducted from the openable
});
e.preventDefault();
e.stopPropagation();
});
e.preventDefault();
e.stopPropagation();
});
li.appendChild(link);
li.appendChild(link);
const span = $('span');
span.classList.add('path');
span.classList.add('detail');
span.innerText = parentPath;
span.title = fullPath;
li.appendChild(span);
const span = $('span');
span.style.paddingLeft = '4px';
span.classList.add('path');
span.classList.add('detail');
span.innerText = parentPath;
span.title = fullPath;
li.appendChild(span);
return li
}).filter(v => !!v)
)
return li
}).filter(v => !!v)
)
}
@@ -280,6 +295,11 @@ export class EditorGroupWatermark extends Disposable {
const keys3 = this.keybindingService.lookupKeybinding('workbench.action.openGlobalKeybindings');
const button3 = append(boxBelow, $('button'));
button3.textContent = 'Void Settings'
button3.style.display = 'block'
button3.style.marginLeft = 'auto'
button3.style.marginRight = 'auto'
button3.classList.add('void-settings-watermark-button')
const label3 = new KeybindingLabel(button3, OS, { renderUnboundKeybindings: true, ...defaultKeybindingLabelStyles });
if (keys3)
label3.set(keys3);
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../base/common/lifecycle.js';
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
@@ -17,6 +17,7 @@ import { IEditorService } from '../../../services/editor/common/editorService.js
import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js';
import { EditorResourceAccessor } from '../../../common/editor.js';
import { IModelService } from '../../../../editor/common/services/model.js';
import { extractCodeFromResult } from './helpers/extractCodeFromResult.js';
// The extension this was called from is here - https://github.com/voideditor/void/blob/autocomplete/extensions/void/src/extension/extension.ts
@@ -165,20 +166,6 @@ const postprocessResult = (result: string) => {
}
const extractCodeFromResult = (result: string) => {
// Match either:
// 1. ```language\n<code>```
// 2. ```<code>```
const match = result.match(/```(?:\w+\n)?([\s\S]*?)```|```([\s\S]*?)```/);
if (!match) {
return result;
}
// Return whichever group matched (non-empty)
return match[1] ?? match[2] ?? result;
}
// trims the end of the prefix to improve cache hit rate
const removeLeftTabsAndTrimEnd = (s: string): string => {
@@ -768,3 +755,5 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
registerSingleton(IAutocompleteService, AutocompleteService, InstantiationType.Eager);
@@ -1,3 +1,8 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../../base/common/lifecycle.js';
import { URI } from '../../../../../base/common/uri.js';
import { generateUuid } from '../../../../../base/common/uuid.js';
@@ -9,7 +14,6 @@ import { createDecorator } from '../../../../../platform/instantiation/common/in
// lets you add a "consistent" item to a Model (aka URI), instead of just to a single editor
type AddItemInputs = { uri: URI; fn: (editor: ICodeEditor) => (() => void); }
export interface IConsistentItemService {
@@ -56,6 +60,7 @@ export class ConsistentItemService extends Disposable {
}
// when editor switches tabs (models)
const addTabSwitchListeners = (editor: ICodeEditor) => {
this._register(
editor.onDidChangeModel(e => {
@@ -65,6 +70,7 @@ export class ConsistentItemService extends Disposable {
)
}
// when editor is disposed
const addDisposeListener = (editor: ICodeEditor) => {
this._register(editor.onDidDispose(() => {
// anything on the editor has been disposed already
@@ -176,3 +182,243 @@ export class ConsistentItemService extends Disposable {
registerSingleton(IConsistentItemService, ConsistentItemService, InstantiationType.Eager);
// mostly generated by o1 (almost the same as above, but just for 1 editor)
export interface IConsistentEditorItemService {
readonly _serviceBrand: undefined;
addToEditor(editor: ICodeEditor, fn: () => () => void): string;
removeFromEditor(itemId: string): void;
}
export const IConsistentEditorItemService = createDecorator<IConsistentEditorItemService>('ConsistentEditorItemService');
export class ConsistentEditorItemService extends Disposable {
readonly _serviceBrand: undefined;
/**
* For each editorId, we track the set of itemIds that have been "added" to that editor.
* This does *not* necessarily mean they're currently mounted (the user may have switched models).
*/
private readonly itemIdsByEditorId: Record<string, Set<string>> = {};
/**
* For each itemId, we store relevant info (the fn to call on the editor, the editorId, the uri, and the current dispose function).
*/
private readonly itemInfoById: Record<
string,
{
editorId: string;
uriFsPath: string;
fn: (editor: ICodeEditor) => () => void;
disposeFn?: () => void;
}
> = {};
constructor(
@ICodeEditorService private readonly _editorService: ICodeEditorService,
) {
super();
//
// Wire up listeners to watch for new editors, removed editors, etc.
//
// Initialize any already-existing editors
for (const editor of this._editorService.listCodeEditors()) {
this._initializeEditor(editor);
}
// When an editor is added, track it
this._register(
this._editorService.onCodeEditorAdd((editor) => {
this._initializeEditor(editor);
})
);
// When an editor is removed, remove all items associated with that editor
this._register(
this._editorService.onCodeEditorRemove((editor) => {
this._removeAllItemsFromEditor(editor);
})
);
}
/**
* Sets up listeners on the provided editor so that:
* - If the editor changes models, we remove items and re-mount only if the new model matches.
* - If the editor is disposed, we do the needed cleanup.
*/
private _initializeEditor(editor: ICodeEditor) {
const editorId = editor.getId();
//
// Listen for model changes
//
this._register(
editor.onDidChangeModel((e) => {
this._removeAllItemsFromEditor(editor);
if (!e.newModelUrl) {
return;
}
// Re-mount any items that belong to this editor and match the new URI
const itemsForEditor = this.itemIdsByEditorId[editorId];
if (itemsForEditor) {
for (const itemId of itemsForEditor) {
const itemInfo = this.itemInfoById[itemId];
if (itemInfo && itemInfo.uriFsPath === e.newModelUrl.fsPath) {
this._mountItemOnEditor(editor, itemId);
}
}
}
})
);
//
// When the editor is disposed, remove all items from it
//
this._register(
editor.onDidDispose(() => {
this._removeAllItemsFromEditor(editor);
})
);
//
// If the editor already has a model (e.g. on initial load), try mounting items
//
const uri = editor.getModel()?.uri;
if (!uri) {
return;
}
const itemsForEditor = this.itemIdsByEditorId[editorId];
if (itemsForEditor) {
for (const itemId of itemsForEditor) {
const itemInfo = this.itemInfoById[itemId];
if (itemInfo && itemInfo.uriFsPath === uri.fsPath) {
this._mountItemOnEditor(editor, itemId);
}
}
}
}
/**
* Actually calls the item-creation function `fn(editor)` and saves the resulting disposeFn
* so we can later clean it up.
*/
private _mountItemOnEditor(editor: ICodeEditor, itemId: string) {
const info = this.itemInfoById[itemId];
if (!info) {
return;
}
const { fn } = info;
const disposeFn = fn(editor);
info.disposeFn = disposeFn;
}
/**
* Removes a single item from an editor (calling its `disposeFn` if present).
*/
private _removeItemFromEditor(editor: ICodeEditor, itemId: string) {
const info = this.itemInfoById[itemId];
if (info?.disposeFn) {
info.disposeFn();
info.disposeFn = undefined;
}
}
/**
* Removes *all* items from the given editor. Typically called when the editor changes model or is disposed.
*/
private _removeAllItemsFromEditor(editor: ICodeEditor) {
const editorId = editor.getId();
const itemsForEditor = this.itemIdsByEditorId[editorId];
if (!itemsForEditor) {
return;
}
for (const itemId of itemsForEditor) {
this._removeItemFromEditor(editor, itemId);
}
}
/**
* Public API: Adds an item to an *individual* editor (determined by editor ID),
* but only when that editor is showing the same model (uri.fsPath).
*/
addToEditor(editor: ICodeEditor, fn: () => () => void): string {
const uri = editor.getModel()?.uri
if (!uri) {
throw new Error('No URI on the provided editor or in AddItemInputs.');
}
const editorId = editor.getId();
// Create an ID for this item
const itemId = generateUuid();
// Record the info
this.itemInfoById[itemId] = {
editorId,
uriFsPath: uri.fsPath,
fn,
};
// Add to the editor's known items
if (!this.itemIdsByEditorId[editorId]) {
this.itemIdsByEditorId[editorId] = new Set();
}
this.itemIdsByEditorId[editorId].add(itemId);
// If the editor's current URI matches, mount it now
if (editor.getModel()?.uri.fsPath === uri.fsPath) {
this._mountItemOnEditor(editor, itemId);
}
return itemId;
}
/**
* Public API: Removes an item from the *specific* editor. We look up which editor
* had this item and remove it from that editor.
*/
removeFromEditor(itemId: string): void {
const info = this.itemInfoById[itemId];
if (!info) {
// Nothing to remove
return;
}
const { editorId } = info;
// Find the editor in question
const editor = this._editorService.listCodeEditors().find(
(ed) => ed.getId() === editorId
);
if (editor) {
// Dispose on that editor
this._removeItemFromEditor(editor, itemId);
}
// Clean up references
this.itemIdsByEditorId[editorId]?.delete(itemId);
delete this.itemInfoById[itemId];
}
}
registerSingleton(IConsistentEditorItemService, ConsistentEditorItemService, InstantiationType.Eager);
@@ -0,0 +1,18 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
export const extractCodeFromResult = (result: string) => {
// Match either:
// 1. ```language\n<code>```
// 2. ```<code>```
const match = result.match(/```(?:\w+\n)?([\s\S]*?)```|```([\s\S]*?)```/);
if (!match) {
return result;
}
// Return whichever group matched (non-empty)
return match[1] ?? match[2] ?? result;
}
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { diffLines } from '../react/out/diff/index.js'
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { isMacintosh } from '../../../../../base/common/platform.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../base/common/lifecycle.js';
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
@@ -26,7 +26,7 @@ import { ILanguageService } from '../../../../editor/common/languages/language.j
import * as dom from '../../../../base/browser/dom.js';
import { Widget } from '../../../../base/browser/ui/widget.js';
import { URI } from '../../../../base/common/uri.js';
import { IConsistentItemService } from './helperServices/consistentItemService.js';
import { IConsistentEditorItemService, IConsistentItemService } from './helperServices/consistentItemService.js';
import { ctrlKStream_prefixAndSuffix, ctrlKStream_prompt, ctrlKStream_systemMessage, ctrlLStream_prompt, ctrlLStream_systemMessage } from './prompt/prompts.js';
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
import { IPosition } from '../../../../editor/common/core/position.js';
@@ -117,7 +117,7 @@ type CtrlKZone = {
editorId: string; // the editor the input lives on
_mountInfo: null | {
inputBox: InputBox; // the input box that lives in the zone
inputBoxRef: { current: InputBox | null }; // the input box that lives in the zone
dispose: () => void;
refresh: () => void;
}
@@ -136,7 +136,7 @@ type DiffZone = {
} | {
isStreaming: false;
streamRequestIdRef?: undefined;
line: null;
line?: undefined;
};
editorId?: undefined;
} & CommonZoneProps
@@ -171,6 +171,7 @@ export interface IInlineDiffsService {
startApplying(opts: StartApplyingOpts): number | undefined;
interruptStreaming(diffareaid: number): void;
addCtrlKZone(opts: AddCtrlKOpts): number | undefined;
removeCtrlKZone(opts: { diffareaid: number }): void;
}
export const IInlineDiffsService = createDecorator<IInlineDiffsService>('inlineDiffAreasService');
@@ -195,6 +196,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
@ILLMMessageService private readonly _llmMessageService: ILLMMessageService,
@IConsistentItemService private readonly _consistentItemService: IConsistentItemService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IConsistentEditorItemService private readonly _consistentEditorItemService: IConsistentEditorItemService,
) {
super();
@@ -208,9 +210,8 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
this._register(
model.onDidChangeContent(e => {
// it's as if we just called _write, now all we need to do is realign and refresh
const uri = model.uri
if (this.weAreWriting) return
const uri = model.uri
this._onUserChangeContent(uri, e)
})
)
@@ -242,6 +243,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
private _onInternalChangeContent(uri: URI, { shouldRealign }: { shouldRealign: false | { newText: string, oldRange: IRange } }) {
if (shouldRealign) {
const { newText, oldRange } = shouldRealign
console.log('realiging', newText, oldRange)
this._realignAllDiffAreasLines(uri, newText, oldRange)
}
this._refreshStylesAndDiffsInURI(uri)
@@ -319,76 +321,78 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
mostRecentTextOfCtrlKZoneId: Record<string, string | undefined> = {}
private _addCtrlKZoneInput = async (ctrlKZone: CtrlKZone) => {
private _addCtrlKZoneInput = (ctrlKZone: CtrlKZone) => {
const { editorId } = ctrlKZone
const editor = this._editorService.listCodeEditors().find(e => e.getId() === editorId)
if (!editor) {
console.error('editor not found')
return null
}
if (!editor) { return null }
const domNode = document.createElement('div');
domNode.style.zIndex = '1'
const viewZone: IViewZone = {
afterLineNumber: ctrlKZone.startLine - 1,
domNode: domNode,
heightInPx: 0,
suppressMouseDown: false,
};
// mount zone
let zoneId: string | null = null
editor.changeViewZones(accessor => {
zoneId = accessor.addZone(viewZone)
})
let viewZone_: IViewZone | null = null
const inputBoxRef: { current: InputBox | null } = { current: null }
const itemId = this._consistentEditorItemService.addToEditor(editor, () => {
const domNode = document.createElement('div');
domNode.style.zIndex = '1'
const viewZone: IViewZone = {
afterLineNumber: ctrlKZone.startLine - 1,
domNode: domNode,
heightInPx: 52,
suppressMouseDown: false,
};
viewZone_ = viewZone
let res_: (inputBox: InputBox) => void
const inputBoxPromise: Promise<InputBox> = new Promise((res, rej) => { res_ = res })
// mount zone
editor.changeViewZones(accessor => {
zoneId = accessor.addZone(viewZone)
})
// mount react
this._instantiationService.invokeFunction(accessor => {
const props: QuickEditPropsType = {
diffareaid: ctrlKZone.diffareaid,
onGetInputBox(inputBox) {
res_(inputBox)
// not sure why this requries a timeout
setTimeout(() => inputBox.focus(), 0)
},
onChangeHeight(height) {
if (height === undefined) return
viewZone.heightInPx = height
// re-render with this new height
editor.changeViewZones(accessor => {
if (zoneId) {
accessor.layoutZone(zoneId)
// mount react
this._instantiationService.invokeFunction(accessor => {
mountCtrlK(domNode, accessor, {
diffareaid: ctrlKZone.diffareaid,
onGetInputBox: (inputBox) => {
inputBoxRef.current = inputBox
// if it's mounting for the first time, focus it
if (!(ctrlKZone.diffareaid in this.mostRecentTextOfCtrlKZoneId)) { // detect first mount this way (a hack)
this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] = undefined
setTimeout(() => inputBox.focus(), 0)
}
})
},
onUserUpdateText: (text) => { this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] = text; },
initText: this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] ?? null,
}
mountCtrlK(domNode, accessor, props)
})
},
onChangeHeight(height) {
if (height === undefined) return
viewZone.heightInPx = height
// re-render with this new height
editor.changeViewZones(accessor => {
if (zoneId) {
accessor.layoutZone(zoneId)
}
})
},
onUserUpdateText: (text) => { this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] = text; },
initText: this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] ?? null,
} satisfies QuickEditPropsType)
const inputBox = await inputBoxPromise
})
return () => editor.changeViewZones(accessor => {
if (zoneId)
accessor.removeZone(zoneId)
})
})
return {
inputBox,
inputBoxRef,
refresh: () => editor.changeViewZones(accessor => {
if (zoneId && viewZone) {
viewZone.afterLineNumber = ctrlKZone.startLine - 1
if (zoneId && viewZone_) {
viewZone_.afterLineNumber = ctrlKZone.startLine - 1
accessor.layoutZone(zoneId)
}
}),
dispose: () => {
editor.changeViewZones(accessor => {
if (zoneId)
accessor.removeZone(zoneId)
})
this._consistentEditorItemService.removeFromEditor(itemId)
},
}
} satisfies CtrlKZone['_mountInfo']
}
@@ -398,12 +402,12 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
const diffArea = this.diffAreaOfId[diffareaid]
if (diffArea.type !== 'CtrlKZone') continue
if (!diffArea._mountInfo) {
diffArea._mountInfo = await this._addCtrlKZoneInput(diffArea)
diffArea._mountInfo = this._addCtrlKZoneInput(diffArea)
console.log('MOUNTED', diffArea.diffareaid)
}
else {
diffArea._mountInfo.refresh()
}
}
}
@@ -574,10 +578,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
type: 'DiffZone',
_diffOfId: {},
_URI: uri,
_streamState: {
isStreaming: false,
line: null,
} as const,
_streamState: { isStreaming: false },
_removeStylesFns: new Set(),
}
}
@@ -735,17 +736,14 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
for (const diffareaid of this.diffAreasOfURI[model.uri.fsPath] || []) {
const diffArea = this.diffAreaOfId[diffareaid]
console.log('DA', diffArea.startLine, diffArea.endLine)
console.log('CHANGE', startLine, endLine)
// if the diffArea is entirely above the range, it is not affected
if (diffArea.endLine < startLine) {
// console.log('DA FULLY ABOVE (doing nothing)')
// console.log('CHANGE FULLY BELOW DA (doing nothing)')
continue
}
// if a diffArea is entirely below the range, shift the diffArea up/down by the delta amount of newlines
else if (endLine < diffArea.startLine) {
// console.log('DA FULLY BELOW')
// console.log('CHANGE FULLY ABOVE DA')
const changedRangeHeight = endLine - startLine + 1
const deltaNewlines = newTextHeight - changedRangeHeight
diffArea.startLine += deltaNewlines
@@ -766,16 +764,16 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
}
// if the change contains only the diffArea's top
else if (startLine < diffArea.startLine && diffArea.startLine <= endLine) {
// console.log('TOP ONLY')
// console.log('CHANGE CONTAINS TOP OF DA ONLY')
const numOverlappingLines = endLine - diffArea.startLine + 1
const numRemainingLinesInDA = diffArea.endLine - diffArea.startLine + 1 - numOverlappingLines
const newHeight = numRemainingLinesInDA + (newTextHeight - 1)
const newHeight = (numRemainingLinesInDA - 1) + (newTextHeight - 1) + 1
diffArea.startLine = startLine
diffArea.endLine = startLine + newHeight
}
// if the change contains only the diffArea's bottom
else if (startLine <= diffArea.endLine && diffArea.endLine < endLine) {
// console.log('BOTTOM ONLY')
// console.log('CHANGE CONTAINS BOTTOM OF DA ONLY')
const numOverlappingLines = diffArea.endLine - startLine + 1
diffArea.endLine += newTextHeight - numOverlappingLines
}
@@ -818,47 +816,40 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
// if streaming, use diffs to figure out where to write new code
// these are two different coordinate systems - new and old line number
let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted
let originalCodeStartLine: number // get original[oldStartingPoint...]
let newCodeEndLine: number // get file[diffArea.startLine...newFileEndLine] with line=newFileEndLine highlighted
let originalCodeStartLine: number // get original[oldStartingPoint...] (line in the original code, so starts at 1)
const lastDiff = computedDiffs.pop()
if (!lastDiff) {
// if the writing is identical so far, display no changes
newFileEndLine = diffZone.startLine
originalCodeStartLine = 1
newCodeEndLine = 1
}
else {
if (lastDiff.type === 'insertion') {
newFileEndLine = lastDiff.endLine
originalCodeStartLine = lastDiff.originalStartLine
}
else if (lastDiff.type === 'deletion') {
newFileEndLine = lastDiff.startLine
originalCodeStartLine = lastDiff.originalStartLine
}
else if (lastDiff.type === 'edit') {
newFileEndLine = lastDiff.endLine
originalCodeStartLine = lastDiff.originalStartLine
}
else {
originalCodeStartLine = lastDiff.originalStartLine
if (lastDiff.type === 'insertion' || lastDiff.type === 'edit')
newCodeEndLine = lastDiff.endLine
else if (lastDiff.type === 'deletion')
newCodeEndLine = lastDiff.startLine
else
throw new Error(`Void: diff.type not recognized on: ${lastDiff}`)
}
}
diffZone._streamState.line = newFileEndLine
// lines are 1-indexed
const newFileTop = llmText.split('\n').slice(diffZone.startLine, (newFileEndLine - 1)).join('\n')
const oldFileBottom = diffZone.originalCode.split('\n').slice((originalCodeStartLine - 1), Infinity).join('\n')
const newCodeTop = llmText.split('\n').slice(0, (newCodeEndLine - 1) + 1).join('\n')
const oldFileBottom = diffZone.originalCode.split('\n').slice((originalCodeStartLine - 1) + 1, Infinity).join('\n')
const newCode = `${newFileTop}\n${oldFileBottom}`
const newCode = `${newCodeTop}\n${oldFileBottom}`
this._writeText(uri, newCode,
{ startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, // 1-indexed
{ shouldRealignDiffAreas: true }
)
// add diffZone.startLine to convert to right coordinate system (line in file, not in diffarea)
diffZone._streamState.line = (diffZone.startLine - 1) + newCodeEndLine
return computedDiffs
@@ -866,6 +857,54 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
// // if streaming, use diffs to figure out where to write new code
// // these are two different coordinate systems - new and old line number
// let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted
// let originalCodeStartLine: number // get original[oldStartingPoint...]
// const lastDiff = computedDiffs.pop()
// if (!lastDiff) {
// // if the writing is identical so far, display no changes
// newFileEndLine = diffZone.startLine
// originalCodeStartLine = 1
// }
// else {
// if (lastDiff.type === 'insertion') {
// newFileEndLine = lastDiff.endLine
// originalCodeStartLine = lastDiff.originalStartLine
// }
// else if (lastDiff.type === 'deletion') {
// newFileEndLine = lastDiff.startLine
// originalCodeStartLine = lastDiff.originalStartLine
// }
// else if (lastDiff.type === 'edit') {
// newFileEndLine = lastDiff.endLine
// originalCodeStartLine = lastDiff.originalStartLine
// }
// else {
// throw new Error(`Void: diff.type not recognized on: ${lastDiff}`)
// }
// }
// diffZone._streamState.line = newFileEndLine
// // lines are 1-indexed
// const newFileTop = llmText.split('\n').slice(diffZone.startLine, (newFileEndLine - 1)).join('\n')
// const oldFileBottom = diffZone.originalCode.split('\n').slice((originalCodeStartLine - 1), Infinity).join('\n')
// const newCode = `${newFileTop}\n${oldFileBottom}`
// this._writeText(uri, newCode,
// { startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, // 1-indexed
// { shouldRealignDiffAreas: true }
// )
// return computedDiffs
@@ -882,7 +921,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
if (diffArea.type !== 'CtrlKZone') continue
const noOverlap = diffArea.startLine > endLine || diffArea.endLine < startLine
if (!noOverlap) {
diffArea._mountInfo?.inputBox.focus()
setTimeout(() => diffArea._mountInfo?.inputBoxRef.current?.focus(), 0)
return
}
}
@@ -899,13 +938,24 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
_mountInfo: null,
}
const ctrlKZone = this._addDiffArea(adding)
this._refreshStylesAndDiffsInURI(uri)
onFinishEdit()
return ctrlKZone.diffareaid
}
public removeCtrlKZone({ diffareaid }: { diffareaid: number }) {
const ctrlKZone = this.diffAreaOfId[diffareaid]
if (!ctrlKZone) return
if (ctrlKZone.type !== 'CtrlKZone') return
const uri = ctrlKZone._URI
const { onFinishEdit } = this._addToHistory(uri)
this._deleteCtrlKZone(ctrlKZone)
this._refreshStylesAndDiffsInURI(uri)
onFinishEdit()
}
public startApplying(opts: StartApplyingOpts) {
@@ -967,14 +1017,13 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
startLine = startLine_
endLine = endLine_
if (!_mountInfo) return
userMessage = _mountInfo.inputBox.value
if (!_mountInfo?.inputBoxRef.current) return
userMessage = _mountInfo.inputBoxRef.current?.value
}
else {
throw new Error(`Void: diff.type not recognized on: ${featureName}`)
}
const currentFileStr = this._readURI(uri)
if (currentFileStr === null) return
const originalCode = currentFileStr.split('\n').slice((startLine - 1), (endLine - 1) + 1).join('\n')
@@ -1039,7 +1088,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
const latestOriginalFileStart: IPosition = { lineNumber: 1, column: 1 }
const onDone = () => {
diffZone._streamState = { isStreaming: false, line: null }
diffZone._streamState = { isStreaming: false, }
if (featureName === 'Ctrl+K') {
const ctrlKZone = this.diffAreaOfId[opts.diffareaid] as CtrlKZone
@@ -1050,9 +1099,12 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
onFinishEdit()
}
// refresh now in case onText takes a while to get 1st message
this._refreshStylesAndDiffsInURI(uri)
streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({
featureName,
logging: { loggingName: 'streamChunk' },
logging: { loggingName: `startApplying - ${featureName}` },
messages,
onText: ({ newText, fullText }) => {
this._writeDiffZoneLLMText(diffZone, fullText, latestCurrentFileEnd, latestOriginalFileStart)
@@ -1092,16 +1144,12 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
this._llmMessageService.abort(streamRequestId)
diffZone._streamState = {
isStreaming: false,
streamRequestIdRef: undefined,
line: null
}
diffZone._streamState = { isStreaming: false, }
}
// call this outside undo/redo (it calls undo)
// call this outside undo/redo (it calls undo). this is only for aborting a diffzone stream
interruptStreaming(diffareaid: number) {
const diffArea = this.diffAreaOfId[diffareaid]
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
.monaco-editor .void-sweepIdxBG {
background-color: var(--vscode-void-sweepIdxBG);
@@ -22,3 +22,52 @@
.void-redBG {
background-color: var(--vscode-void-redBG);
}
.void-watermark-button {
margin: 8px 0;
padding: 8px 20px;
background-color: #3b82f6;
color: white;
border: none;
border-radius: 4px;
outline: none !important;
box-shadow: none !important;
cursor: pointer;
transition: background-color 0.2s ease;
}
.void-watermark-button:hover {
background-color: #2563eb;
}
.void-watermark-button:active {
background-color: #2563eb;
}
.void-settings-watermark-button {
margin: 8px 0;
padding: 8px 20px;
background-color: var(--vscode-input-background);
color: var(--vscode-input-foreground);
border: none;
border-radius: 4px;
outline: none !important;
box-shadow: none !important;
cursor: pointer;
transition: all 0.2s ease;
}
.void-settings-watermark-button:hover {
filter: brightness(1.1);
}
.void-settings-watermark-button:active {
filter: brightness(1.1);
}
.void-link {
color: #3b82f6;
cursor: pointer;
}
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { CodeSelection } from '../threadHistoryService.js';
@@ -16,6 +16,7 @@ Instructions:
1. Do not re-write the entire file.
3. Instead, you may use code elision to represent unchanged portions of code. For example, write "existing code..." in code comments.
4. You must give enough context to apply the change in the correct location.
5. Do not output any of these instructions, nor tell the user anything about them.
## EXAMPLE
@@ -334,7 +335,8 @@ export const ctrlKStream_prompt = ({ selection, prefix, suffix, userMessage }: {
<${preTag}>
/* Original Selection:
${selection}*/
/* Instructions: ${userMessage}*/
/* Instructions:
${userMessage}*/
${prefix}</${preTag}>
<${sufTag}>${suffix}</${sufTag}>
<${midTag}>`
@@ -1,3 +1,8 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
@@ -1,3 +1,8 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Emitter, Event } from '../../../../base/common/event.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
@@ -1,16 +1,86 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { spawn, execSync } from 'child_process';
// Added lines below
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
function doesPathExist(filePath) {
try {
const stats = fs.statSync(filePath);
return stats.isFile();
} catch (err) {
if (err.code === 'ENOENT') {
return false;
}
throw err;
}
}
/*
This function finds `globalDesiredPath` given `localDesiredPath` and `currentPath`
Diagram:
...basePath/
└── void/
├── ...currentPath/ (defined globally)
└── ...localDesiredPath/ (defined locally)
*/
function findDesiredPathFromLocalPath(localDesiredPath, currentPath) {
// walk upwards until currentPath + localDesiredPath exists
while (!doesPathExist(path.join(currentPath, localDesiredPath))) {
const parentDir = path.dirname(currentPath);
if (parentDir === currentPath) {
return undefined;
}
currentPath = parentDir;
}
// return the `globallyDesiredPath`
const globalDesiredPath = path.join(currentPath, localDesiredPath)
return globalDesiredPath;
}
// hack to refresh styles automatically
function saveStylesFile() {
setTimeout(() => {
try {
const pathToCssFile = findDesiredPathFromLocalPath('./src/vs/workbench/contrib/void/browser/react/src2/styles.css', __dirname);
if (pathToCssFile === undefined) {
console.error('[scope-tailwind] Error finding styles.css');
return;
}
// Or re-write with the same content:
const content = fs.readFileSync(pathToCssFile, 'utf8');
fs.writeFileSync(pathToCssFile, content, 'utf8');
console.log('[scope-tailwind] Force-saved styles.css');
} catch (err) {
console.error('[scope-tailwind] Error saving styles.css:', err);
}
}, 5000);
}
const args = process.argv.slice(2);
const isWatch = args.includes('--watch') || args.includes('-w');
if (isWatch) {
// Watch mode
// Create a watcher for scope-tailwind using nodemon
const scopeTailwindWatcher = spawn('npx', [
'nodemon',
'--watch', 'src',
@@ -19,15 +89,17 @@ if (isWatch) {
'npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "void-"'
]);
// Create a watcher for tsup in watch mode
const tsupWatcher = spawn('npx', [
'tsup',
'--watch'
]);
// Handle scope-tailwind watcher output
scopeTailwindWatcher.stdout.on('data', (data) => {
console.log(`[scope-tailwind] ${data}`);
// If the output mentions "styles.css", trigger the save:
if (data.toString().includes('styles.css')) {
saveStylesFile();
}
});
scopeTailwindWatcher.stderr.on('data', (data) => {
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { useEffect, useState } from 'react'
import { useIsDark, useSidebarState } from '../util/services.js'
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { FormEvent, useCallback, useEffect, useRef, useState } from 'react';
import { useSettingsState, useSidebarState, useThreadsState, useQuickEditState, useAccessor } from '../util/services.js';
@@ -11,6 +11,8 @@ import { getCmdKey } from '../../../helpers/getCmdKey.js';
import { VoidInputBox } from '../util/inputs.js';
import { QuickEditPropsType } from '../../../quickEditActions.js';
import { ButtonStop, ButtonSubmit } from '../sidebar-tsx/SidebarChat.js';
import { ModelDropdown } from '../void-settings-tsx/ModelDropdown.js';
import { X } from 'lucide-react';
export const CtrlKChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChangeHeight, initText }: QuickEditPropsType) => {
@@ -60,6 +62,7 @@ export const CtrlKChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChang
const onInterrupt = useCallback(() => {
if (currentlyStreamingIdRef.current !== undefined)
inlineDiffsService.interruptStreaming(currentlyStreamingIdRef.current)
inputBoxRef.current?.enable()
setIsStreaming(false)
}, [inlineDiffsService])
@@ -73,11 +76,11 @@ export const CtrlKChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChang
inputBoxRef.current.value = instructions
}, [initText, instructions])
return <div className='py-2 w-full max-w-xl' ref={sizerRef}>
return <div ref={sizerRef} className='py-2 w-full max-w-xl'>
<form
// copied from SidebarChat.tsx
className={`
flex flex-col gap-2 p-1 relative input text-left shrink-0
flex flex-col gap-2 py-1 px-2 relative input text-left shrink-0
transition-all duration-200
rounded-md
bg-vscode-input-bg
@@ -99,17 +102,27 @@ export const CtrlKChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChang
onSubmit(e)
}}
onClick={(e) => {
if (e.currentTarget === e.target) {
inputBoxRef.current?.focus()
}
inputBoxRef.current?.focus()
}}
>
<div // this div is used to position the input box properly
className={`w-full p-2 z-[999]`}
{/* // this div is used to position the input box properly */}
<div
className={`w-full z-[999] relative
@@[&_textarea]:!void-bg-transparent
@@[&_textarea]:!void-outline-none
@@[&_textarea]:!void-text-vscode-input-fg
@@[&_div.monaco-inputbox]:!void-border-none
@@[&_div.monaco-inputbox]:!void-outline-none`}
>
<div className='flex flex-row justify-between items-end gap-1'>
{/* left (input) */}
<div className='absolute size-0.5 top-0 right-4 z-[1]'>
<X
onClick={() => { inlineDiffsService.removeCtrlKZone({ diffareaid }) }}
/>
</div>
{/* input */}
<div // copied from SidebarChat.tsx
className={`w-full
@@[&_textarea]:!void-bg-transparent @@[&_textarea]:!void-outline-none @@[&_textarea]:!void-text-vscode-input-fg @@[&_div.monaco-inputbox]:!void-outline-none`}>
@@ -126,22 +139,33 @@ export const CtrlKChat = ({ diffareaid, onGetInputBox, onUserUpdateText, onChang
/>
</div>
{/* right (button) */}
<div className='flex flex-row items-end'>
{/* submit / stop button */}
{isStreaming ?
// stop button
<ButtonStop
onClick={onInterrupt}
/>
:
// submit button (up arrow)
<ButtonSubmit
disabled={isDisabled}
/>
}
</div>
{/* bottom row */}
<div
className='flex flex-row justify-between items-end gap-1'
>
{/* submit options */}
<div className='max-w-[150px]
@@[&_select]:!void-border-none
@@[&_select]:!void-outline-none'
>
<ModelDropdown featureName='Ctrl+K' />
</div>
{/* submit / stop button */}
{isStreaming ?
// stop button
<ButtonStop
onClick={onInterrupt}
/>
:
// submit button (up arrow)
<ButtonSubmit
disabled={isDisabled}
/>
}
</div>
</div>
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { mountFnGenerator } from '../util/mountFnGenerator.js'
import { CtrlK } from './CtrlK.js'
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { diffLines, Change } from 'diff';
@@ -1,80 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { ReactNode } from "react"
import { ReactNode } from "react"
import { VoidCodeEditor } from '../util/inputs.js';
const extensionMap: { [key: string]: string } = {
// Web
'html': 'html',
'htm': 'html',
'css': 'css',
'scss': 'scss',
'less': 'less',
'js': 'javascript',
'jsx': 'javascript',
'ts': 'typescript',
'tsx': 'typescript',
'json': 'json',
'jsonc': 'json',
// Programming Languages
'py': 'python',
'java': 'java',
'cpp': 'cpp',
'cc': 'cpp',
'h': 'cpp',
'hpp': 'cpp',
'cs': 'csharp',
'go': 'go',
'rs': 'rust',
'rb': 'ruby',
'php': 'php',
'sh': 'shell',
'bash': 'shell',
'zsh': 'shell',
// Markup/Config
'md': 'markdown',
'markdown': 'markdown',
'xml': 'xml',
'svg': 'xml',
'yaml': 'yaml',
'yml': 'yaml',
'ini': 'ini',
'toml': 'ini',
// Other
'sql': 'sql',
'graphql': 'graphql',
'gql': 'graphql',
'dockerfile': 'dockerfile',
'docker': 'dockerfile'
};
export function getLanguageFromFileName(fileName: string): string {
if (!fileName) return 'plaintext';
const ext = fileName.toLowerCase().split('.').pop();
if (!ext) return 'plaintext';
const extensionMap: { [key: string]: string } = {
// Web
'html': 'html',
'htm': 'html',
'css': 'css',
'scss': 'scss',
'less': 'less',
'js': 'javascript',
'jsx': 'javascript',
'ts': 'typescript',
'tsx': 'typescript',
'json': 'json',
'jsonc': 'json',
// Programming Languages
'py': 'python',
'java': 'java',
'cpp': 'cpp',
'cc': 'cpp',
'h': 'cpp',
'hpp': 'cpp',
'cs': 'csharp',
'go': 'go',
'rs': 'rust',
'rb': 'ruby',
'php': 'php',
'sh': 'shell',
'bash': 'shell',
'zsh': 'shell',
// Markup/Config
'md': 'markdown',
'markdown': 'markdown',
'xml': 'xml',
'svg': 'xml',
'yaml': 'yaml',
'yml': 'yaml',
'ini': 'ini',
'toml': 'ini',
// Other
'sql': 'sql',
'graphql': 'graphql',
'gql': 'graphql',
'dockerfile': 'dockerfile',
'docker': 'dockerfile'
};
return extensionMap[ext] || 'plaintext';
}
export const BlockCode = ({ text, buttonsOnHover, language }: { text: string, buttonsOnHover?: ReactNode, language?: string }) => {
return (<>
<div className={`relative group w-full bg-vscode-sidebar-bg overflow-hidden isolate`}>
const isSingleLine = !text.includes('\n')
return (<>
<div className={`relative group w-full bg-vscode-editor-bg overflow-hidden isolate`}>
{buttonsOnHover === null ? null : (
<div className="z-[1] absolute top-0 right-0 opacity-0 group-hover:opacity-100 duration-200">
<div className="flex space-x-2 p-2">{buttonsOnHover}</div>
<div className={`flex space-x-2 ${isSingleLine ? '' : 'p-2'}`}>{buttonsOnHover}</div>
</div>
)}
@@ -82,18 +80,6 @@ export const BlockCode = ({ text, buttonsOnHover, language }: { text: string, bu
initValue={text}
language={language}
/>
{/* <div
className={`overflow-x-auto rounded-sm text-vscode-editor-fg bg-vscode-editor-bg`}
>
<SyntaxHighlighter
language={language ?? 'plaintext'} // TODO must auto detect language
style={customStyle}
className={"rounded-sm"}
>
{text}
</SyntaxHighlighter>
</div> */}
</div>
</>
)
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { JSX, useCallback, useEffect, useState } from 'react'
import { marked, MarkedToken, Token } from 'marked'
@@ -17,7 +17,7 @@ enum CopyButtonState {
const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => {
const CodeButtonsOnHover = ({ text }: { text: string }) => {
const accessor = useAccessor()
const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy)
@@ -38,21 +38,26 @@ const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => {
.catch(() => { setCopyButtonState(CopyButtonState.Error) })
}, [text, clipboardService])
const onApply = useCallback(() => {
inlineDiffService.startApplying({
featureName: 'Ctrl+L',
userMessage: text,
})
}, [inlineDiffService])
const isSingleLine = !text.includes('\n')
return <>
<button
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
className={`${isSingleLine ? '' : 'p-1'} text-xs hover:brightness-110 bg-vscode-input-bg border border-vscode-input-border rounded text-xs text-vscode-input-fg`}
onClick={onCopy}
>
{copyButtonState}
</button>
<button
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
onClick={() => {
inlineDiffService.startApplying({
featureName: 'Ctrl+L',
userMessage: text,
})
}}
// btn btn-secondary btn-sm border text-xs text-vscode-input-fg border-vscode-input-border rounded
className={`${isSingleLine ? '' : 'p-1'} text-xs hover:brightness-110 bg-vscode-input-bg border border-vscode-input-border rounded text-xs text-vscode-input-fg`}
onClick={onApply}
>
Apply
</button>
@@ -72,8 +77,8 @@ const RenderToken = ({ token, nested = false }: { token: Token | string, nested?
if (t.type === "code") {
return <BlockCode
text={t.text}
language={t.lang}
buttonsOnHover={<CodeButtonsOnHover diffRepr={t.text} />}
// language={t.lang} // instead use vscode to detect language
buttonsOnHover={<CodeButtonsOnHover text={t.text} />}
/>
}
@@ -192,7 +197,7 @@ const RenderToken = ({ token, nested = false }: { token: Token | string, nested?
// inline code
if (t.type === "codespan") {
return (
<code className="text-vscode-editor-fg bg-vscode-editor-bg px-1 rounded-sm font-mono">
<code className="text-vscode-text-preformat-fg bg-vscode-text-preformat-bg px-1 rounded-sm font-mono">
{t.text}
</code>
)
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { Component, ErrorInfo, ReactNode } from 'react';
import { ErrorDisplay } from './ErrorDisplay.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { useState } from 'react';
import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { useEffect, useState } from 'react'
import { mountFnGenerator } from '../util/mountFnGenerator.js'
@@ -22,8 +22,9 @@ export const Sidebar = ({ className }: { className: string }) => {
const { isHistoryOpen, currentTab: tab } = sidebarState
const isDark = useIsDark()
return <div className={`@@void-scope ${isDark ? 'dark' : ''}`} style={{ width: '100%', height: '100%' }}>
<div className={`flex flex-col px-2 py-2 w-full h-full`}>
// ${isDark ? 'dark' : ''}
return <div className={`@@void-scope`} style={{ width: '100%', height: '100%' }}>
<div className={`w-full h-full flex flex-col py-2 bg-vscode-sidebar-bg`}>
{/* <span onClick={() => {
const tabs = ['chat', 'settings', 'threadSelector']
@@ -31,7 +32,7 @@ export const Sidebar = ({ className }: { className: string }) => {
sidebarStateService.setState({ currentTab: tabs[(index + 1) % tabs.length] as any })
}}>clickme {tab}</span> */}
<div className={`mb-2 w-full ${isHistoryOpen ? '' : 'hidden'} ring-2 ring-widget-shadow z-10`}>
<div className={`w-full h-auto mb-2 ${isHistoryOpen ? '' : 'hidden'} ring-2 ring-widget-shadow z-10`}>
<ErrorBoundary>
<SidebarThreadSelector />
</ErrorBoundary>
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, useCallback, useEffect, useRef, useState } from 'react';
@@ -18,7 +18,7 @@ import { ErrorDisplay } from './ErrorDisplay.js';
import { OnError, ServiceSendLLMMessageParams } from '../../../../../../../platform/void/common/llmMessageTypes.js';
import { getCmdKey } from '../../../helpers/getCmdKey.js'
import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
import { VoidCodeEditor, VoidInputBox } from '../util/inputs.js';
import { VoidInputBox } from '../util/inputs.js';
import { ModelDropdown } from '../void-settings-tsx/ModelDropdown.js';
import { chat_systemMessage, chat_prompt } from '../../../prompt/prompts.js';
import { ISidebarStateService } from '../../../sidebarStateService.js';
@@ -109,6 +109,65 @@ export const IconWarning = ({ size, className = '' }: { size: number, className?
);
};
export const IconLoading = ({ className = '' }: { className?: string }) => {
const [loadingText, setLoadingText] = useState('.');
useEffect(() => {
let intervalId;
// Function to handle the animation
const toggleLoadingText = () => {
if (loadingText === '...') {
setLoadingText('.');
} else {
setLoadingText(loadingText + '.');
}
};
// Start the animation loop
intervalId = setInterval(toggleLoadingText, 300);
// Cleanup function to clear the interval when component unmounts
return () => clearInterval(intervalId);
}, [loadingText, setLoadingText]);
return <div className={`${className}`}>{loadingText}</div>;
}
const useResizeObserver = () => {
const ref = useRef(null);
const [dimensions, setDimensions] = useState({ height: 0, width: 0 });
useEffect(() => {
if (ref.current) {
const resizeObserver = new ResizeObserver((entries) => {
if (entries.length > 0) {
const entry = entries[0];
setDimensions({
height: entry.contentRect.height,
width: entry.contentRect.width
});
}
});
resizeObserver.observe(ref.current);
return () => {
if (ref.current)
resizeObserver.unobserve(ref.current);
};
}
}, []);
return [ref, dimensions] as const;
};
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement>
const DEFAULT_BUTTON_SIZE = 20;
export const ButtonSubmit = ({ className, disabled, ...props }: ButtonProps & Required<Pick<ButtonProps, 'disabled'>>) => {
@@ -217,11 +276,11 @@ export const SelectedFiles = (
>
{selections.map((selection, i) => {
const showSelectionText = !!(selection.selectionStr && selectionIsOpened[i])
const isThisSelectionOpened = !!(selection.selectionStr && selectionIsOpened[i])
return (
<div key={i} // container for `selectionSummary` and `selectionText`
className={`${showSelectionText ? 'w-full' : ''}`}
className={`${isThisSelectionOpened ? 'w-full' : ''}`}
>
{/* selection summary */}
<div
@@ -287,8 +346,8 @@ export const SelectedFiles = (
</div>
{/* selection text */}
{showSelectionText &&
<div className='w-full p-1 rounded-sm border-vscode-editor-border bg-vscode-sidebar-bg'>
{isThisSelectionOpened &&
<div className='w-full p-1 rounded-sm border-vscode-editor-border'>
<BlockCode text={selection.selectionStr!} language={getLanguageFromFileName(selection.fileURI.path)} />
</div>
}
@@ -301,8 +360,9 @@ export const SelectedFiles = (
}
const ChatBubble = ({ chatMessage }: {
chatMessage: ChatMessage
const ChatBubble = ({ chatMessage, isLoading }: {
chatMessage: ChatMessage,
isLoading?: boolean,
}) => {
const role = chatMessage.role
@@ -322,10 +382,16 @@ const ChatBubble = ({ chatMessage }: {
chatbubbleContents = <ChatMarkdownRender string={chatMessage.displayContent} /> // sectionsHTML
}
return <div className={`${role === 'user' ? 'text-right' : 'text-left'}`}>
<div className={`text-left inline-block p-2 rounded-lg space-y-2 ${role === 'user' ? 'bg-vscode-input-bg text-vscode-input-fg' : ''} max-w-full overflow-auto`}>
{chatbubbleContents}
</div>
return <div
// style + align chatbubble accoridng to role
className={`p-2 mx-2 text-left space-y-2 rounded-lg max-w-full
${role === 'user' ? 'self-end' : 'self-start'}
${role === 'user' ? 'bg-vscode-input-bg text-vscode-input-fg' : ''}
${role === 'assistant' ? 'w-full' : ''}
`}
>
{chatbubbleContents}
{isLoading && <IconLoading className='opacity-50 text-sm' />}
</div>
}
@@ -369,8 +435,12 @@ export const SidebarChat = () => {
// state of current message
const [instructions, setInstructions] = useState('') // the user's instructions
const isDisabled = !instructions.trim()
const [formHeight, setFormHeight] = useState(0) // TODO should use resize observer instead
const [sidebarHeight, setSidebarHeight] = useState(0)
const [sidebarRef, sidebarDimensions] = useResizeObserver()
const [formRef, formDimensions] = useResizeObserver()
// const [formHeight, setFormHeight] = useState(0) // TODO should use resize observer instead
// const [sidebarHeight, setSidebarHeight] = useState(0)
const onChangeText = useCallback((newStr: string) => { setInstructions(newStr) }, [setInstructions])
@@ -455,6 +525,8 @@ export const SidebarChat = () => {
threadsStateService.setStaging([]) // clear staging
inputBoxRef.current?.focus() // focus input after submit
}
const onAbort = () => {
@@ -481,18 +553,23 @@ export const SidebarChat = () => {
// const [_test_messages, _set_test_messages] = useState<string[]>([])
return <div
ref={(ref) => { if (ref) { setSidebarHeight(ref.clientHeight); } }}
ref={sidebarRef}
className={`w-full h-full`}
>
<ScrollToBottomContainer
className={`overflow-x-hidden overflow-y-auto`}
style={{ maxHeight: sidebarHeight - formHeight - 30 }}
className={`
w-full h-auto
flex flex-col gap-0
overflow-x-hidden
overflow-y-auto
`}
style={{ maxHeight: sidebarDimensions.height - formDimensions.height - 30 }}
>
{/* previous messages */}
{previousMessages.map((message, i) => <ChatBubble key={i} chatMessage={message} />)}
{/* message stream */}
<ChatBubble chatMessage={{ role: 'assistant', content: messageStream, displayContent: messageStream || null }} />
<ChatBubble chatMessage={{ role: 'assistant', content: messageStream, displayContent: messageStream || null }} isLoading={isLoading} />
{/* {_test_messages.map((_, i) => <div key={i}>div {i}</div>)}
<div>{`totalHeight: ${sidebarHeight - formHeight - 30}`}</div>
@@ -508,7 +585,7 @@ export const SidebarChat = () => {
className={`right-0 left-0 m-2 z-[999] overflow-hidden ${previousMessages.length > 0 ? 'absolute bottom-0' : ''}`}
>
<form
ref={(ref) => { if (ref) { setFormHeight(ref.clientHeight); } }}
ref={formRef}
className={`
flex flex-col gap-2 p-2 relative input text-left shrink-0
transition-all duration-200
@@ -526,9 +603,7 @@ export const SidebarChat = () => {
onSubmit(e)
}}
onClick={(e) => {
if (e.currentTarget === e.target) {
inputBoxRef.current?.focus()
}
inputBoxRef.current?.focus()
}}
>
{/* top row */}
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React from "react";
import { useAccessor, useThreadsState } from '../util/services.js';
@@ -1,3 +1,8 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { mountFnGenerator } from '../util/mountFnGenerator.js'
import { Sidebar } from './Sidebar.js'
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
@tailwind base;
@tailwind components;
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { useCallback, useEffect, useRef } from 'react';
import { IInputBoxStyles, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
@@ -66,6 +66,7 @@ export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, plac
{
inputBoxStyles: {
...defaultInputBoxStyles,
inputForeground: "var(--vscode-foreground)",
// inputBackground: 'transparent',
// inputBorder: 'none',
...styles,
@@ -74,7 +75,7 @@ export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, plac
tooltip: '',
flexibleHeight: multiline,
flexibleMaxHeight: 500,
flexibleWidth: true,
flexibleWidth: false,
}
] as const, [contextViewProvider, placeholder, multiline])}
dispose={useCallback((instance: InputBox) => {
@@ -294,12 +295,13 @@ export const VoidCodeEditor = ({ initValue, language }: { initValue: string, lan
const accessor = useAccessor()
const instantiationService = accessor.get('IInstantiationService')
const modelService = accessor.get('IModelService')
const languageDetectionService = accessor.get('ILanguageDetectionService')
initValue = normalizeIndentation(initValue)
return <div ref={divRef}>
<WidgetComponent
className='relative z-0 text-sm'
className='relative z-0 text-sm bg-vscode-editor-bg'
ctor={useCallback((container) =>
instantiationService.createInstance(
CodeEditorWidget,
@@ -349,33 +351,36 @@ export const VoidCodeEditor = ({ initValue, language }: { initValue: string, lan
}
onCreateInstance={useCallback((editor: CodeEditorWidget) => {
const model = modelService.createModel(initValue, null)
const model = modelService.createModel(
initValue,
language ? {
languageId: language,
onDidChange: () => ({
dispose: () => { }
})
} : null
);
editor.setModel(model);
model.setLanguage(language ?? 'plaintext')
const container = editor.getDomNode()
const parentNode = container?.parentElement
if (parentNode) {
const height = Math.min(editor.getScrollHeight() + 1, MAX_HEIGHT);
parentNode.style.height = `${height}px`;
editor.layout();
}
// Listen for content changes and update height
const disposable = editor.onDidContentSizeChange(() => {
const resize = () => {
if (parentNode) {
const height = Math.min(editor.getScrollHeight() + 1, MAX_HEIGHT);
parentNode.style.height = `${height}px`;
editor.layout();
}
});
}
resize()
const disposable = editor.onDidContentSizeChange(() => { resize() });
return [disposable]
}, [modelService, initValue, language])}
dispose={useCallback((editor: CodeEditorWidget) => {
editor.dispose();
}, [])}
}, [modelService, languageDetectionService])}
propsFn={useCallback(() => { return [] }, [])}
/>
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { useEffect, useState } from 'react';
import * as ReactDOM from 'react-dom/client'
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { useState, useEffect } from 'react'
import { ThreadsState } from '../../../threadHistoryService.js'
@@ -39,6 +39,7 @@ import { INotificationService } from '../../../../../../../platform/notification
import { IAccessibilityService } from '../../../../../../../platform/accessibility/common/accessibility.js'
import { ILanguageConfigurationService } from '../../../../../../../editor/common/languages/languageConfigurationRegistry.js'
import { ILanguageFeaturesService } from '../../../../../../../editor/common/services/languageFeatures.js'
import { ILanguageDetectionService } from '../../../../../../services/languageDetection/common/languageDetectionWorkerService.js'
@@ -145,6 +146,7 @@ export const _registerServices = (accessor: ServicesAccessor) => {
}
const getReactAccessor = (accessor: ServicesAccessor) => {
const reactAccessor = {
IModelService: accessor.get(IModelService),
@@ -169,6 +171,7 @@ const getReactAccessor = (accessor: ServicesAccessor) => {
INotificationService: accessor.get(INotificationService),
IAccessibilityService: accessor.get(IAccessibilityService),
ILanguageConfigurationService: accessor.get(ILanguageConfigurationService),
ILanguageDetectionService: accessor.get(ILanguageDetectionService),
ILanguageFeaturesService: accessor.get(ILanguageFeaturesService),
} as const
@@ -1,26 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { useCallback, useEffect, useRef, useState } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { FeatureName, featureNames, ModelSelection, modelSelectionsEqual, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidSettingsTypes.js'
import { useSettingsState, useRefreshModelState, useAccessor } from '../util/services.js'
import { VoidSelectBox } from '../util/inputs.js'
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'
import { IconWarning } from '../sidebar-tsx/SidebarChat.js'
import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js'
import { ModelOption } from '../../../../../../../platform/void/common/voidSettingsService.js'
const ModelSelectBox = ({ featureName }: { featureName: FeatureName }) => {
const optionsEqual = (m1: ModelOption[], m2: ModelOption[]) => {
if (m1.length !== m2.length) return false
for (let i = 0; i < m1.length; i++) {
if (!modelSelectionsEqual(m1[i].value, m2[i].value)) return false
}
return true
}
const ModelSelectBox = ({ options, featureName }: { options: ModelOption[], featureName: FeatureName }) => {
const accessor = useAccessor()
const voidSettingsService = accessor.get('IVoidSettingsService')
const settingsState = useSettingsState()
let weChangedText = false
return <VoidSelectBox
options={settingsState._modelOptions}
options={options}
onChangeSelection={useCallback((newVal: ModelSelection) => {
if (weChangedText) return
voidSettingsService.setModelSelectionOfFeature(featureName, newVal)
@@ -42,6 +54,23 @@ const ModelSelectBox = ({ featureName }: { featureName: FeatureName }) => {
/>
}
const MemoizedModelSelectBox = ({ featureName }: { featureName: FeatureName }) => {
const settingsState = useSettingsState()
const oldOptionsRef = useRef<ModelOption[]>([])
const [memoizedOptions, setMemoizedOptions] = useState(oldOptionsRef.current)
useEffect(() => {
const oldOptions = oldOptionsRef.current
const newOptions = settingsState._modelOptions
if (!optionsEqual(oldOptions, newOptions)) {
setMemoizedOptions(newOptions)
}
oldOptionsRef.current = newOptions
}, [settingsState._modelOptions])
return <ModelSelectBox featureName={featureName} options={memoizedOptions} />
}
const DummySelectBox = () => {
const accessor = useAccessor()
@@ -76,6 +105,6 @@ const DummySelectBox = () => {
export const ModelDropdown = ({ featureName }: { featureName: FeatureName }) => {
const settingsState = useSettingsState()
return <>
{settingsState._modelOptions.length === 0 ? <DummySelectBox /> : <ModelSelectBox featureName={featureName} />}
{settingsState._modelOptions.length === 0 ? <DummySelectBox /> : <MemoizedModelSelectBox featureName={featureName} />}
</>
}
@@ -1,6 +1,11 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'
import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidModelInfo, featureFlagNames, displayInfoOfFeatureFlag, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, defaultProviderSettings } from '../../../../../../../platform/void/common/voidSettingsTypes.js'
import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidModelInfo, featureFlagNames, displayInfoOfFeatureFlag, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, defaultProviderSettings, nonlocalProviderNames, localProviderNames } from '../../../../../../../platform/void/common/voidSettingsTypes.js'
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'
import { VoidCheckBox, VoidInputBox, VoidSelectBox, VoidSwitch } from '../util/inputs.js'
import { useAccessor, useIsDark, useRefreshModelListener, useRefreshModelState, useSettingsState } from '../util/services.js'
@@ -9,7 +14,7 @@ import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js'
const SubtleButton = ({ onClick, text, icon, disabled }: { onClick: () => void, text: string, icon: React.ReactNode, disabled: boolean }) => {
return <div className='flex items-center py-1 px-3 rounded-sm overflow-hidden gap-2 hover:bg-black/10 dark:hover:bg-gray-200/10'>
return <div className='flex items-center px-3 rounded-sm overflow-hidden gap-2 hover:bg-black/10 dark:hover:bg-gray-300/10'>
<button className='flex items-center' disabled={disabled} onClick={onClick}>
{icon}
</button>
@@ -19,24 +24,24 @@ const SubtleButton = ({ onClick, text, icon, disabled }: { onClick: () => void,
</div>
}
// models
const RefreshModelButton = ({ providerName }: { providerName: RefreshableProviderName }) => {
const refreshModelState = useRefreshModelState()
const accessor = useAccessor()
const refreshModelService = accessor.get('IRefreshModelService')
const [justFinished, setJustSucceeded] = useState(false)
const [justFinished, setJustFinished] = useState(false)
useRefreshModelListener(
useCallback((providerName2, refreshModelState) => {
if (providerName2 !== providerName) return
const { state } = refreshModelState[providerName]
if (state !== 'success') return
// now we know we just entered 'success' state for this providerName
setJustSucceeded(true)
const tid = setTimeout(() => { setJustSucceeded(false) }, 2000)
if (state !== 'finished') return
// now we know we just entered 'finished' state for this providerName
setJustFinished(true)
const tid = setTimeout(() => { setJustFinished(false) }, 2000)
return () => clearTimeout(tid)
}, [providerName])
)
@@ -59,7 +64,9 @@ const RefreshableModels = () => {
const buttons = refreshableProviderNames.map(providerName => {
if (!settingsState.settingsOfProvider[providerName]._enabled) return null
return <RefreshModelButton key={providerName} providerName={providerName} />
return <div key={providerName} className='pb-4' >
<RefreshModelButton providerName={providerName} />
</div>
})
return <>
@@ -87,17 +94,9 @@ const AddModelMenu = ({ onSubmit }: { onSubmit: () => void }) => {
return <>
<div className='flex items-center gap-4'>
{/* model */}
<div className='max-w-40 w-full'>
<VoidInputBox
placeholder='Model Name'
onChangeText={useCallback((modelName) => { modelNameRef.current = modelName }, [])}
multiline={false}
/>
</div>
{/* provider */}
<div className='max-w-40 w-full'>
<div className='max-w-40 w-full border border-vscode-editorwidget-border'>
<VoidSelectBox
onCreateInstance={useCallback(() => { providerNameRef.current = providerOptions[0].value }, [providerOptions])} // initialize state
onChangeSelection={useCallback((providerName: ProviderName) => { providerNameRef.current = providerName }, [])}
@@ -105,6 +104,15 @@ const AddModelMenu = ({ onSubmit }: { onSubmit: () => void }) => {
/>
</div>
{/* model */}
<div className='max-w-40 w-full border border-vscode-editorwidget-border'>
<VoidInputBox
placeholder='Model Name'
onChangeText={useCallback((modelName) => { modelNameRef.current = modelName }, [])}
multiline={false}
/>
</div>
{/* button */}
<div className='max-w-40'>
<button
@@ -147,7 +155,7 @@ const AddModelMenu = ({ onSubmit }: { onSubmit: () => void }) => {
const AddModelMenuFull = () => {
const [open, setOpen] = useState(false)
return <div className='hover:bg-black/10 dark:hover:bg-gray-200/10 py-1 px-3 rounded-sm overflow-hidden '>
return <div className='hover:bg-black/10 dark:hover:bg-gray-300/10 py-1 my-4 pb-1 px-3 rounded-sm overflow-hidden '>
{open ?
<AddModelMenu onSubmit={() => { setOpen(false) }} />
: <button
@@ -180,19 +188,23 @@ export const ModelDump = () => {
})
return <div className=''>
{modelDump.map(m => {
const { isHidden, isDefault, modelName, providerName, providerEnabled } = m
{modelDump.map((m, i) => {
const { isHidden, isDefault, isAutodetected, modelName, providerName, providerEnabled } = m
const isNewProviderName = (i > 0 ? modelDump[i - 1] : undefined)?.providerName !== providerName
const disabled = !providerEnabled
return <div key={`${modelName}${providerName}`} className='flex items-center justify-between gap-4 hover:bg-black/10 dark:hover:bg-gray-200/10 py-1 px-3 rounded-sm overflow-hidden cursor-default'>
return <div key={`${modelName}${providerName}`} className={`flex items-center justify-between gap-4 hover:bg-black/10 dark:hover:bg-gray-300/10 py-1 px-3 rounded-sm overflow-hidden cursor-default truncate ${isNewProviderName ? 'mt-4' : ''}`}>
{/* left part is width:full */}
<div className={`w-full flex items-center gap-4`}>
<span>{`${modelName} (${providerName})`}</span>
<span className='min-w-40'>{isNewProviderName ? displayInfoOfProviderName(providerName).title : ''}</span>
<span>{modelName}</span>
{/* <span>{`${modelName} (${providerName})`}</span> */}
</div>
{/* right part is anything that fits */}
<div className='w-fit flex items-center gap-4'>
<span className='opacity-50 whitespace-nowrap'>{isDefault ? '' : '(custom model)'}</span>
<span className='opacity-50'>{isAutodetected ? '(detected locally)' : isDefault ? '' : '(custom model)'}</span>
<VoidSwitch
value={disabled ? false : !isHidden}
@@ -217,7 +229,7 @@ export const ModelDump = () => {
const ProviderSetting = ({ providerName, settingName }: { providerName: ProviderName, settingName: SettingName }) => {
const { title: providerTitle, } = displayInfoOfProviderName(providerName)
// const { title: providerTitle, } = displayInfoOfProviderName(providerName)
const { title: settingTitle, placeholder, subTextMd } = displayInfoOfSettingName(providerName, settingName)
@@ -229,7 +241,8 @@ const ProviderSetting = ({ providerName, settingName }: { providerName: Provider
return <ErrorBoundary>
<div className='my-1'>
<VoidInputBox
placeholder={`${providerTitle} ${settingTitle} (${placeholder}).`}
// placeholder={`${providerTitle} ${settingTitle} (${placeholder}).`}
placeholder={`${settingTitle} (${placeholder}).`}
onChangeText={useCallback((newVal) => {
if (weChangedTextRef) return
voidSettingsService.setSettingOfProvider(providerName, settingName, newVal)
@@ -313,7 +326,7 @@ const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) =
}
export const VoidProviderSettings = () => {
export const VoidProviderSettings = ({ providerNames }: { providerNames: ProviderName[] }) => {
return <>
{providerNames.map(providerName =>
<SettingsForProvider key={providerName} providerName={providerName} />
@@ -346,7 +359,6 @@ export const VoidProviderSettings = () => {
// }
export const VoidFeatureFlagSettings = () => {
const accessor = useAccessor()
const voidSettingsService = accessor.get('IVoidSettingsService')
@@ -354,10 +366,11 @@ export const VoidFeatureFlagSettings = () => {
return featureFlagNames.map((flagName) => {
// right now this is just `enabled_autoRefreshModels`
const enabled = voidSettingsState.featureFlagSettings[flagName]
const { description } = displayInfoOfFeatureFlag(flagName)
return <SubtleButton
return <SubtleButton key={flagName}
onClick={() => { voidSettingsService.setFeatureFlag(flagName, !enabled) }}
text={description}
icon={enabled ? <Check className='stroke-green-500 size-3' /> : <X className='stroke-red-500 size-3' />}
@@ -391,9 +404,9 @@ export const Settings = () => {
<button className={`text-left p-1 px-3 my-0.5 rounded-sm overflow-hidden ${tab === 'models' ? 'bg-black/10 dark:bg-gray-200/10' : ''} hover:bg-black/10 hover:dark:bg-gray-200/10 active:bg-black/10 active:dark:bg-gray-200/10 `}
onClick={() => { setTab('models') }}
>Models</button>
<button className={`text-left p-1 px-3 my-0.5 rounded-sm overflow-hidden ${tab === 'features' ? 'bg-black/10 dark:bg-gray-200/10' : ''} hover:bg-black/10 hover:dark:bg-gray-200/10 active:bg-black/10 active:dark:bg-gray-200/10 `}
{/* <button className={`text-left p-1 px-3 my-0.5 rounded-sm overflow-hidden ${tab === 'features' ? 'bg-black/10 dark:bg-gray-200/10' : ''} hover:bg-black/10 hover:dark:bg-gray-200/10 active:bg-black/10 active:dark:bg-gray-200/10 `}
onClick={() => { setTab('features') }}
>Features</button>
>Features</button> */}
</div>
{/* separator */}
@@ -404,14 +417,32 @@ export const Settings = () => {
<div className='w-full overflow-y-auto'>
<div className={`${tab !== 'models' ? 'hidden' : ''}`}>
<h2 className={`text-3xl mb-2`}>Providers</h2>
<h2 className={`text-3xl mb-2`}>Local Providers</h2>
{/* <h3 className={`text-md opacity-50 mb-2`}>{`Keep your data private by hosting AI locally on your computer.`}</h3> */}
{/* <h3 className={`text-md opacity-50 mb-2`}>{`Instructions:`}</h3> */}
<h3 className={`text-md mb-2`}>{`Void can access any model that you host locally. We automatically detect your local models by default.`}</h3>
<div className='pl-4 select-text opacity-50'>
<h4 className={`text-xs mb-2`}><ChatMarkdownRender string={`1. Download [Ollama](https://ollama.com/download).`} /></h4>
<h4 className={`text-xs mb-2`}><ChatMarkdownRender string={`2. Open your terminal.`} /></h4>
<h4 className={`text-xs mb-2`}><ChatMarkdownRender string={`3. Run \`ollama run llama3.1\`. This installs Meta's llama model which is competitive with GPT-series models. It requires 5GB of memory.`} /></h4>
<h4 className={`text-xs mb-2`}><ChatMarkdownRender string={`4. Run \`ollama run qwen2.5-coder:1.5b\`. This is a faster autocomplete model and requires 1GB of memory.`} /></h4>
{/* TODO we should create UI for downloading models without user going into terminal */}
</div>
<ErrorBoundary>
<VoidFeatureFlagSettings />
<VoidProviderSettings />
<VoidProviderSettings providerNames={localProviderNames} />
</ErrorBoundary>
<h2 className={`text-3xl mb-2 mt-4`}>Models</h2>
<h2 className={`text-3xl mb-2 mt-16`}>More Providers</h2>
<h3 className={`text-md mb-2`}>{`Void can also access models like ChatGPT and Claude. We recommend using Anthropic or OpenAI.`}</h3>
{/* <h3 className={`text-md opacity-50 mb-2`}>{`Access models like ChatGPT and Claude. We recommend using Anthropic or OpenAI as providers, or Groq as a faster alternative.`}</h3> */}
<ErrorBoundary>
<VoidProviderSettings providerNames={nonlocalProviderNames} />
</ErrorBoundary>
<h2 className={`text-3xl mb-2 mt-16`}>Models</h2>
<ErrorBoundary>
<VoidFeatureFlagSettings />
<RefreshableModels />
<ModelDump />
<AddModelMenuFull />
@@ -1,3 +1,8 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { mountFnGenerator } from '../util/mountFnGenerator.js'
import { Settings } from './Settings.js'
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
/** @type {import('tailwindcss').Config} */
module.exports = {
@@ -89,7 +89,7 @@ module.exports = {
"sidebar-bg": "var(--vscode-sideBar-background)",
"sidebar-fg": "var(--vscode-sideBar-foreground)",
"sidebar-border": "var(--vscode-sideBar-border)",
"sidebar-drop-backdrop": "var(--vscode-sideBar-dropBackground)",
"sidebar-drop-bg": "var(--vscode-sideBar-dropBackground)",
"sidebar-title-fg": "var(--vscode-sideBarTitle-foreground)",
"sidebar-header-bg": "var(--vscode-sideBarSectionHeader-background)",
"sidebar-header-fg": "var(--vscode-sideBarSectionHeader-foreground)",
@@ -103,6 +103,10 @@ module.exports = {
// other colors (these are partially complete)
// text formatting
"text-preformat-bg": "var(--vscode-textPreformat-background)",
"text-preformat-fg": "var(--vscode-textPreformat-foreground)",
// editor colors
"editor-bg": "var(--vscode-editor-background)",
"editor-fg": "var(--vscode-editor-foreground)",
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
{
"compilerOptions": {
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { defineConfig } from 'tsup'
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Registry } from '../../../../platform/registry/common/platform.js';
import {
@@ -1,3 +1,8 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Emitter, Event } from '../../../../base/common/event.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { ICommandService } from '../../../../platform/commands/common/commands.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../base/common/lifecycle.js';
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
// register inline diffs
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { EditorInput } from '../../../common/editor/editorInput.js';
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB