Compare commits

..

640 Commits

Author SHA1 Message Date
Andrew Pareles a0fa104d23 Merge pull request #401 from voideditor/model-selection
Model selection
2025-04-15 02:34:33 -07:00
Andrew Pareles 7fd0b133e5 misc prompt improvements 2025-04-15 02:30:37 -07:00
Andrew Pareles 321876932a loading 2025-04-15 01:51:59 -07:00
Andrew Pareles eda8517c78 Merge pull request #400 from voideditor/model-selection
GPT 4.1 and Claude 3.7 Thinking with Tools
2025-04-14 23:52:58 -07:00
Andrew Pareles 6ce37f7627 misc 2025-04-14 23:47:14 -07:00
Andrew Pareles 18b76885f5 1.2.1 2025-04-14 23:17:33 -07:00
Andrew Pareles ba8644fbb6 read_file improvements 2025-04-14 23:12:10 -07:00
Andrew Pareles ee959ded08 4.1 and 3.7 work! 2025-04-14 22:57:29 -07:00
Mathew Pareles 499a47904e style 2025-04-14 22:53:39 -07:00
Mathew Pareles 5ac03bb917 fix 2025-04-14 22:47:10 -07:00
Mathew Pareles 922f3ba2d7 fix current file 2025-04-14 22:46:54 -07:00
Mathew Pareles a189629341 fix current file 2025-04-14 22:46:15 -07:00
Andrew Pareles d2098f71eb support 4.1 native tools and 3.7 native thinking... 2025-04-14 22:01:46 -07:00
Mathew Pareles 77c59f2e80 lint error severity must be warning or error 2025-04-14 14:41:48 -07:00
Mathew Pareles e6ecad3f65 desc 2025-04-14 14:34:04 -07:00
Mathew Pareles ee43ec99ea duplicate tooltips 2025-04-14 13:59:32 -07:00
Andrew Pareles e77039e9ad Merge pull request #399 from voideditor/model-selection
1.2.0
2025-04-14 13:38:12 -07:00
Andrew Pareles 6aa1375067 1.2.0 2025-04-14 13:36:43 -07:00
Andrew Pareles fe017948c6 Merge pull request #398 from voideditor/model-selection
GPT 4.1 + fixes
2025-04-14 13:34:33 -07:00
Andrew Pareles 9b3d4cbdba lint errors 2025-04-14 13:33:49 -07:00
Andrew Pareles a8cf0b361f prompt 2025-04-14 13:29:13 -07:00
Andrew Pareles 680125baec hot-swap out default models on mount 2025-04-14 12:48:44 -07:00
Andrew Pareles 0db4f7118d Merge remote-tracking branch 'origin/main' into model-selection 2025-04-14 12:31:49 -07:00
Andrew Pareles 2ea20243a8 lint error UI + misc 2025-04-14 12:31:31 -07:00
Andrew Pareles 4d02381dab Merge pull request #321 from jcommaret/feat_mistral-199
[Provider]Mistral -😻- Mistral working : [OpenAICompatibleModel Chat + tools +  sendMistralFIM]
2025-04-14 12:03:53 -07:00
Andrew Pareles 6990216fbe api 2025-04-14 12:03:37 -07:00
Andrew Pareles f5d68d9348 add back comment 2025-04-14 12:02:48 -07:00
Andrew Pareles 165c5aac24 fixes 2025-04-14 11:59:22 -07:00
Andrew Pareles 22f4a75c00 Merge branch 'main' into pr/jcommaret/321 2025-04-14 11:59:14 -07:00
Andrew Pareles 06513168c1 mistral corrections 2025-04-14 11:51:42 -07:00
Andrew Pareles a80883c10f 4.1 fallback for OSS providers 2025-04-14 11:10:52 -07:00
Andrew Pareles 20b0fd6af9 add 4.1 2025-04-14 10:57:26 -07:00
Andrew Pareles 5171f7244f Merge pull request #397 from voideditor/model-selection
Misc
2025-04-14 01:28:56 -07:00
Andrew Pareles 43708d25e7 1.1.0 2025-04-14 01:28:37 -07:00
Andrew Pareles 2b54733242 disable onboarding 2025-04-13 22:07:30 -07:00
Andrew Pareles cd77d1a1c4 Update README.md 2025-04-13 20:38:46 -07:00
Andrew Pareles 37af09add8 message 2025-04-13 12:04:17 -07:00
Andrew Pareles ade1e70536 Merge pull request #395 from voideditor/model-selection
minor fixes
2025-04-12 20:16:39 -07:00
Andrew Pareles 159e0b677f minor fixes 2025-04-12 20:16:20 -07:00
Andrew Pareles 335ebe1e11 Merge pull request #394 from voideditor/model-selection
Merge remote-tracking branch 'origin/main' into model-selection
2025-04-12 14:08:52 -07:00
Andrew Pareles 7be1ae5003 Merge remote-tracking branch 'origin/main' into model-selection 2025-04-12 14:07:28 -07:00
Andrew Pareles 5d7f8fc91f Merge pull request #393 from voideditor/model-selection
Quasar + misc fixes before 1.0.3
2025-04-12 14:07:11 -07:00
Andrew Pareles d6b3bdee35 interrupt fallback 2025-04-12 14:06:05 -07:00
Andrew Pareles 8c1626f79b quasar 2025-04-12 14:06:05 -07:00
Andrew Pareles 97ffe0b4cc read lint errors tool 2025-04-12 14:06:05 -07:00
Mathew Pareles 9cf316f808 tooltip 2025-04-12 13:55:46 -07:00
Mathew Pareles aabacba2a9 messaging 2025-04-12 13:52:28 -07:00
Mathew Pareles 39ccb1bbbd msg 2025-04-12 13:51:01 -07:00
Mathew Pareles 63560308b5 lint errors more manageable 2025-04-12 13:48:12 -07:00
Mathew Pareles 967aff2d19 placeholder for tools 2025-04-12 13:26:23 -07:00
Mathew Pareles 50327a238e add onboarding working draft (need to add images + enable but basically done) 2025-04-12 13:22:13 -07:00
Andrew Pareles 8eb5abe429 Merge pull request #392 from voideditor/model-selection
prompt
2025-04-11 14:31:56 -07:00
Andrew Pareles 3c532478de prompt 2025-04-11 14:31:10 -07:00
Andrew Pareles 5d5839736e Merge pull request #391 from voideditor/model-selection
Prepare for 1.0.3 release
2025-04-11 14:26:19 -07:00
Andrew Pareles b8fd40e735 enable auto updates! 2025-04-11 14:25:40 -07:00
Andrew Pareles a869ddb6f3 rm tools from capabilities 2025-04-11 13:22:33 -07:00
Andrew Pareles e5a40e3d35 edit streams in 2025-04-11 13:17:31 -07:00
Andrew Pareles 1081b8879f Merge pull request #389 from voideditor/model-selection
Misc 1.0.3 canary improvements - tool call fix, tooltips, lint error passing
2025-04-11 03:34:29 -07:00
Andrew Pareles 4efc5209ab fix 2025-04-11 03:34:15 -07:00
Andrew Pareles adcdeed094 Merge remote-tracking branch 'origin/main' into model-selection 2025-04-11 03:33:30 -07:00
Andrew Pareles 86285e2393 dirStr limits 2025-04-11 03:31:18 -07:00
Andrew Pareles c1b6b2e01d tooltips 2025-04-11 03:31:18 -07:00
Andrew Pareles 2dcdd78316 fix extractGrammar and lots of misc improvements 2025-04-11 03:31:18 -07:00
Andrew Pareles fa9a3db86f fix extractCode 2025-04-11 03:31:18 -07:00
Mathew Pareles 4445ff28ce missing comment 2025-04-10 01:18:25 -07:00
Mathew Pareles 9d6a4c4648 ux 2025-04-10 01:09:51 -07:00
Mathew Pareles e41f8790b5 gradient 2025-04-10 00:07:14 -07:00
Andrew Pareles d363edb1a8 Update VOID_CODEBASE_GUIDE.md 2025-04-10 00:02:24 -07:00
Mathew Pareles 3cddc5d5cc spacing 2025-04-09 23:56:25 -07:00
Mathew Pareles 2509290ab2 spacing 2025-04-09 23:55:43 -07:00
Mathew Pareles aba49edfdd add a model back 2025-04-09 23:40:58 -07:00
Mathew Pareles 13b60f7ee3 auxbar size 2025-04-09 23:25:41 -07:00
Mathew Pareles bd5cf464c2 lint error ui 2025-04-09 22:33:17 -07:00
Andrew Pareles 44e7d10ece Update HOW_TO_CONTRIBUTE.md 2025-04-09 21:51:07 -07:00
Andrew Pareles 6bf79b27c0 Dummy service update (#387)
* +

* +

* +

* +
2025-04-09 21:49:07 -07:00
Andrew Pareles bb22aefb6a Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:39:23 -07:00
Andrew Pareles 7002f320d6 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:35:53 -07:00
Andrew Pareles def8803919 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:33:30 -07:00
Andrew Pareles fdb21a68b8 Update README.md 2025-04-09 21:32:54 -07:00
Andrew Pareles 455acc3490 Update README.md 2025-04-09 21:32:38 -07:00
Andrew Pareles bc5b07b26f Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:31:10 -07:00
Andrew Pareles 79ad57b112 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:30:59 -07:00
Andrew Pareles 9391dcb0b8 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:29:01 -07:00
Andrew Pareles b90716e38e Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:25:06 -07:00
Andrew Pareles d8d21b0f25 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:24:53 -07:00
Andrew Pareles 3bf06bfe26 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:24:15 -07:00
Andrew Pareles 9166fa8684 Update README.md 2025-04-09 21:23:29 -07:00
Andrew Pareles df2165403e Update README.md 2025-04-09 21:23:19 -07:00
Andrew Pareles 44e0f0dfc7 Update README.md 2025-04-09 21:22:55 -07:00
Andrew Pareles d6e496c396 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:21:20 -07:00
Andrew Pareles 7c94b95df4 Update README.md 2025-04-09 21:18:35 -07:00
Andrew Pareles 85759eeb4a Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:17:15 -07:00
Andrew Pareles 6230971dff Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:16:33 -07:00
Andrew Pareles 8f08e40819 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:15:52 -07:00
Andrew Pareles ae635f0102 Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:15:29 -07:00
Andrew Pareles cb8cc587f6 + 2025-04-09 21:13:25 -07:00
Andrew Pareles 77f931c923 + 2025-04-09 21:12:10 -07:00
Andrew Pareles 8dcb5d35df Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:11:46 -07:00
Andrew Pareles 6483dcffa8 + 2025-04-09 21:11:40 -07:00
Andrew Pareles ccaf6e2e3d Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:10:57 -07:00
Andrew Pareles d5ae1a328c Update VOID_CODEBASE_GUIDE.md 2025-04-09 21:10:24 -07:00
Andrew Pareles 45c83ee661 + 2025-04-09 20:58:41 -07:00
Andrew Pareles af7dbb4b70 Update HOW_TO_CONTRIBUTE.md 2025-04-09 20:53:52 -07:00
Andrew Pareles 72a6daaa58 Update VOID_CODEBASE_GUIDE.md 2025-04-09 20:52:24 -07:00
Andrew Pareles 898a5803dd Update VOID_CODEBASE_GUIDE.md 2025-04-09 20:45:49 -07:00
Andrew Pareles cffc6f6db9 Update VOID_CODEBASE_GUIDE.md 2025-04-09 20:45:36 -07:00
Andrew Pareles b2ba634cc4 Update HOW_TO_CONTRIBUTE.md 2025-04-09 20:05:55 -07:00
Andrew Pareles c62d6a6790 Update HOW_TO_CONTRIBUTE.md 2025-04-09 20:05:17 -07:00
Andrew Pareles f8bee1d4a2 Update HOW_TO_CONTRIBUTE.md 2025-04-09 20:05:01 -07:00
Andrew Pareles 41e66a73c4 Update and rename VOID_USEFUL_LINKS.md to VOID_CODEBASE_GUIDE.md 2025-04-09 20:01:55 -07:00
Andrew Pareles 5da0e4821f Merge pull request #386 from voideditor/model-selection
HOW_TO_CONTRIBUTE
2025-04-09 18:46:47 -07:00
Andrew Pareles 6aefdc3095 HOW_TO_CONTRIBUTE 2025-04-09 18:46:25 -07:00
Andrew Pareles 4055dc7775 log 2025-04-09 17:08:05 -07:00
Andrew Pareles f48889627a Merge pull request #385 from voideditor/model-selection
logs
2025-04-09 17:02:12 -07:00
Andrew Pareles 3c59428fe8 logs 2025-04-09 17:01:42 -07:00
Andrew Pareles 8a8a982d21 Merge pull request #384 from voideditor/model-selection
Model selection
2025-04-09 16:57:59 -07:00
Andrew Pareles 7f890b946d add logs 2025-04-09 16:57:35 -07:00
Andrew Pareles 1799ae9d84 Merge remote-tracking branch 'origin/main' into model-selection 2025-04-09 16:47:28 -07:00
Andrew Pareles c7bdd2d64c copilot head 2025-04-09 16:22:38 -07:00
Andrew Pareles 5fde9194af rm voidimage bug 2025-04-09 08:45:38 -07:00
Andrew Pareles b6408512d3 Merge pull request #381 from voideditor/tools-in-plaintext
Increased Support for Agent Mode (Ollama, vLLM, etc)
2025-04-09 08:29:29 -07:00
Andrew Pareles d5dc411e4c Merge remote-tracking branch 'origin/main' into tools-in-plaintext 2025-04-09 08:16:51 -07:00
Andrew Pareles e14fdd6678 awaited 2025-04-09 08:16:43 -07:00
Andrew Pareles 8b5dfa66d5 Merge remote-tracking branch 'origin/model-selection' into tools-in-plaintext 2025-04-09 08:14:01 -07:00
Andrew Pareles aef606dfb4 better update checking 2025-04-09 08:08:47 -07:00
Mathew Pareles 89b29ac97b settings page is decent for update 2025-04-09 07:56:17 -07:00
Andrew Pareles 908de32b8f Merge pull request #382 from voideditor/revert-374-main
Revert "Merge changes from vscode disabling mangling"
2025-04-09 07:08:46 -07:00
Andrew Pareles 4787400865 Revert "Merge changes from vscode disabling mangling" 2025-04-09 07:08:30 -07:00
Mathew Pareles 8b5e7dc25b revert package.json bug 2025-04-09 06:22:41 -07:00
Andrew Pareles 7289f5f15a Merge remote-tracking branch 'origin/main' into tools-in-plaintext 2025-04-09 06:09:16 -07:00
Jérôme Commaret 5e83d419ce Merge Main and add English Comments 2025-04-09 13:47:33 +02:00
Mathew Pareles 3128000cf7 fix ux 2025-04-09 04:39:32 -07:00
Jérôme Commaret b1626849ba Merge branch 'main' into feat_mistral-199 2025-04-09 13:33:06 +02:00
Mathew Pareles 99901d1e20 prepare to add linterror ui (incomplete) 2025-04-09 04:31:06 -07:00
Andrew Pareles 858faed92b 5s 2025-04-09 04:18:46 -07:00
Andrew Pareles a4f94087df supportsTools is back 2025-04-09 04:01:39 -07:00
Andrew Pareles 4236d920d9 Merge remote-tracking branch 'origin/model-selection' into tools-in-plaintext 2025-04-09 04:01:30 -07:00
Andrew Pareles 56619c7b17 rm copilot head 2025-04-09 03:31:34 -07:00
Andrew Pareles ea65903b3f fix running_tool state and double undo on click stop in Apply 2025-04-09 03:25:09 -07:00
Andrew Pareles 78671db5b8 checkpoint state improvements 2025-04-09 02:45:34 -07:00
Mathew Pareles 7f38c1c9c3 ux 2025-04-09 01:23:31 -07:00
Mathew Pareles eefa9648c4 ui changes 2025-04-09 01:10:32 -07:00
Andrew Pareles 8c26fd2081 params 2025-04-08 23:59:07 -07:00
Andrew Pareles 49fbb62259 proper handling of tool call result, and improved prompting 2025-04-08 23:55:57 -07:00
Andrew Pareles ad5a77dc55 rm sax 2025-04-08 15:43:06 -07:00
Jérôme Commaret 2f078326c2 Merge pull request #5 from jcommaret/dev
Dev
2025-04-08 19:48:22 +02:00
Jérôme Commaret 448be9a2cb Met à jour les dépendances dans package-lock.json, y compris la mise à niveau de plusieurs paquets vers leurs dernières versions, ajout de licences manquantes et suppression de dépendances obsolètes. 2025-04-08 19:18:29 +02:00
Jérôme Commaret 0a1f675f45 Merge branch 'main' into dev 2025-04-08 19:17:25 +02:00
Andrew Pareles 052a50f9b0 misc fixes + clarify displayContent 2025-04-08 03:20:34 -07:00
Andrew Pareles e331fb4eed trim whitespace + fix onFinalMessage last chunk error 2025-04-08 02:44:05 -07:00
Andrew Pareles 4e197f4659 debug 2025-04-08 02:04:08 -07:00
Andrew Pareles 39bf2283cc GPTify sax library 2025-04-08 02:03:25 -07:00
Mathew Pareles c87d1886cd tooltips work 2025-04-08 02:00:42 -07:00
Andrew Pareles 6f693c4d0a progress 2025-04-07 23:05:03 -07:00
Andrew Pareles 1c5adb96d3 tool calls via plaintext initial draft 2025-04-07 22:26:31 -07:00
Andrew Pareles 8f8fa8548d + 2025-04-07 20:28:39 -07:00
Mathew Pareles bbe7575321 fix styles that were broken by someone 2025-04-07 18:36:51 -07:00
Andrew Pareles dae5d054e7 Merge pull request #371 from bjoaquinc/remote-wsl-extension
Added open-remote-wsl to /extensions
2025-04-07 16:49:56 -07:00
Andrew Pareles 0638cef79b Merge pull request #374 from GhostDog98/main
Merge changes from vscode disabling mangling
2025-04-07 16:49:12 -07:00
Mathew Pareles 8e2e8c58c9 onboarding draft and lots of misc changes 2025-04-07 16:38:15 -07:00
Mathew Pareles 4b4dde71b3 bug 2025-04-07 16:38:15 -07:00
Mathew Pareles d59b99a2a0 make helper close when Cmd+L or Cmd+K pressed 2025-04-07 16:38:15 -07:00
Mathew Pareles df32898523 remove dropdown max w 2025-04-07 16:38:15 -07:00
Mathew Pareles 2bd8e61706 fn 2025-04-07 16:38:15 -07:00
Mathew Pareles 35724f36cd smartselect draft (need to add setting) 2025-04-07 16:38:15 -07:00
Mathew Pareles 7fba51b59f final changes to selection helper 2025-04-07 16:38:15 -07:00
Mathew Pareles 599d80dcc3 minor change 2025-04-07 16:38:15 -07:00
Mathew Pareles 3c186031be minor positional change 2025-04-07 16:38:15 -07:00
Mathew Pareles 51c84df5a5 better helperwidget positioning 2025-04-07 16:38:15 -07:00
Andrew Pareles 77c031abc5 300ms 2025-04-07 16:13:18 -07:00
Andrew Pareles e81e71c81f hover delay 300ms 2025-04-07 16:12:37 -07:00
Andrew Pareles be07ecec2e Merge remote-tracking branch 'origin/main' into model-selection 2025-04-07 04:00:57 -07:00
Andrew Pareles 3aa0bf11e6 Merge pull request #337 from Dylan-86/main
Added more models for agents from openRouter
2025-04-07 03:46:40 -07:00
Andrew Pareles 329e68ccb1 merge 2025-04-07 03:46:12 -07:00
Andrew Pareles f3da41ba46 Merge pull request #376 from GhostDog98/patch-2
Update CONTRIBUTING.md
2025-04-07 03:39:10 -07:00
Andrew Pareles 78384b3acf Merge pull request #378 from voideditor/add-checkpoints
Add checkpoints
2025-04-07 03:38:31 -07:00
Andrew Pareles 2912d5cd89 rm logs 2025-04-07 03:37:43 -07:00
Andrew Pareles c699577183 only add current file if no message was sent 2025-04-07 02:51:46 -07:00
Andrew Pareles 707fe9e0a3 add links for any file the LLM has read 2025-04-07 02:40:27 -07:00
Andrew Pareles 71dd603c3d context weights 2025-04-07 01:59:45 -07:00
Andrew Pareles 7aa5f1e4ec context trimming 2025-04-07 01:46:07 -07:00
Andrew Pareles 7c4e92a030 pass lint errors!! 2025-04-06 22:47:57 -07:00
Andrew Pareles 76125cc83e feed lint errors 2025-04-06 22:46:30 -07:00
Andrew Pareles 3fc0fd0bc0 ghost 2025-04-06 22:16:32 -07:00
Andrew Pareles 5b0d8f4418 tool use + checkpoint state updates 2025-04-06 22:08:00 -07:00
ghostoverflow256 d71f377ac1 Update CONTRIBUTING.md
also this
2025-04-07 11:23:13 +10:00
ghostoverflow256 dbd63229c8 Update CONTRIBUTING.md
Also linux ia32 doesn't exist as a build target in the gulp files...
2025-04-07 11:21:37 +10:00
ghostoverflow256 1e39cdaba4 Update CONTRIBUTING.md
`vscode-linux-arm` does not exist as a defined build option. `vscode-linux-arm64` *does* however.
2025-04-07 11:13:43 +10:00
Andrew Pareles 5f76fa8d7d turn off reasoning for everything except chat by default 2025-04-06 17:49:32 -07:00
Andrew Pareles f6358e319b model capabilities progress 2025-04-06 17:31:11 -07:00
Andrew Pareles 5a52689d91 Merge pull request #375 from GhostDog98/patch-1
Update CONTRIBUTING.md
2025-04-06 17:04:11 -07:00
ghostoverflow256 5187f0e96c Update CONTRIBUTING.md
Added fix for macos compilation
2025-04-07 09:29:29 +10:00
João Moreno 086e471dd5 disable mangling when building vscode locally (#243856)
* disable mangling when building vscode locally

* local build: ensure mangling runs when minification is on
2025-04-07 09:06:17 +10:00
Andrew Pareles 033de587f2 improve checkpoint logic + UI 2025-04-06 03:29:45 -07:00
Andrew Pareles bd0db41f67 compare by ignoring whitespace 2025-04-05 21:13:03 -07:00
Andrew Pareles a5ac79c6a4 gemma 2025-04-05 21:06:27 -07:00
Andrew Pareles 67a253164e + 2025-04-05 20:56:28 -07:00
Andrew Pareles 3c93e0a414 separate out thinking by feature 2025-04-05 20:56:08 -07:00
Andrew Pareles 1d9e0faaa3 make chats by reference 2025-04-05 20:40:59 -07:00
Andrew Pareles b440c8732f search tools now support regex, reading line numbers, etc 2025-04-05 16:59:03 -07:00
Andrew Pareles 2aae01f3b6 rename tools 2025-04-05 15:59:43 -07:00
Andrew Pareles c1ffca04a7 recursive folder tool call and context 2025-04-05 00:26:20 -07:00
Andrew Pareles de703e82cb add new models, more info in prompt, 2025-04-04 02:39:11 -07:00
Andrew Pareles a2a9cdba60 checkpoints revert/restore diffareas!!! 2025-04-03 17:12:18 -07:00
Jérôme Commaret 795ce62ce8 fix mistral tooling 2025-04-03 20:55:30 +02:00
Andrew Pareles bde51106a1 checkpoints 2025-04-03 01:03:03 -07:00
Andrew Pareles 3ac9dcf0c0 checkpoint progress 2025-04-02 19:13:51 -07:00
Jérôme Commaret 2d9fe3b0be fix models capabilities 2025-04-02 23:39:44 +02:00
Andrew Pareles 7e8af9c2ef improve Apply state and misc other improvements for Apply 2025-04-02 00:25:23 -07:00
Andrew Pareles a94ce5d474 checkpoints work! need to add user_temp_modifications now and deal with diffzones 2025-03-31 22:56:47 -07:00
Joaquin Coromina eceaa6a59b Updated DEFAULT_DOWNLOAD_URL_TEMPLATE to also point at the void binaries repo 2025-04-01 00:03:02 -04:00
Joaquin Coromina 103dd68b3c Updated config to point at voidVersion instead of the base vscode version 2025-04-01 00:00:34 -04:00
Andrew Pareles 7decc8e146 add checkpoints 2025-03-31 20:35:40 -07:00
Joaquin Coromina c58210ba80 Updated the package.json default url to point to the void binaries repo properly 2025-03-31 23:16:06 -04:00
Joaquin Coromina c68a497e77 Merge branch 'voideditor:main' into remote-wsl-extension 2025-03-31 23:11:16 -04:00
Jérôme Commaret 03992c1206 Merge branch 'voideditor:main' into main 2025-03-31 16:48:53 +02:00
Andrew Pareles 7c0ba71314 scaffolding for checkpoints 2025-03-30 21:30:01 -07:00
Andrew Pareles 878a439acd rm tool test thread 2025-03-30 20:53:07 -07:00
Andrew Pareles f047ef9c55 add dummy contrib 2025-03-30 20:52:00 -07:00
Andrew Pareles db6ebeb9f2 Merge pull request #364 from bjoaquinc/patch-2
Updated version argument to get voidVersion
2025-03-30 20:47:06 -07:00
Andrew Pareles 7631b41b27 comment 2025-03-30 20:45:42 -07:00
Andrew Pareles b2d8ca3023 Merge pull request #365 from voideditor/minor-changes-mar
Minor changes mar
2025-03-30 20:44:53 -07:00
Andrew Pareles f11ff3475b revert 2025-03-30 20:44:18 -07:00
Andrew Pareles e74cc15d71 minor UI improvements 2025-03-30 20:44:06 -07:00
Joaquin Coromina 0294cb3b98 Removed the fall back, restored the original code and commented it out 2025-03-30 22:51:40 -04:00
Joaquin Coromina 88dc32a43e Merge branch 'main' into patch-2 2025-03-30 22:46:27 -04:00
Joaquin Coromina 42e1277bb9 Merge branch 'voideditor:main' into remote-wsl-extension 2025-03-30 20:30:43 -04:00
Andrew Pareles 2dcd95cf2a Merge pull request #363 from bjoaquinc/patch-1
Fixed default url in package.json since this is used by app
2025-03-30 17:24:37 -07:00
Joaquin Coromina 499abdf26c Fixed serverConfig.ts to pull voidVersion instead of version 2025-03-30 18:40:41 -04:00
Joaquin Coromina 52e92ac3f9 Update ${NAME_OF_REPO} to binaries repo for accessing the remote host tarballs 2025-03-30 10:50:40 -04:00
Andrew Pareles 885ebc8528 Merge pull request #360 from bjoaquinc/updated-remote-ssh-for-binaries
Fixed the open-remote-ssh extension to point at binaries repo
2025-03-30 03:50:07 -07:00
Andrew Pareles 4f4ef694ff Merge pull request #362 from voideditor/minor-changes-mar
1.0.4 test
2025-03-29 22:01:10 -07:00
Andrew Pareles 8cc68989dd 1.0.4 test 2025-03-29 21:50:06 -07:00
Andrew Pareles 57795b3210 Merge pull request #361 from voideditor/minor-changes-mar
Minor changes mar
2025-03-29 21:21:46 -07:00
Andrew Pareles 68be4dcf57 minor UI 2025-03-29 21:21:31 -07:00
Andrew Pareles eb4d2a6c7b version=34.3.2 2025-03-29 21:21:24 -07:00
Jérôme Commaret 0c30c2681d Merge branch 'voideditor:main' into main 2025-03-30 00:23:24 +01:00
Joaquin Coromina 36277eaed8 updated the downlaod url to point at void reh 2025-03-29 17:43:12 -04:00
Joaquin Coromina ba4cac3fb6 Updated the DEFAULT_DOWNLOAD_URL_TEMPLATE to point at the new void binaries repo and made the string dynamic 2025-03-29 17:34:29 -04:00
Andrew Pareles 3ce2911e3d 1.0.3 2025-03-29 03:39:48 -07:00
Andrew Pareles 89dc3c8805 1.0.2 2025-03-29 03:38:19 -07:00
Andrew Pareles 1bbd6f59fe Merge pull request #358 from voideditor/minor-changes-mar
version -> voidVersion
2025-03-29 03:34:36 -07:00
Andrew Pareles bf6cb764ec version -> voidVersion 2025-03-29 03:34:20 -07:00
Mathew Pareles 7fdde7af3e inline suggestions finished 2025-03-29 03:16:30 -07:00
Andrew Pareles a2a265423f 1.0.3 2025-03-29 03:07:05 -07:00
Andrew Pareles 5f39e314b6 1.0.2 2025-03-29 02:42:59 -07:00
Andrew Pareles b7e87b4af8 1.0.3 2025-03-29 02:04:31 -07:00
Andrew Pareles b34b2f3d7b 1.0.2 2025-03-29 01:57:06 -07:00
Andrew Pareles 5b6bf42576 Merge pull request #357 from voideditor/minor-changes-mar
bump to 1.0.3 to test auto-updates
2025-03-29 01:18:11 -07:00
Andrew Pareles a72ca7868e 1.0.3 2025-03-29 01:14:31 -07:00
Andrew Pareles a0f43c90c4 Merge pull request #356 from voideditor/minor-changes-mar
revert to 1.0.2 for testing
2025-03-29 00:30:06 -07:00
Andrew Pareles 41b85052f0 revert to 1.0.2 for testing 2025-03-29 00:29:27 -07:00
Andrew Pareles 4120031e0d Merge pull request #355 from voideditor/minor-changes-mar
test update to 1.0.3
2025-03-28 23:38:56 -07:00
Andrew Pareles a499fa19fb test update to 1.0.3 2025-03-28 23:38:31 -07:00
Andrew Pareles 6867dabf09 Merge pull request #354 from voideditor/github-workflow
enable auto updates
2025-03-28 17:37:00 -07:00
Andrew Pareles 93875818f5 enable auto updates 2025-03-28 16:21:11 -07:00
Andrew Pareles d09223c362 Merge pull request #353 from voideditor/minor-changes-mar
dummy commit
2025-03-28 02:04:53 -07:00
Andrew Pareles fb5ae96a44 dummy commit 2025-03-28 02:03:47 -07:00
Andrew Pareles dbe62e82e1 Merge pull request #351 from voideditor/minor-changes-mar
new code.ico
2025-03-27 17:14:24 -07:00
Andrew Pareles e5c72ac13e new code.ico 2025-03-27 17:13:31 -07:00
Andrew Pareles dc143e7cb5 Merge pull request #350 from voideditor/minor-changes-mar
xpm
2025-03-27 17:04:36 -07:00
Andrew Pareles 80443892f3 xpm 2025-03-27 17:03:31 -07:00
Andrew Pareles 70e89c8f20 Merge pull request #349 from voideditor/github-workflow
fix vscodium patches
2025-03-27 15:56:10 -07:00
Jérôme Commaret 4f183f6772 Merge remote-tracking branch 'origin/feat_close-update-notification' 2025-03-27 20:43:22 +01:00
Jérôme Commaret f61a7c91e4 Merge remote-tracking branch 'origin/feat_mistral-199' 2025-03-27 20:25:38 +01:00
Jérôme Commaret 610009eb1c Merge branch 'voideditor:main' into main 2025-03-27 20:07:15 +01:00
Jérôme Commaret cd1b783917 Merge branch 'main' into feat_mistral-199 2025-03-27 13:46:55 +01:00
Andrew Pareles 57e8ef5463 fix vscodium patches 2025-03-26 23:14:43 -07:00
Andrew Pareles d5581b1bb4 Merge pull request #345 from voideditor/github-workflow
tslib
2025-03-26 21:43:04 -07:00
Andrew Pareles 1482ebba64 tslib 2025-03-26 21:42:26 -07:00
Andrew Pareles 6d4253dff9 Merge pull request #344 from voideditor/github-workflow
More vscodium patches
2025-03-26 21:01:50 -07:00
Andrew Pareles cc4c863c82 version-0 and version-1 2025-03-26 21:01:21 -07:00
Andrew Pareles 4618e81d20 Merge pull request #343 from voideditor/github-workflow
Vscodium patches
2025-03-26 19:58:53 -07:00
Andrew Pareles 7fa971cbcd Merge pull request #342 from voideditor/minor-changes-mar
Minor changes mar
2025-03-26 19:56:22 -07:00
Andrew Pareles 2267757c41 apply some patches 2025-03-26 19:53:02 -07:00
Andrew Pareles e07727393b rm workflows, move to its own dir 2025-03-26 14:55:08 -07:00
Mathew Pareles 6a73930f4b small styling change 2025-03-26 04:13:07 -07:00
Mathew Pareles 6c382be737 styles 2025-03-26 03:49:25 -07:00
Mathew Pareles c43a234788 commandbar (needs styling for accept/reject) 2025-03-26 03:47:45 -07:00
Andrew Pareles 4e56321eb9 update 2025-03-25 20:11:35 -07:00
Andrew Pareles 9e177c73ca + 2025-03-25 20:08:02 -07:00
Andrew Pareles ccbe1911d2 change build 2025-03-25 20:04:19 -07:00
Jérôme Commaret 015a7c10dc fix : notification closed, but appearing again 2025-03-25 15:50:33 +01:00
Jérôme Commaret f0c8eb55a6 fix close button 2025-03-25 14:37:06 +01:00
Dylan-86 24e5b78583 Added more models for agents from openRouter 2025-03-25 12:09:39 +01:00
Andrew Pareles ed89858fbc leading-1 2025-03-25 01:26:41 -07:00
Andrew Pareles a6d5a5f325 universal macOS 2025-03-24 22:53:54 -07:00
Andrew Pareles e566db8396 Merge remote-tracking branch 'origin/model-selection' into github-workflow 2025-03-24 19:49:22 -07:00
Andrew Pareles b52cf80bd5 update 2025-03-24 19:48:11 -07:00
Andrew Pareles f1a31246b2 b2 2025-03-24 19:46:19 -07:00
Andrew Pareles e33b3ceae1 add refetch 2025-03-24 19:43:14 -07:00
Andrew Pareles c4e78cd3ba size 2025-03-24 18:36:20 -07:00
Andrew Pareles fa1e74acf2 deps 2025-03-24 18:35:41 -07:00
Andrew Pareles 112a770f2c leading 1 2025-03-24 18:31:49 -07:00
Andrew Pareles b1bcac5a6d deps 2025-03-24 18:29:40 -07:00
Andrew Pareles ae79229c00 more retry options 2025-03-24 08:43:52 -07:00
Andrew Pareles 28ac708904 error message 2025-03-24 08:36:01 -07:00
Andrew Pareles 161b446bb2 test3 2025-03-24 08:26:57 -07:00
Jérôme Commaret e0aee9863e ADD : closing the notification update 2025-03-24 16:26:16 +01:00
Andrew Pareles 072484dd5d v4 2025-03-24 07:58:31 -07:00
Andrew Pareles a86d3dc82b large 2025-03-24 00:47:29 -07:00
Andrew Pareles b141b2edfe revert 3.7 with large macos 2025-03-24 00:47:07 -07:00
Mathew Pareles cf9482bd76 add back dropdown animation which was removed 2025-03-23 23:41:28 -07:00
Andrew Pareles 4be2c3b6fd +? 2025-03-23 23:29:53 -07:00
Mathew Pareles 69a75f5c47 fix paste 2025-03-23 23:24:08 -07:00
Andrew Pareles 0ab4f8513b fix? 2025-03-23 23:23:48 -07:00
Andrew Pareles 93f8597dd4 rm ci 2025-03-23 23:21:10 -07:00
Andrew Pareles 0f0e56a263 + 2025-03-23 23:16:37 -07:00
Andrew Pareles 91a23bb2d3 asd 2025-03-23 23:15:17 -07:00
Andrew Pareles 19503d92b4 fix? 2025-03-23 23:13:47 -07:00
Andrew Pareles e2648bba3e fix? 2025-03-23 23:12:45 -07:00
Andrew Pareles 090eaba3aa test 3 2025-03-23 23:07:14 -07:00
Mathew Pareles 1f98e75a4f terminal better 2025-03-23 22:46:25 -07:00
Andrew Pareles a38b4c8ee0 revert to 3.7 again 2025-03-23 22:44:06 -07:00
Andrew Pareles 1def205b28 test2 2025-03-23 22:28:59 -07:00
Mathew Pareles 32bee63b50 fix prompting that made void use pathname_search over regular search 2025-03-23 22:26:09 -07:00
Mathew Pareles 4798bbdc86 prompt 2025-03-23 22:03:26 -07:00
Andrew Pareles a854a341d3 revert to 3.7 2025-03-23 20:56:47 -07:00
Andrew Pareles 1a4623589c rm. ci 2025-03-23 20:54:30 -07:00
Andrew Pareles 51b19a6831 update 2025-03-23 20:53:37 -07:00
Andrew Pareles b58ad5f876 update 2025-03-23 20:48:35 -07:00
Andrew Pareles 4951116a6c test 2025-03-23 20:46:02 -07:00
Andrew Pareles 46ca49e02b + 2025-03-23 20:43:57 -07:00
Andrew Pareles 9133d6ecf5 test 2025-03-23 20:42:33 -07:00
Andrew Pareles ad736edd9e 3.7 edits 2025-03-23 20:29:59 -07:00
Mathew Pareles 1c7494861d rename for better accuracy 2025-03-23 20:26:47 -07:00
Andrew Pareles 31163e8516 npm i 2025-03-23 20:20:41 -07:00
Andrew Pareles 853a26ae8e disable ci 2025-03-23 20:08:34 -07:00
Andrew Pareles 16c7aaf599 v4? 2025-03-23 20:04:35 -07:00
Mathew Pareles d6df2514e1 fix ugly colors 2025-03-23 20:01:05 -07:00
Andrew Pareles 2b33294a85 + 2025-03-23 19:44:54 -07:00
Andrew Pareles 3ecdae6ab6 rename 2025-03-23 19:43:39 -07:00
Mathew Pareles fd1c840618 fix ugly blue color 2025-03-23 19:35:26 -07:00
Mathew Pareles 71f8315e06 fix paste issue 2025-03-23 19:30:32 -07:00
Andrew Pareles 633f7ffee5 test new workflow 2025-03-23 19:30:22 -07:00
Mathew Pareles 35f53e66dd definition 2025-03-23 18:47:34 -07:00
Joaquin Coromina 016d59e81a Merge branch 'voideditor:main' into remote-wsl-extension 2025-03-23 20:03:22 -04:00
Andrew Pareles 82851d519d slice 2025-03-23 00:31:38 -07:00
Andrew Pareles cfc18827bb Merge remote-tracking branch 'origin/main' into model-selection 2025-03-22 19:12:22 -07:00
Andrew Pareles 04e8a204a3 add trusted domain 2025-03-22 18:14:55 -07:00
Jérôme Commaret 4cc7f292b0 Merge branch 'main' into feat_mistral-199 2025-03-22 16:54:11 +01:00
Jérôme Commaret 7c991b4dfc Merge branch 'voideditor:main' into main 2025-03-22 12:29:55 +01:00
Jérôme Commaret 84d17737cf Merge branch 'main' into feat_mistral-199 2025-03-22 12:24:48 +01:00
Jérôme Commaret 4c6b32633b Merge branch 'voideditor:main' into main 2025-03-22 12:22:32 +01:00
Andrew Pareles b933c5cf45 Merge pull request #326 from voideditor/model-selection
Small (or empty) file editing
2025-03-22 12:22:32 +01:00
Andrew Pareles 5b76b4a6a3 Merge pull request #325 from voideditor/v1.0.2
V1.0.2
2025-03-22 12:22:32 +01:00
Mathew Pareles 251d175517 fixes 2025-03-22 12:22:32 +01:00
Andrew Pareles 69ce84c8df Merge pull request #324 from voideditor/model-selection
Command Bar for Changes + Tool UI
2025-03-22 12:22:32 +01:00
Andrew Pareles f9e9771a87 No X button 2025-03-22 12:22:32 +01:00
Mathew Pareles 18ff819ae9 fix search+replace on empty files 2025-03-22 12:22:32 +01:00
Andrew Pareles 98c09c653a 1 2025-03-22 12:22:32 +01:00
Andrew Pareles e1d4d85846 more persistent update message 2025-03-22 12:22:32 +01:00
Andrew Pareles e52e55a431 rm 2025-03-22 12:22:32 +01:00
Andrew Pareles 32d465dbe8 window 2025-03-22 12:22:32 +01:00
Andrew Pareles 1f9bc92b66 1.0.2 2025-03-22 12:22:32 +01:00
Andrew Pareles 9e5c82d6f8 current file fix will vs did 2025-03-22 12:22:32 +01:00
Andrew Pareles dd429208ce void agent 2025-03-22 12:22:32 +01:00
Andrew Pareles 82278ab115 format 2025-03-22 12:22:32 +01:00
Andrew Pareles 13cb8d4de7 misc improvements 2025-03-22 12:22:32 +01:00
Mathew Pareles 2bc004f54d ux for commandbar 2025-03-22 12:22:32 +01:00
Andrew Pareles e58b6041b4 Merge remote-tracking branch 'origin/main' into model-selection 2025-03-22 12:22:32 +01:00
Andrew Pareles 6fd514230c Merge pull request #326 from voideditor/model-selection
Small (or empty) file editing
2025-03-22 00:40:17 -07:00
Mathew Pareles f75b4f34d8 fixes 2025-03-22 00:39:22 -07:00
Mathew Pareles 6ecf7be826 fix search+replace on empty files 2025-03-22 00:02:06 -07:00
Andrew Pareles 47998e4ee5 Merge pull request #325 from voideditor/v1.0.2
V1.0.2
2025-03-21 22:41:28 -07:00
Andrew Pareles 995f39b61f No X button 2025-03-21 22:38:26 -07:00
Andrew Pareles ec2ae0e190 more persistent update message 2025-03-21 22:31:24 -07:00
Andrew Pareles 2bef719754 Merge pull request #324 from voideditor/model-selection
Command Bar for Changes + Tool UI
2025-03-21 21:57:02 -07:00
Andrew Pareles c6376eb1db 1 2025-03-21 21:55:20 -07:00
Andrew Pareles db2c60db82 rm 2025-03-21 21:53:42 -07:00
Andrew Pareles a6f1b1e0ad window 2025-03-21 21:52:53 -07:00
Andrew Pareles 9edca589a0 1.0.2 2025-03-21 21:49:08 -07:00
Andrew Pareles da6710a079 current file fix will vs did 2025-03-21 21:45:17 -07:00
Andrew Pareles f60d5cad2f void agent 2025-03-21 21:22:21 -07:00
Andrew Pareles 75f1a2d79b format 2025-03-21 21:20:57 -07:00
Andrew Pareles 22dd42ba70 misc improvements 2025-03-21 21:20:41 -07:00
Mathew Pareles 1675879fa4 ux for commandbar 2025-03-21 20:14:55 -07:00
Andrew Pareles 00734ce327 update commandbar 2025-03-21 19:58:41 -07:00
Andrew Pareles d6a7921526 Merge remote-tracking branch 'origin/main' into model-selection 2025-03-21 19:58:41 -07:00
Mathew Pareles 3e584d01a3 focus on chatmessage edit 2025-03-21 19:53:12 -07:00
Mathew Pareles 847b57c406 bug 2025-03-21 19:40:59 -07:00
Andrew Pareles d4e9dbb82a rm titles and add .fsPaths 2025-03-21 19:33:03 -07:00
Andrew Pareles f3451d5077 command bar state + accept/reject all 2025-03-21 19:23:06 -07:00
Mathew Pareles 159d52a716 styles 2025-03-21 19:21:13 -07:00
Jérôme Commaret f7d4eebf56 Add Mistral 2025-03-22 03:14:06 +01:00
Mathew Pareles 29f8fa6e1d improve selections 2025-03-21 19:09:49 -07:00
Jérôme Commaret 8e735ec453 Fix model 2025-03-22 03:00:55 +01:00
Jérôme Commaret 850bca80ab Merge branch 'feat_mistral-199' into dev-jco 2025-03-22 01:00:08 +01:00
Jérôme Commaret 440dd809e3 Merge branch 'dev' into dev-jco 2025-03-22 00:59:27 +01:00
Jérôme Commaret ffb416e4e8 Merge remote-tracking branch 'upstream/model-selection' into dev 2025-03-22 00:56:58 +01:00
Jérôme Commaret 0a17a9de19 Merge branch 'main' into dev 2025-03-22 00:47:40 +01:00
Jérôme Commaret a0a7900381 Merge branch 'voideditor:main' into main 2025-03-22 00:45:06 +01:00
Jérôme Commaret 5f77b9faae Réducing Mistral Model context lenght 2025-03-22 00:28:13 +01:00
Andrew Pareles 9dc7632d9b Merge pull request #279 from jcommaret/voidVersion-from-tag
⚙️ - Void version from : tag (semver) commit and date
2025-03-21 15:25:49 -07:00
Andrew Pareles e1d16b3de4 + 2025-03-21 15:23:40 -07:00
Andrew Pareles 4301eb2bb1 version 2025-03-21 15:21:23 -07:00
Andrew Pareles 4ad82cff07 format 2025-03-21 15:19:07 -07:00
Andrew Pareles 22a16974f9 keep versioning, revert mistral (will review separately) 2025-03-21 15:18:29 -07:00
Andrew Pareles cf9eca0564 copy 2025-03-21 14:47:17 -07:00
Jérôme Commaret 3fc97c1a83 Mistral model update 2025-03-21 17:51:05 +01:00
Mathew Pareles 2eafda9132 fix opacity 2025-03-21 06:44:17 -07:00
Andrew Pareles c889acadc0 interruption handling 2025-03-21 06:44:05 -07:00
Mathew Pareles b3522ad73c better styling for commandbar 2025-03-21 06:42:07 -07:00
Andrew Pareles 96867326ae prompt 2025-03-21 05:59:49 -07:00
Andrew Pareles 4946d3c2a0 copy 2025-03-21 05:56:18 -07:00
Andrew Pareles 6a6cb56d87 add visual feedback for tool that's being loaded (eg edit tool) 2025-03-21 05:50:31 -07:00
Andrew Pareles eed71d9582 minor changes 2025-03-21 04:18:25 -07:00
Andrew Pareles 90a282a1f7 minor prompt fix 2025-03-21 02:54:52 -07:00
Andrew Pareles 8511681710 fixed events and interrupts during agent mode 2025-03-21 02:54:52 -07:00
Mathew Pareles 4dff3e216a command bar decent 2025-03-21 00:22:53 -07:00
Mathew Pareles e894be803b command bar draft 2025-03-21 00:22:53 -07:00
Andrew Pareles 07e995fc2f strict:true 2025-03-20 07:24:57 -07:00
Andrew Pareles cffe053226 merge 2025-03-20 06:03:37 -07:00
Andrew Pareles dafc509fa1 always add tool_request 2025-03-20 05:56:56 -07:00
Mathew Pareles 794c4593e6 styles 2025-03-20 04:44:10 -07:00
Mathew Pareles 5d825ad970 styles better 2025-03-20 04:43:06 -07:00
Andrew Pareles cc4115c8c4 misc fixes 2025-03-20 04:19:33 -07:00
Andrew Pareles b86356d2af misc bug fixes with oai-compat and agent 2025-03-20 04:03:37 -07:00
Andrew Pareles 8dab3ac854 tool error styles 2025-03-20 02:52:44 -07:00
Andrew Pareles 1326f19a5e add openrouter claude thinking 2025-03-20 02:37:04 -07:00
Andrew Pareles 69697542ba misc fixes 2025-03-20 00:46:10 -07:00
Andrew Pareles a7728a6030 show current tool running + misc style fixes + chat interrupt stops tool too 2025-03-19 23:00:26 -07:00
Andrew Pareles 9ee14e6a71 tense 2025-03-19 23:00:26 -07:00
Mathew Pareles 2ae098ee96 improvements 2025-03-19 22:19:51 -07:00
Mathew Pareles 0dae638be7 add automatic file adding 2025-03-19 21:58:14 -07:00
Andrew Pareles dd321121e3 prompt 2025-03-19 18:55:13 -07:00
Andrew Pareles 932b4974e9 prompt 2025-03-19 18:30:42 -07:00
Andrew Pareles 20d89cd07c prompt update 2025-03-19 18:28:38 -07:00
Andrew Pareles f75dee39b3 fix 2025-03-19 17:44:53 -07:00
Andrew Pareles acd711f93b fix 2025-03-19 16:32:36 -07:00
Andrew Pareles 744d387fe1 editCodeService and voidCommandBar service!!! + VoidCommandBar.tsx 2025-03-19 16:15:09 -07:00
Jérôme Commaret a3a8a7a842 Merge branch 'feat_bundle_speed' into dev-jco 2025-03-19 12:51:19 +01:00
Jérôme Commaret e28449b83d Bump: voidVersion 2025-03-19 12:36:09 +01:00
Jérôme Commaret 6ecc5cdbad fix voidversion 2025-03-19 12:35:55 +01:00
Jérôme Commaret d64b6fba31 Merge branch 'dev' into dev-jco 2025-03-19 12:29:14 +01:00
Jérôme Commaret d7ac86a6bd Merge branch 'main' into dev 2025-03-19 12:28:29 +01:00
Jérôme Commaret 598f0d47f8 Merge branch 'feat_mistral-199' into feat_bundle_speed 2025-03-19 11:59:56 +01:00
Jérôme Commaret 3b1ccdd2b1 Merge branch 'feat_bundle_speed' into dev-jco 2025-03-19 11:03:23 +01:00
Jérôme Commaret 054a8ad85a Fix bundle speed on local computer 2025-03-19 11:01:21 +01:00
Mathew Pareles ec505b653b debug fastapply mutiple changes (unsolved) 2025-03-18 22:35:57 -07:00
Mathew Pareles ad471a3015 fixes 2025-03-18 21:22:13 -07:00
Andrew Pareles 86fddaa0e0 text_search 2025-03-18 04:56:59 -07:00
Andrew Pareles b9405a76cd auto-approve 2025-03-18 04:51:23 -07:00
Mathew Pareles 730758cbec small changes.. 2025-03-18 03:43:12 -07:00
Andrew Pareles b26b62998d settings page 2025-03-18 03:42:53 -07:00
Andrew Pareles 6856e23e2a just revert all SEARCH/REPLACE on error + history reorder 2025-03-18 03:35:54 -07:00
Andrew Pareles b305fc5034 keep-conflicts on apply 2025-03-18 02:41:45 -07:00
Andrew Pareles 9ab3ec3c11 add autoapply 2025-03-18 00:33:40 -07:00
Andrew Pareles 5b3471a7d7 changes + potentially fix error finding SEARCH block 2025-03-17 23:47:35 -07:00
Mathew Pareles 6bb0966eea draft 2025-03-17 22:58:32 -07:00
Andrew Pareles 5d0a4c8157 misc 2025-03-17 22:51:23 -07:00
Andrew Pareles a1c7b254d3 flex instead of observer 2025-03-17 22:38:45 -07:00
Andrew Pareles 4cf79f7a3b styles 2025-03-17 22:25:41 -07:00
Andrew Pareles b1e50798d8 apply improvements 2025-03-17 20:55:37 -07:00
Jérôme Commaret 31ae3ea46e Merge branch 'feat_mistral-199' into dev-jco 2025-03-18 03:22:44 +01:00
Jérôme Commaret 67d453cffe mistralModelOptions is still to update 2025-03-18 03:01:48 +01:00
Andrew Pareles a320323aa1 promise resolves when abort to free space 2025-03-17 18:39:14 -07:00
Andrew Pareles ad2e5f2616 misc 2025-03-17 16:26:33 -07:00
Andrew Pareles f64f6b54b4 styles + fixes 2025-03-17 16:18:26 -07:00
Jérôme Commaret ab557d4707 Merge branch 'voidVersion-from-tag' into dev-jco 2025-03-17 22:43:00 +01:00
Jérôme Commaret e5a21bf7c6 Merge branch 'feat_mistral-199' into dev-jco 2025-03-17 22:42:30 +01:00
Jérôme Commaret d5ef3b7d9b Merge branch 'voideditor:main' into main 2025-03-17 18:32:05 +01:00
Jérôme Commaret f3753c8da0 Merge branch 'main' into feat_mistral-199 2025-03-17 18:26:29 +01:00
Jérôme Commaret 6d8f843b12 Merge branch 'main' into voidVersion-from-tag 2025-03-17 18:25:35 +01:00
Jérôme Commaret a96e2f9a08 Update scripts/update-version.js
let try this.

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-03-17 18:20:56 +01:00
Jérôme Commaret e08e728fc8 Bump: voidVersion 2025-03-17 17:34:37 +01:00
Jérôme Commaret 18d2d9f5ee Merge branch 'dev' into voidVersion-from-tag 2025-03-17 17:33:30 +01:00
Andrew Pareles f2caa9876c isRunning: tool | message 2025-03-17 04:33:00 -07:00
Andrew Pareles 4a06d79170 type fix 2025-03-17 04:09:31 -07:00
Andrew Pareles 9a8814cc50 jump to file for edit request 2025-03-17 04:00:18 -07:00
Andrew Pareles d6327f2b03 explicit dependence on threadId in agentloop 2025-03-17 03:47:44 -07:00
Andrew Pareles 4528270937 add Void in file menu 2025-03-17 03:00:56 -07:00
Andrew Pareles 8ef693b218 comment 2025-03-17 02:47:11 -07:00
Andrew Pareles 46db2c6e95 + 2025-03-17 02:46:38 -07:00
Andrew Pareles 6b897a6f71 state simplify 2025-03-17 02:44:49 -07:00
Andrew Pareles 7aecbe25cd minor change 2025-03-17 02:39:55 -07:00
Andrew Pareles f79d438084 update styles 2025-03-17 02:19:51 -07:00
Andrew Pareles 35eb93d829 finally bg edits seem to actually work 2025-03-17 01:06:37 -07:00
Andrew Pareles 9350c0dcdf misc state fixes and fix model background (add voidModelService, delete voidFileService) 2025-03-16 23:19:22 -07:00
Mathew Pareles 39e989d3cc codespan link 2025-03-16 22:48:17 -07:00
Mathew Pareles a3cfd0d023 fix codespan links 2025-03-16 22:29:17 -07:00
Jérôme Commaret 7b3a82f1d2 Removed log 2025-03-16 21:13:57 +01:00
Jérôme Commaret 17ac9c081c Mistral FIM Method : seems to be the good one 2025-03-16 21:07:04 +01:00
Andrew Pareles 97b44a94e4 redesign how tool use loop works 2025-03-16 02:55:47 -07:00
Mathew Pareles 4a9af17736 commandbar draft (broken) 2025-03-15 21:55:14 -07:00
Mathew Pareles a33160f33e loading 2025-03-15 21:55:14 -07:00
Mathew Pareles 4bd6f4f8ac loading symbol Cmd+K 2025-03-15 21:54:48 -07:00
Mathew Pareles 71dc1ab93e change default setting for tailwind autosuggestions 2025-03-15 21:54:48 -07:00
Mathew Pareles 0ec9e4a54e codespan shorten paths 2025-03-15 21:54:48 -07:00
Mathew Pareles 4284461b06 fix height issue 2025-03-15 21:54:22 -07:00
Mathew Pareles 3f7f2fb40c commandbar draft 2025-03-15 21:54:22 -07:00
Andrew Pareles 47e93d1cf3 fix up settings 2025-03-15 17:00:21 -07:00
Andrew Pareles a99d1c99a5 agent mode must use tool models only 2025-03-15 16:23:14 -07:00
Andrew Pareles 7b76df235a reasoning component when streaming 2025-03-15 16:00:03 -07:00
Jérôme Commaret c6d246f783 main branch conflicts fixed 2025-03-15 16:07:33 +01:00
Jérôme Commaret ec8eb8bded Merge branch 'main' into dev 2025-03-15 15:02:41 +01:00
Jérôme Commaret 39fb3bc304 Merge branch 'feat_mistral-199' into dev 2025-03-15 14:01:18 +01:00
Andrew Pareles aca5deb814 desc 2025-03-15 04:24:39 -07:00
Andrew Pareles 824de11f21 setting 2025-03-15 04:21:01 -07:00
Andrew Pareles a4d93ac95a chatMode and agent mode UI 2025-03-15 04:19:30 -07:00
Andrew Pareles c1e83e458b header uri name 2025-03-15 03:42:21 -07:00
Andrew Pareles 7ea5870a11 alive ping 2025-03-15 03:38:36 -07:00
Andrew Pareles cf6cef8e0e massively improve styles 2025-03-15 03:24:48 -07:00
Andrew Pareles bf2843b1bd edit state and apply state should work, edit UI 2025-03-15 02:59:32 -07:00
Andrew Pareles 8a6b75b6bb style updates progress 2025-03-15 01:17:13 -07:00
Andrew Pareles e7e5e5ebf8 Merge pull request #320 from ZorinFoss/main
Enhance appimage creation
2025-03-14 19:46:01 -07:00
Jérôme Commaret 00e57dd1bf Merge pull request #3 from jcommaret/feat_mistral-199
Feat mistral 199
2025-03-14 23:23:15 +01:00
Jérôme Commaret 744841b092 Merge branch 'main' into feat_mistral-199 2025-03-14 23:22:37 +01:00
Jérôme Commaret 136840cca4 😻 : Miaou ! 2025-03-14 11:54:43 +01:00
Andrew Pareles dbdd40bd3d add better settings 2025-03-14 02:23:58 -07:00
Andrew Pareles 8f3a752562 sync fast apply to chat 2025-03-14 00:41:17 -07:00
Andrew Pareles fa43a9f937 language detection 2025-03-14 00:20:03 -07:00
Andrew Pareles c222a801a8 fix CtrlK model selection 2025-03-13 21:29:45 -07:00
Andrew Pareles def78affb4 ensureModelExists (forceModel) and fix quick edit state 2025-03-13 21:05:44 -07:00
Andrew Pareles edefe841d5 changes in the BG work!! + autosave 2025-03-13 16:54:31 -07:00
Jérôme Commaret 27b782ff32 HOURRA ! 2025-03-13 18:24:05 +01:00
Jérôme Commaret 654b577ce6 Looks really hacky, but working ! YES ! 2025-03-13 18:23:05 +01:00
Jérôme Commaret 8dcc3bf187 fix to do to types of Mistral connexion 2025-03-13 04:04:51 +01:00
Mathew Pareles cd512402d9 collab 2025-03-12 14:34:58 -07:00
Jérôme Commaret bc6f68e105 Hope it works 2025-03-12 18:52:54 +01:00
Jérôme Commaret b7f764799c stuff changed there also 2025-03-12 15:34:35 +01:00
Jérôme Commaret 6e39b225b2 - FIM is seems ok but disabled (have to find where to change it)
- Send Mistral chat to fix [error 422]
2025-03-12 15:34:02 +01:00
Andrew Pareles 744f0ed852 style 2025-03-12 05:33:38 -07:00
Andrew Pareles 1b6d81f4e0 approve tool works even if closed void and reopened 2025-03-12 05:16:42 -07:00
Mathew Pareles bb370b390c logic 2025-03-12 05:06:16 -07:00
Mathew Pareles 378ef493b5 fix gratuitous error 2025-03-12 04:35:12 -07:00
Mathew Pareles a70f606362 fix faulty changes 2025-03-12 03:41:08 -07:00
Andrew Pareles f66fbcd911 agentLoop improvements 2025-03-12 03:19:24 -07:00
Mathew Pareles 1c2e8c0c59 minh 2025-03-12 03:09:00 -07:00
Mathew Pareles 09ba9387bc fix refactor 2025-03-12 02:54:36 -07:00
Andrew Pareles b97c5089ce update approve/reject state 2025-03-12 02:26:55 -07:00
Andrew Pareles 7563f595f3 misc 2025-03-12 02:06:30 -07:00
Andrew Pareles 79ef6b5773 fix stream state 2025-03-12 02:02:25 -07:00
Andrew Pareles 11d376325e tool UI progress + terminal fix + misc fixes 2025-03-12 01:24:21 -07:00
Andrew Pareles 1ef8011bb0 tool UI step 1 (BROKEN) 2025-03-12 00:51:17 -07:00
Mathew Pareles fa1f407b76 fix naming for tool response 2025-03-12 00:37:30 -07:00
Mathew Pareles 094e05070a size 2025-03-12 00:20:21 -07:00
Mathew Pareles 6d5d4c34d7 size 2025-03-12 00:11:14 -07:00
Mathew Pareles 967f7dc85e make apply actually work 2025-03-12 00:06:28 -07:00
Jérôme Commaret d541d8fbb6 Merge branch 'voideditor:main' into main 2025-03-12 01:45:00 +01:00
Jérôme Commaret 89ac7738e4 sendLLMMessageToProviverImplementation : Mistral SendChat 2025-03-11 16:21:49 +01:00
Jérôme Commaret 7aca03de5c ADD : defaultModelsOfProvider : mistral - codestral-latest 2025-03-11 16:19:34 +01:00
Jérôme Commaret 4e9dc5ce4a defaultProviderSettings : mistral option 2025-03-11 16:12:14 +01:00
Andrew Pareles d6d5f77183 terminal progress + misc 2025-03-11 06:53:54 -07:00
Zorin FOSS 62b3a7d7be Add files via upload 2025-03-11 06:25:39 +02:00
Zorin FOSS 437244540a Enhance appimage script 2025-03-11 06:25:04 +02:00
Zorin FOSS 05d102f354 Enhance void.desktop 2025-03-11 05:27:42 +02:00
Andrew Pareles e4b0061f66 Merge remote-tracking branch 'origin/model-selection' 2025-03-10 19:14:29 -07:00
Jérôme Commaret e70e5d4208 Merge branch 'voideditor:main' into main 2025-03-11 00:36:08 +01:00
Andrew Pareles ccd0db635b Merge pull request #319 from voideditor/model-selection
Terminal Service Progress + Fix Import Issue
2025-03-10 15:38:39 -07:00
Andrew Pareles ab81479340 terminalService 2025-03-10 15:36:00 -07:00
Andrew Pareles 65be47d970 ancilary terminal tool 2025-03-10 15:36:00 -07:00
Mathew Pareles 217a33cc2e tool call improvements (error message + better ux) 2025-03-10 00:39:50 -07:00
Andrew Pareles 6a55abcd81 split into common/ and browser/ so terminal works 2025-03-09 18:09:35 -07:00
Jérôme Commaret 330092d023 mistral npm updated 2025-03-09 21:55:20 +01:00
Jérôme Commaret 7b665d05cf Merge pull request #1 from jcommaret/fix-pb
Fix pb
2025-03-09 05:35:38 +01:00
Jérôme Commaret 352496fc3a Merge branch 'main' into fix-pb 2025-03-09 05:35:16 +01:00
Jérôme Commaret eda11f29d9 FIM : I think it works as expected 2025-03-09 03:30:23 +01:00
Joaquin Coromina 9fce7ce812 Merge branch 'voideditor:main' into remote-wsl-extension 2025-03-08 19:20:59 +07:00
Joaquin Coromina 85009ab5b3 Added open-remote-wsl output folder to the exclude list for hygiene checks 2025-03-08 05:48:15 -06:00
Joaquin Coromina fe01285d58 Added open-remote-wsl to the npm install build command 2025-03-08 05:42:20 -06:00
Joaquin Coromina d1fa4d3439 Added open-remote-wsl to the gulp build pipeline 2025-03-08 05:39:43 -06:00
Mathew Pareles 2300559081 links to code 2025-03-08 03:09:50 -08:00
Mathew Pareles 4a9c0c864f links draft 1 2025-03-08 03:09:50 -08:00
Andrew Pareles 4212e50ac5 Merge pull request #312 from voideditor/model-selection
Tool use improvements + Clear settings
2025-03-08 02:51:25 -08:00
Andrew Pareles 009dfbef37 improve prompt 2025-03-08 02:24:39 -08:00
Andrew Pareles 75ef9d5528 rm id and add tool_use_id 2025-03-07 23:59:58 -08:00
Jérôme Commaret ec4c52aff9 remove package 2025-03-08 08:23:40 +01:00
Jérôme Commaret bccc6a64ef Mistral FIM maybe 2025-03-08 08:23:20 +01:00
Andrew Pareles 93c0bb1d04 rename 2025-03-07 23:03:11 -08:00
Andrew Pareles 4061e6382a rm console.logs 2025-03-07 22:58:17 -08:00
Andrew Pareles b5dbd42eed make it so model does not change during agent mode even if user switches 2025-03-07 22:55:06 -08:00
Joaquin Coromina d0921f899c Added open-remote-wsl to extensions 2025-03-08 00:23:54 -06:00
Andrew Pareles c42a782bde rm void- 2025-03-07 20:56:03 -08:00
Andrew Pareles 6be0b74047 update 2025-03-07 20:55:42 -08:00
Andrew Pareles 0fb9222f3a switch 2025-03-07 20:54:55 -08:00
Andrew Pareles db7ec0272a don't display empty assistant messages (these are only used for LLMMessage) 2025-03-07 20:48:57 -08:00
Andrew Pareles 7558d4dc1c anthropic reasoning works with tools 2025-03-07 20:28:23 -08:00
Andrew Pareles f89b036d17 fix empty message order 2025-03-07 19:37:39 -08:00
Andrew Pareles 2b7df8c604 remove rawAnthropicContent 2025-03-07 19:37:33 -08:00
Jérôme Commaret ac2fb9e36d Mistral : Added, not fim 2025-03-07 14:30:33 +01:00
Andrew Pareles 5ad5fe7014 clickable tool requests 2025-03-07 03:14:20 -08:00
Andrew Pareles 5eb2bcfef6 Merge pull request #310 from voideditor/model-selection
Tool use progress + UI
2025-03-07 03:08:22 -08:00
Andrew Pareles cd532435c2 tool use is getting close! minor fixes + UI 2025-03-07 03:07:06 -08:00
Andrew Pareles 6e2a803628 tool use UI 2025-03-07 02:20:03 -08:00
Andrew Pareles 1e5e3d7f01 update prompt 2025-03-07 01:18:03 -08:00
Andrew Pareles ae15fd1f38 thinking slider backend support, QwQ 2025-03-07 00:28:54 -08:00
Andrew Pareles bc1a9d692e qwq + groq reasoning 2025-03-06 19:24:14 -08:00
Andrew Pareles dce3f392ce force placeholder color 2025-03-06 18:52:30 -08:00
Andrew Pareles 6d3de34818 style 2025-03-06 04:38:17 -08:00
Andrew Pareles f292fa8df6 xxs 2025-03-06 03:43:11 -08:00
Andrew Pareles 82ffd4e8f4 slider styles 2025-03-06 03:34:57 -08:00
Andrew Pareles 6b5792aaf7 thinking slider UI 2025-03-06 02:51:48 -08:00
Andrew Pareles e1a1ca6275 w-full 2025-03-06 02:06:03 -08:00
Andrew Pareles d2e096957e add reasoning slider (still need to hook it up) 2025-03-06 02:01:08 -08:00
Andrew Pareles 3bb9d5b3a5 xAI -> Grok 2025-03-06 02:00:52 -08:00
Andrew Pareles 1be8d17899 add autocomplete UI and filtering 2025-03-06 02:00:52 -08:00
Andrew Pareles 46e8fb1ea5 anthropic thinking output 2025-03-06 02:00:25 -08:00
Andrew Pareles 02d8527244 updated settings for 3.7 2025-03-06 02:00:25 -08:00
Andrew Pareles aeb3f4f97c split model capabilities 2025-03-06 02:00:25 -08:00
Andrew Pareles 46a8e147d0 contrib 2025-03-06 02:00:25 -08:00
Andrew Pareles a5b68448b5 agent 2025-03-06 02:00:25 -08:00
Andrew Pareles c0ea6cc4d8 update contrib 2025-03-06 02:00:24 -08:00
Mathew Pareles 1086b6272a minor change 2025-03-06 00:19:29 -08:00
Mathew Pareles cc6a231b3c fix markdown stylings with prose 2025-03-06 00:09:58 -08:00
Mathew Pareles ead1d9229f fix markdown spacing draft 2025-03-06 00:08:26 -08:00
Mathew Pareles 1b77fc7091 prepare large refactor 2025-03-06 00:07:55 -08:00
Mathew Pareles ee6d5e47ec dropdown + styles 2025-03-06 00:06:14 -08:00
Mathew Pareles a6c9b5f089 edit message bug fixes 2025-03-06 00:06:05 -08:00
Andrew Pareles c9984ce011 Merge pull request #308 from jcommaret/fix-node-version-in-contribution
[Update]node version  in CONTRIBUTING.md
2025-03-05 19:26:57 -08:00
Jérôme Commaret 4eaffd90d1 Update node version in CONTRIBUTING.md 2025-03-06 04:14:02 +01:00
Andrew Pareles 027c0ee6bd the retry loop works! 2025-03-04 05:20:23 -08:00
Andrew Pareles 4ba7352374 fix substr logic + nuanced FINAL_ logic 2025-03-04 03:33:44 -08:00
Andrew Pareles 866547df79 fix extractCodeFromResult Search/Replace 2025-03-04 02:24:25 -08:00
Andrew Pareles 7f278aafcb fix SEARCH/REPLACE detection 2025-03-04 01:18:10 -08:00
Andrew Pareles 5c5a44e4ac stop omitting system message 2025-03-03 23:54:57 -08:00
Andrew Pareles 83f9ff54ab update 2025-03-03 23:35:36 -08:00
Andrew Pareles 1259e71589 update prompt and add accept/reject ui 2025-03-03 23:01:24 -08:00
Andrew Pareles c9cc6cc6a1 properly deal with VSCode chat 2025-03-03 22:46:40 -08:00
Andrew Pareles dc65016ecb clarity 2025-03-03 22:11:21 -08:00
Andrew Pareles 9aae56682e sweep to fix lint 2025-03-03 21:59:47 -08:00
Andrew Pareles 176d302258 lint 2025-03-03 21:53:57 -08:00
Andrew Pareles e23fee5191 fix untraceable import error that breaks everything 2025-03-03 21:53:14 -08:00
Andrew Pareles 4f34af61f3 Merge remote-tracking branch 'origin/fix-css' into model-selection 2025-03-03 21:17:19 -08:00
Andrew Pareles 1257e54ebe tools are getting close 2025-03-03 20:13:26 -08:00
Andrew Pareles d2b5531a33 Update VOID_USEFUL_LINKS.md 2025-03-02 19:46:39 -08:00
Andrew Pareles 993c171e06 Merge pull request #304 from voideditor/model-selection
Fix tailwind
2025-03-01 19:23:32 -08:00
Jérôme Commaret c3c760c03b Merge branch 'voideditor:main' into voidVersion-from-tag 2025-02-12 21:22:07 +01:00
Jérôme Commaret 12c686c102 Bump: voidVersion 2025-02-11 14:43:30 +01:00
Jérôme Commaret 7bceceef85 Bump: voidVersion 2025-02-11 14:21:02 +01:00
Jérôme Commaret ba15b31661 fix 2025-02-11 14:19:38 +01:00
Jérôme Commaret 3b09174587 bad hash and date, will be updated 2025-02-11 14:19:16 +01:00
Jérôme Commaret 7482e2aa82 version : tag, commit, and date from iProductConfiguration 2025-02-11 13:00:23 +01:00
Jérôme Commaret e3c826b253 Bump: voidVersion 2025-02-11 12:56:53 +01:00
Jérôme Commaret db69464ddc Bump: voidVersion 2025-02-11 12:08:53 +01:00
Jérôme Commaret 014c87bc3e Fetch and display : voidVersion into dialog handler 2025-02-11 12:08:03 +01:00
Jérôme Commaret ba606d229a Added voidVersion to IProductConfiguration 2025-02-11 12:07:06 +01:00
Jérôme Commaret fbb4faacbc added voidVersion to product.json 2025-02-11 12:06:24 +01:00
Jérôme Commaret 9b7fd09278 removed voidVersion from package.json 2025-02-11 12:05:47 +01:00
Jérôme Commaret b90973b195 Bump: voidVersion 2025-02-11 11:36:49 +01:00
169 changed files with 18827 additions and 8066 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
disturl="https://electronjs.org/headers"
target="34.2.0"
target="34.3.2"
ms_build_id="11044223"
runtime="electron"
build_from_source="true"
+8 -10
View File
@@ -12,11 +12,9 @@ There are a few ways to contribute:
### Codebase Guide
We highly recommend reading [this](https://github.com/microsoft/vscode/wiki/Source-Code-Organization) article on VSCode's sourcecode organization.
We [highly recommend reading this](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md) guide that we put together on Void's sourcecode if you'd like to contribute!
<!-- ADD BLOG HERE
We wrote a [guide to working in VSCode].
-->
The repo is not as intimidating as it first seems if you read the guide!
Most of Void's code lives in the folder `src/vs/workbench/contrib/void/`.
@@ -61,13 +59,14 @@ To build Void, open `void/` inside VSCode. Then open your terminal and run:
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.
- This step can take ~5 min. The build is done when you see two check marks (one of the items will continue spinning indefinitely - it compiles our React code).
4. Run Void.
- Run `./scripts/code.sh` (Mac/Linux).
- Run `./scripts/code.bat` (Windows).
6. Nice-to-knows.
- You can always press <kbd>Ctrl+R</kbd> (<kbd>Cmd+R</kbd>) inside the new window to reload and see your new changes. It's faster than <kbd>Ctrl+Shift+P</kbd> and `Reload Window`.
- You might want to add the flags `--user-data-dir ./.tmp/user-data --extensions-dir ./.tmp/extensions` to the above run command, which lets you delete the `.tmp` folder to reset any IDE changes you made when testing.
- You can kill any of the build scripts by pressing `Ctrl+D` in VSCode terminal. If you press `Ctrl+C` the script will close but will keep running in the background (to open all background scripts, just re-build).
#### Building Void from Terminal
@@ -85,11 +84,11 @@ Alternatively, if you want to build Void from the terminal, instead of pressing
#### Common Fixes
- Make sure you followed the prerequisite steps.
- Make sure you have Node version `20.16.0` (the version in `.nvmrc`)!
- Make sure you have Node version `20.18.2` (the version in `.nvmrc`)!
- If you get `"TypeError: Failed to fetch dynamically imported module"`, make sure all imports end with `.js`.
- If you see missing styles, wait a few seconds and then reload.
- If you have any questions, feel free to [submit an issue](https://github.com/voideditor/void/issues/new). You can also refer to VSCode's complete [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) page.
- If you get errors like `npm error libtool: error: unrecognised option: '-static'`, make sure you have GNU libtool instead of BSD libtool (BSD is the default in macos)
## Packaging
@@ -103,12 +102,11 @@ We don't usually recommend packaging. Instead, you should probably just build. I
### Windows
- `npm run gulp vscode-win32-x64` - most common
- `npm run gulp vscode-win32-ia32`
- `npm run gulp vscode-win32-arm64`
### Linux
- `npm run gulp vscode-linux-x64` - most common
- `npm run gulp vscode-linux-arm`
- `npm run gulp vscode-linux-ia32`
- `npm run gulp vscode-linux-arm64`
### Output
+4 -3
View File
@@ -15,25 +15,26 @@ This repo contains the full sourcecode for Void. We are currently in [open beta]
- 👋 [Discord](https://discord.gg/RSNjgaugJs)
- 🔨 [Contribute](https://github.com/voideditor/void/blob/main/CONTRIBUTING.md)
- 🔨 [Contribute](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md)
- 🚙 [Roadmap](https://github.com/orgs/voideditor/projects/2)
- 📝 [Changelog](https://voideditor.com/changelog)
- 🧭 [Codebase Guide](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md)
## Contributing
1. Feel free to attend a weekly meeting in our Discord channel if you'd like to contribute!
2. To get started working on Void, see [Contributing](https://github.com/voideditor/void/blob/main/CONTRIBUTING.md).
2. To get started working on Void, see [here](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md).
3. We're open to collaborations and suggestions of all types - just reach out.
## Reference
Void is a fork of the [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).
Void is a fork of the [vscode](https://github.com/microsoft/vscode) repository. For a guide to the VSCode/Void codebase, see [our Codebase Guide](https://github.com/voideditor/void/blob/main/VOID_CODEBASE_GUIDE.md).
## Support
Feel free to reach out in our Discord or contact us via email: hello@voideditor.com.
+162
View File
@@ -0,0 +1,162 @@
# Void Codebase Guide
The Void codebase is not as intimidating as it seems!
Most of Void's code lives in the folder `src/vs/workbench/contrib/void/`.
The purpose of this document is to explain how Void's codebase works. If you want build instructions, see [Contributing](https://github.com/voideditor/void/blob/main/HOW_TO_CONTRIBUTE.md).
## Void Codebase Guide
### Terminology
Here is some important terminology you should know if you're working inside VSCode:
- An **Editor** is the thing that you type your code in. If you have 10 tabs open, that's just one editor! Editors contain tabs (or "models").
- A **Model** is an internal representation of a file's contents. It's shared between editors (for example, if you press `Cmd+\` to make a new editor, then the model of a file like `A.ts` is shared between them. Two editors, one model. That's how changes sync.).
- Each model has a **URI** it represents, like `/Users/.../my_file.txt`. (A URI or "resource" is generally just a path).
- The **Workbench** is the wrapper that contains all the editors, the terminal, the file system tree, etc.
- Usually you use the `ITextModel` type for models and the `ICodeEditor` type for editors. There aren't that many other types.
### Minimal VSCode Rundown
Here's a minimal VSCode rundown if you're just getting started with Void:
- VSCode is (and therefore Void is) an Electron app. Electron runs two processes: a **main** process (for internals) and a **browser** process (browser means HTML in general, not just "web browser").
- Code in a `browser/` folder always lives on the browser process, and it can use `window` and other browser items.
- Code in an `electron-main/` folder always lives on the main process, and it can import `node_modules`.
- Code in `common/` can be used by either process, but doesn't get any special imports.
- The browser environment is not allowed to import `node_modules`, but there are two workarounds:
1. Bundle the raw node_module code to the browser - we're doing this for React.
2. Implement the code on `electron-main/` and set up a channel between main/browser - we're doing this for sendLLMMessage.
VSCode is organized into "Services". A service is just a class that mounts a single time (in computer science theory this is called a "singleton"). You can register services with `registerSingleton` so that you can easily use them in any constructor with `@<Service>`. See _dummyContrib for an example we put together on how to register them. The registration is the same every time.
Services are always lazily created, even if you register them as Eager. If you want something that always runs on Void's mount, you should use a "workbench contribution". See _dummyContrib for this. Very similar to a Service, just registered slightly differently.
Actions or "commands" are functions you register on VSCode so that either you or the user can call them later. You can run actions as a user by pressing Cmd+Shift+P (opens the command pallete), or you can run them internally by using the commandService to call them by ID. We use actions to register keybinding listeners like Cmd+L, Cmd+K, etc. The nice thing about actions is the user can change the keybindings.
See [here](https://github.com/microsoft/vscode/wiki/Source-Code-Organization) for a decent VSCode guide with even more info.
Each section below contains an overview of a core part of Void's sourcecode. You might want to scroll to find the item that's relevant to you.
### Internal LLM Message Pipeline
Here's a picture of all the dependencies that are relevent between the time you first send a message through Void's sidebar, and the time a request is sent to your provider.
Sending LLM messages from the main process avoids CSP issues with local providers and lets us use node_modules more easily.
<div align="center">
<img width="100%" src="https://github.com/user-attachments/assets/9cf54dbb-82c4-4488-97a2-bd8dea890b50">
</div>
**Notes:** `modelCapabilities` is an important file that must be updated when new models come out!
### Apply
Void has two types of Apply: **Fast Apply** (uses Search/Replace, see below), and **Slow Apply** (rewrites whole file).
When you click Apply and Fast Apply is enabled, we prompt the LLM to output Search/Replace block(s) like this:
```
<<<<<<< ORIGINAL
// original code goes here
=======
// replaced code goes here
>>>>>>> UPDATED
```
This is what allows Void to quickly apply code even on 1000-line files. It's the same as asking the LLM to press Ctrl+F and enter in a search/replace query.
### Apply Inner Workings
The `editCodeService` file runs Apply. The same exact code is also used when the LLM calls the Edit tool, and when you submit Cmd+K. Just different versions of Fast/Slow Apply mode.
Here is some important terminology:
- A **DiffZone** is a {startLine, endLine} region in which we show Diffs (red/green areas). We update it when the user types, so it's always accurate.
- A **DiffArea** is a generalization that tracks line numbers like a DiffZone.
- The only type of zone that can "stream" is a DiffZone. Each DiffZone has an llmCancelToken if it's streaming.
- When you click Apply, we create a **DiffZone** over that the full file so that any changes that the LLM makes will show up in red/green. We then stream the change.
- When an LLM calls Edit, it's really calling Apply.
- When you submit Cmd+K, it's the same as Apply except we create a smaller DiffZone (not on the whole file).
### Writing Files Inner Workings
When Void wants to change your code, it just writes to a text model. This means all you need to know to write to a file is its URI - you don't have to load it, save it, etc. There are some annoying background URI/model things to think about to get this to work, but we handled them all in `voidModelService`.
### Void Settings Inner Workings
We have a service `voidSettingsService` that stores all your Void settings (providers, models, global Void settings, etc). Imagine this as an implicit dependency for any of the core Void services:
<div align="center">
<img width="800" src="https://github.com/user-attachments/assets/9f3cb68c-a61b-4810-8429-bb90b992b3fa">
</div>
Here's a guide to some of the terminology we're using:
- **FeatureName**: Autocomplete | Chat | CtrlK | Apply
- **ModelSelection**: a {providerName, modelName} pair.
- **ProviderName**: The name of a provider: `'ollama'`, `'openAI'`, etc.
- **ModelName**: The name of a model (string type, eg `'gpt-4o'`).
- **RefreshProvider**: a provider that we ping repeatedly to update the models list.
- **ChatMode** = normal | gather | agent
### Approval State
`editCodeService`'s data structures contain all the information about changes that the user needs to review. However, they don't store that information in a useful format. We wrote the following service to get a more useful derived state:
<div align="center">
<img width="600" src="https://github.com/user-attachments/assets/f3645355-dff6-467c-bc38-ffe52077c08b">
</div>
### Build process
If you want to know how our build pipeline works, see our build repo [here](https://github.com/voideditor/void-builder).
## VSCode Codebase Guide (Not Void)
The Void team put together this list of links to get up and running with VSCode's sourcecode, the foundation of Void. We hope it's helpful!
#### Links for Beginners
- [VSCode UI guide](https://code.visualstudio.com/docs/getstarted/userinterface) - covers auxbar, panels, etc.
- [UX guide](https://code.visualstudio.com/api/ux-guidelines/overview) - covers Containers, Views, Items, etc.
#### Links for Contributors
- [How VSCode's sourcecode is organized](https://github.com/microsoft/vscode/wiki/Source-Code-Organization) - this explains where the entry point files are, what `browser/` and `common/` mean, etc. This is the most important read on this whole list! We recommend reading the whole thing.
- [Built-in VSCode styles](https://code.visualstudio.com/api/references/theme-color) - CSS variables that are built into VSCode. Use `var(--vscode-{theme but replacing . with -})`. You can also see their [Webview theming guide](https://code.visualstudio.com/api/extension-guides/webview#theming-webview-content).
#### Misc
- [Every command](https://code.visualstudio.com/api/references/commands) built-in to VSCode - not used often, but here for reference.
- Note: VSCode's repo is the source code for the Monaco editor! An "editor" is a Monaco editor, and it shares the code for ITextModel, etc.
#### VSCode's Extension API
Void is no longer an extension, so these links are no longer required, but they might be useful if we ever build an extension again.
- [Files you need in an extension](https://code.visualstudio.com/api/get-started/extension-anatomy).
- [An extension's `package.json` schema](https://code.visualstudio.com/api/references/extension-manifest).
- ["Contributes" Guide](https://code.visualstudio.com/api/references/contribution-points) - the `"contributes"` part of `package.json` is how an extension mounts.
- [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).
-37
View File
@@ -1,37 +0,0 @@
# Useful links
The Void team put together this list of links to get up and running with VSCode's sourcecode. We hope it's helpful!
## Contributing
- [How VSCode's sourcecode is organized](https://github.com/microsoft/vscode/wiki/Source-Code-Organization) - this explains where the entry point files are, what `browser/` and `common/` mean, etc. This is the most important read on this whole list! We recommend reading the whole thing.
- [Built-in VSCode styles](https://code.visualstudio.com/api/references/theme-color) - CSS variables that are built into VSCode. Use `var(--vscode-{theme but replacing . with -})`. You can also see their [Webview theming guide](https://code.visualstudio.com/api/extension-guides/webview#theming-webview-content).
## Beginners / Getting started
- [VSCode UI guide](https://code.visualstudio.com/docs/getstarted/userinterface) - covers auxbar, panels, etc.
- [UX guide](https://code.visualstudio.com/api/ux-guidelines/overview) - covers Containers, Views, Items, etc.
## Misc
- [Every command](https://code.visualstudio.com/api/references/commands) built-in to VSCode - not used often, but here for reference.
## VSCode's Extension API
Void is no longer an extension, so these links are no longer required, but they might be useful if we ever build an extension again.
- [Files you need in an extension](https://code.visualstudio.com/api/get-started/extension-anatomy).
- [An extension's `package.json` schema](https://code.visualstudio.com/api/references/extension-manifest).
- ["Contributes" Guide](https://code.visualstudio.com/api/references/contribution-points) - the `"contributes"` part of `package.json` is how an extension mounts.
- [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).
+75 -75
View File
@@ -1,75 +1,75 @@
c0a187acca68906c4a6387e8fabd052cb031ace6132d60a71001d9a0e891958e *chromedriver-v34.2.0-darwin-arm64.zip
fa5a46d752267d8497d375e19079e8b6a8df70c234a79b2d6b48f5862e1a0abc *chromedriver-v34.2.0-darwin-x64.zip
61e03d4fa570976d80f740637f56192b6448a05a73d1fba9717900b29f2b1b4d *chromedriver-v34.2.0-linux-arm64.zip
ec774d9b1a1b828a0db1502a1017fcab1dfed99b1b6b2fd2308dd600a1efa98a *chromedriver-v34.2.0-linux-armv7l.zip
cc15a6e6206485a2d96649ceb60509b9da04fa2811c4824b2e0eb43d1f4b1417 *chromedriver-v34.2.0-linux-x64.zip
9777122f6684180ef375b9b21dcabbc731d8a8befa300d1d47ad954a5b64c1c8 *chromedriver-v34.2.0-mas-arm64.zip
69451fa148b105fec9644646b22ca758a206499574c5816591354835c8056679 *chromedriver-v34.2.0-mas-x64.zip
eb7adc7e720f5e0f1d2c12ecbe886bdc01f2c9aaa3954bd6ebd313750bb18819 *chromedriver-v34.2.0-win32-arm64.zip
cb1973b0c2f5565974d5c2cb51816692f064b6cdc7897fa341d528ba7f9b14bf *chromedriver-v34.2.0-win32-ia32.zip
af5575b4727c3dbe7272016cbbaa8872043f843168a47d86748a50397efb4f77 *chromedriver-v34.2.0-win32-x64.zip
fcc718af2a28fb953290dc971e945818b4dbb293f297e6e25acb669d450cc0dd *electron-api.json
fa47e752e559a6472b87d8907f63296ed8cd53ecf862a4ae113018474d40aa8e *electron-v34.2.0-darwin-arm64-dsym-snapshot.zip
336c3374e721e2379901141be6345459f78d243b037c65b55bac4ae8cb14bfbe *electron-v34.2.0-darwin-arm64-dsym.zip
ac3b9d712d9f036f066d8eba42797117a513e2d250fcc117f0354300b7d5c1e5 *electron-v34.2.0-darwin-arm64-symbols.zip
ee447c17b2ac545e48083113d7e39a916821c1316f60f42cbcbee4fffe7c022a *electron-v34.2.0-darwin-arm64.zip
d7510bc038d06b26690ac9a78fbb256626502303ff7f5b1c2242f64a02832b1b *electron-v34.2.0-darwin-x64-dsym-snapshot.zip
0e95c2bbda00afe78e6229b824e3ffe0cc8612956dc11a5a30380224acdbecae *electron-v34.2.0-darwin-x64-dsym.zip
6d734ef8e8fd007071aae9a13534894dd801f11900f3e73e49cf5352b426e575 *electron-v34.2.0-darwin-x64-symbols.zip
8ef741819c8a5370dabc3b9df5e6ac217366477c5d5c656ed23c800bc984d887 *electron-v34.2.0-darwin-x64.zip
c2f448882a0392ebfd9810058a6a9580b087002c74fca3dcd3cf4ba5c3b27a7c *electron-v34.2.0-linux-arm64-debug.zip
51e887c382593021127593ceba89ad662d3a6de0f748b94fe3a0ce78a7393923 *electron-v34.2.0-linux-arm64-symbols.zip
818c91309da8ff948c43df58a996c05c0d27daa690e1d659355d9db01e351919 *electron-v34.2.0-linux-arm64.zip
c2f448882a0392ebfd9810058a6a9580b087002c74fca3dcd3cf4ba5c3b27a7c *electron-v34.2.0-linux-armv7l-debug.zip
71ec2b7473db766194bcf04648229c4affedce06e150e692745cc72dbc3749c5 *electron-v34.2.0-linux-armv7l-symbols.zip
0c75996c6bf2d37d0441d3e1021386a9348f8d21783d75571cdb10ede606424f *electron-v34.2.0-linux-armv7l.zip
d1f17be8df8ec6dc1a23afd8a7752f0b949c755a493b8a2c1e83f76c629258ca *electron-v34.2.0-linux-x64-debug.zip
d8fa79154b0b663dbd0054d69f53aec326997449ef21943d27225a2d6899660f *electron-v34.2.0-linux-x64-symbols.zip
f12a02d86cc657500978d263ec6d1841b6e2085cd3bd4d30a97cfe14685396f3 *electron-v34.2.0-linux-x64.zip
71ef8bfebb8513a405fd2beb44ad18f802bbac2248f81a5328ddaaa12906d70c *electron-v34.2.0-mas-arm64-dsym-snapshot.zip
437aca6cad3158f15fd852e5913462637052940f812b20ccc10fccc133d5a0d6 *electron-v34.2.0-mas-arm64-dsym.zip
7f253375a7b43d34b770c03153e47185be7a64bc0c10a783993a93144eb35492 *electron-v34.2.0-mas-arm64-symbols.zip
1f601c20430b036b485c7dc02a39a297f0d08905462a4568306c12f299375e2f *electron-v34.2.0-mas-arm64.zip
d32664181804a17f825bf1fbfd8f0cbe01e0b41b284937359e6d9754b3481ed8 *electron-v34.2.0-mas-x64-dsym-snapshot.zip
f6b394b89fb77dfeefdf525739fe8cd9695f8c2ac2251ed7c571a376cde059e9 *electron-v34.2.0-mas-x64-dsym.zip
539328d93e9bc122e6e34faeab6a42f4845c523f796c7a11bbdcfe5e856f6b98 *electron-v34.2.0-mas-x64-symbols.zip
749b6ced7a9d35a719ad98d4c3bf1a08c315c19ce97d2497235d4ac302cda665 *electron-v34.2.0-mas-x64.zip
e843ea4cb7a93686728d056c957c124760886167932ff619b518e45917edf38a *electron-v34.2.0-win32-arm64-pdb.zip
4908423be5f8ad1b5dd737edfd69ee0870460e16bb639bb963b0981bda2628e4 *electron-v34.2.0-win32-arm64-symbols.zip
9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-arm64-toolchain-profile.zip
1629cec7b5620e6ca3b5305c393ae147d1a3871a8f164d686b7bee3810fc1109 *electron-v34.2.0-win32-arm64.zip
e7182f1ef5ed13187fe12f35133cefb50c59e1d52ada758cbb72813dda575205 *electron-v34.2.0-win32-ia32-pdb.zip
a10f778f62bf060a7e38f5ea75ea472679b9ef4f12767ee0703e6d77effae78a *electron-v34.2.0-win32-ia32-symbols.zip
9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-ia32-toolchain-profile.zip
96396712a0240f04471f71246b3885a513b08e3f2d40154272d34e59419db7e6 *electron-v34.2.0-win32-ia32.zip
0c55ef5b1a6ea4604e3f0e9f8b5e946944abd1cfc3b974432d40e924a9996812 *electron-v34.2.0-win32-x64-pdb.zip
d6b67cf12edabcc62ae21e7c90ac6b1161dbefe4e6b765c69fc7040096b7d026 *electron-v34.2.0-win32-x64-symbols.zip
9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-x64-toolchain-profile.zip
a4fdf617dca787b7f1c6f8d0d1cb69c8adb37ee23f9553fe69803f9cad713360 *electron-v34.2.0-win32-x64.zip
5ca44a4ee9cc74a56c9556ce3f3774986dbb8b4f4953088c7f4577bfba4356a8 *electron.d.ts
86247a6815cc98f321374edcbfc0ab7ee79bcb13a5c25213f42151e4059ec690 *ffmpeg-v34.2.0-darwin-arm64.zip
e94f4707a91194f97d0451f9d5a27982c873a5c13d83d20926ce6fdb613f536c *ffmpeg-v34.2.0-darwin-x64.zip
947d7b7cb035eab94cc15b602dfa8a925cf238c1d9225c01732fe0c41f59c571 *ffmpeg-v34.2.0-linux-arm64.zip
64d74b6b94ac4fd755b97e0ec8d50af9cd73810fdb52f6c081b13a3337ace0ea *ffmpeg-v34.2.0-linux-armv7l.zip
a62cb20c5e5f2ba6f1df6f1bc406cc30f7ed44fe6380b506afa333d1b4ad7a87 *ffmpeg-v34.2.0-linux-x64.zip
715bbb3d193b1ca57d4b329edd05cd6b125dc188cdb73d1c66f220f62c8562e3 *ffmpeg-v34.2.0-mas-arm64.zip
866bf47106e8c3e50f046783eb8f5c756069c6de962a46f4edc7a5ee7e4593ee *ffmpeg-v34.2.0-mas-x64.zip
71a2a6c4cca15ccbcbf8912f5d73f855b0ca79804f941f68527ae808f8163957 *ffmpeg-v34.2.0-win32-arm64.zip
399f841cca166781078ca42c8ea060e3d5850ec6cb79716186d0806f3ce20842 *ffmpeg-v34.2.0-win32-ia32.zip
afad2a44f20a0c0c01fb1fea637f3f821842399593c9961c74d650ca12d32cbb *ffmpeg-v34.2.0-win32-x64.zip
c95fdc9dba05aa68aeccb69d4c34f0cb1fb98d7f5291d974d0b638488693655f *hunspell_dictionaries.zip
0abe74138afdb6e45a085d77407659f13c75ab96f694313d4e98bd662f9c6df2 *libcxx-objects-v34.2.0-linux-arm64.zip
3d0cbf6fb150b006428eab78235856d2204d5e93ca85f162e429b4c8bd9b0d3b *libcxx-objects-v34.2.0-linux-armv7l.zip
3a5491e32cec825499919be1b8bf0550d28fe5a31ff00a95572d49a58bb4820a *libcxx-objects-v34.2.0-linux-x64.zip
ff8753d52f759041b8e5462125ee2b96798fe8b5047f8fb8ae60cd07af2fb13d *libcxx_headers.zip
c98cce0091681bc367a48f66c5f4602961aa9cb6dd1a995d8969d6b39ce732f3 *libcxxabi_headers.zip
78a9606190fb227460ddcd276ad540873595d6a82fd1007f42900f53347e3eb1 *mksnapshot-v34.2.0-darwin-arm64.zip
6d4587a36f509356a908b6752de527cfe8a294a2d435b82ea3a98288c3a3a178 *mksnapshot-v34.2.0-darwin-x64.zip
17737bd34f7feefc5143b745f2c4f0e5a41678780e144eb43de41f8d4b9d7852 *mksnapshot-v34.2.0-linux-arm64-x64.zip
565aa9a84d913b7b5eb8d3b868cff151cb8a6c16548233ffd92b2f9bf3789227 *mksnapshot-v34.2.0-linux-armv7l-x64.zip
7332d7864ab4e5ee7fa8d00580d9357f30d0e69d3d1434d67c61f79f666314a4 *mksnapshot-v34.2.0-linux-x64.zip
cc81bfc9894378a9fc8a777429e04cc80860663b3dff8eba1f8cc72559a4f23d *mksnapshot-v34.2.0-mas-arm64.zip
c98230088698638159f6e7e0c5ddde3cb4dba9fa81d76e3c58fc86f96b63ef81 *mksnapshot-v34.2.0-mas-x64.zip
cc7b42943d998e1083ad8119dc2201dd27d709c15aa2a9b78f409485d2927592 *mksnapshot-v34.2.0-win32-arm64-x64.zip
ade9e3126f113318e226f9bdeba068b4383b30a98517a361386eff448459effa *mksnapshot-v34.2.0-win32-ia32.zip
344292ea318dc0e21f37bc7c82d57a57449f0fb278d9868f1b1b597bdcb1f36f *mksnapshot-v34.2.0-win32-x64.zip
c9b82c9f381742e839fea00aeb14f24519bcaf38a0f4eed25532191701f9535b *chromedriver-v34.3.2-darwin-arm64.zip
d556c1e2b06f1bf131e83c2fb981de755c28e1083a884d257eb964815be16b0c *chromedriver-v34.3.2-darwin-x64.zip
1cabad4f3303ac2ff172a9f22185f64944dbaa6fc68271609077158eaefdee35 *chromedriver-v34.3.2-linux-arm64.zip
4213ce52c72ef414179b5c5c22ae8423847ff030d438296bd6c2aac763930a7b *chromedriver-v34.3.2-linux-armv7l.zip
3c64c08221fdfc0f4be60ea8b1b126f2ecca45f60001b63778522f711022c6ea *chromedriver-v34.3.2-linux-x64.zip
e8388734d88e011cb6cd79795431de9206820749219d80565ee49d90501d2bf3 *chromedriver-v34.3.2-mas-arm64.zip
3ad1dd37bd6e0bb37e8503898db7aedd56bd5213e6d6760b05c3d11f4625062b *chromedriver-v34.3.2-mas-x64.zip
d567b481a0f5d88e84bba7718f89fb08f56363bfc4cb5914e1c2086358a5c252 *chromedriver-v34.3.2-win32-arm64.zip
df6732e9dc61cb20a3c0b2a2de453aac7e2bd54e7cbff43512afa614852c15fa *chromedriver-v34.3.2-win32-ia32.zip
dda0765c8d064924632e18cd152014ecd767f3808fc51c8249a053bfb7ca70a2 *chromedriver-v34.3.2-win32-x64.zip
1945f15caff98f2e0f1ee539c483d352fb8d4d0c13f342caa7abe247676d828c *electron-api.json
c078bbf727b3c3026f60e07a0f4643b85c06c581b54be017d0a6c284ba6772d3 *electron-v34.3.2-darwin-arm64-dsym-snapshot.zip
35f587754d6a3272606258386bf73688d63dd53c7e572d3a7cbaae6f3f60bdae *electron-v34.3.2-darwin-arm64-dsym.zip
08b14ee02c98353de3c738120dfd017322666e82b914a7f6de9b9888dcc5c0f0 *electron-v34.3.2-darwin-arm64-symbols.zip
2a4aa7e8fa30f229e465ebd18d3e4722e2b41529dc51a68a954d333a7e556ffe *electron-v34.3.2-darwin-arm64.zip
1509ccdeb80024f5e3edd5ecf804b4cef4e47ea2bd74e33ef0b39044b0ccf892 *electron-v34.3.2-darwin-x64-dsym-snapshot.zip
3bbe5d587c3f582ed8c126b0fb635cc02ad9a14d077b04892fe6f862092445b0 *electron-v34.3.2-darwin-x64-dsym.zip
fa7ece82e6ecaf1c94ed341e8ebff98e64687c68fe113f52cd9a21400302e22f *electron-v34.3.2-darwin-x64-symbols.zip
23938c62257a65a863ed7aa7c7966ba5f257a7d3dc16c78293e826962cc39c5c *electron-v34.3.2-darwin-x64.zip
0547eecf8ab538d74fa854f591ce8b888a3dbb339256d2db3038e7bb2c6dd929 *electron-v34.3.2-linux-arm64-debug.zip
676d0dc2b1c1c85c8b2abbb8cd5376ee22ecdb910493b910d9ae5a998532136a *electron-v34.3.2-linux-arm64-symbols.zip
774e4ccb39d553e5487994a9f8c60774a90f08cdb049ff65f3963fc27c969ff2 *electron-v34.3.2-linux-arm64.zip
0547eecf8ab538d74fa854f591ce8b888a3dbb339256d2db3038e7bb2c6dd929 *electron-v34.3.2-linux-armv7l-debug.zip
ba33bf53fcb35dea568a2795f5b23ecf46c218abe8258946611c72a1f42f716c *electron-v34.3.2-linux-armv7l-symbols.zip
73ae92c8fffb351d3a455569cf57ce9a3f676f42bf61939c613c607fe0fc3bfb *electron-v34.3.2-linux-armv7l.zip
e61a9a69dd7ea6f2687708a8e83516670cdea53c728226e598e2f6f1fad5b77b *electron-v34.3.2-linux-x64-debug.zip
f1a04df7fe67dd1cd29e7b87871525458d2eb24c0cf3b5835a1c56974707562a *electron-v34.3.2-linux-x64-symbols.zip
7b74c0c4fae82e27c7e9cbca13e9763e046113dba8737d3e27de9a0b300ac87e *electron-v34.3.2-linux-x64.zip
8571a6aa83e00925ceb39fdc5a45a9f6b9aa3d92fd84951c6f252ed723aea4ae *electron-v34.3.2-mas-arm64-dsym-snapshot.zip
477410c6f9a6c5eeaedf376058a02c2996fc0a334aa40eeec7d3734c09347f4d *electron-v34.3.2-mas-arm64-dsym.zip
c2e62dcd6630cb51b2d8e2869e74e47d29bda785521cea6e82e546d0fc58aabb *electron-v34.3.2-mas-arm64-symbols.zip
a1698e8546a062fd59b7f8e5507a7f3220fb00b347f2377de83fc9a07f7f3507 *electron-v34.3.2-mas-arm64.zip
741a24ac230a3651dca81d211f9f00b835c428a5ed0c5f67d370d4e88b62f8d6 *electron-v34.3.2-mas-x64-dsym-snapshot.zip
aeff97ec9e5c9e173ac89e38acd94476025c5640d5f27be1e8c2abd54398bab3 *electron-v34.3.2-mas-x64-dsym.zip
9f14b66b1d612ac66697288e8763171c388f7f200854871a5f0ab464a6a921c2 *electron-v34.3.2-mas-x64-symbols.zip
c979d7e7175f1e8e03ca187997d4c156b878189fc3611b347fadebcb16f3e027 *electron-v34.3.2-mas-x64.zip
f43c700641e8220205dd356952e32718d113cf530520c4ed7209b59851eac266 *electron-v34.3.2-win32-arm64-pdb.zip
3ba6e01c99bffac6b5dd3fd6f122ecdb571cf6f675dc5498c65050bd7a382ef8 *electron-v34.3.2-win32-arm64-symbols.zip
c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-arm64-toolchain-profile.zip
9b9cb65d75a16782088b492f9ef3bb4d27525012b819c12bf29bd27e159d749b *electron-v34.3.2-win32-arm64.zip
1006e7af4c149114b5ebc3497617aaa6cd1bb0b131e0a225fd73709ff308f9c5 *electron-v34.3.2-win32-ia32-pdb.zip
1ecb6430cd04454f08f557c9579163f3552144bfcc0b67b768dad8868b5b891d *electron-v34.3.2-win32-ia32-symbols.zip
c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-ia32-toolchain-profile.zip
d004fd5f853754001fafaec33e383d1950b30c935ee71b297ec1c9e084355e9b *electron-v34.3.2-win32-ia32.zip
4e0721552fd2f09e9466e88089af8b965f1bfbc4ae00a59aaf6245b1d1efabfd *electron-v34.3.2-win32-x64-pdb.zip
9dea812a7e7cd0fb18e5fed9a99db5531959a068c24d3c0ecedceb644cd3ffa0 *electron-v34.3.2-win32-x64-symbols.zip
c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-x64-toolchain-profile.zip
1785e161420fb90d2331c26e50bba3413cae9625b7db3c8524ea02ade631efba *electron-v34.3.2-win32-x64.zip
722b304c31ddac58b0083d94a241c5276464f04bd8ea4fcbfd33051d197be103 *electron.d.ts
31ce159b2e47d1de5bc907d8e1c89726b0f2ba530ec2e9d7a8e5c723b1ccf6e0 *ffmpeg-v34.3.2-darwin-arm64.zip
565539bac64a6ee9cf6f188070f520210a1507341718f5dc388ac7c454b1e1d5 *ffmpeg-v34.3.2-darwin-x64.zip
6006ea0f46ab229feb2685be086b0fafd65981e2939dd2218a078459c75ab527 *ffmpeg-v34.3.2-linux-arm64.zip
9404ce2e85df7c40f809f2cf62c7af607de299839fe6b7ae978c3015500abcc8 *ffmpeg-v34.3.2-linux-armv7l.zip
79aec96898b7e2462826780ee0b52b9ab299dc662af333e128a34fd5ddae87f1 *ffmpeg-v34.3.2-linux-x64.zip
9190743c78210574faf5d5ecb82a00f8fa15e5f2253378cb925a99ca9d39961b *ffmpeg-v34.3.2-mas-arm64.zip
48915adcb1a6342efeda896035101300f0432c0304cfb38f2378e98c6309ebae *ffmpeg-v34.3.2-mas-x64.zip
745d5ef786de6d4a720475079836e2fda7b501cfcd255819485a47de5b24b74e *ffmpeg-v34.3.2-win32-arm64.zip
d0d86d60978439dc8ae4a723d4e4c1f853891d596bfd84033440a232fa762e2f *ffmpeg-v34.3.2-win32-ia32.zip
4441539fd8c9cbe79880ff1bade9bdc0c3744c33d7409130af6404e57ee401ff *ffmpeg-v34.3.2-win32-x64.zip
39edd1eeefe881aa75af0e438204e0b1c6e6724e34fa5819109276331c0c2c9a *hunspell_dictionaries.zip
20dd417536e5f4ebc01f480221284c0673729c27b082bc04e2922f16cd571719 *libcxx-objects-v34.3.2-linux-arm64.zip
7e53c5779c04f895f8282c0450ec4a63074d15a0e910e41006cfea133d0288af *libcxx-objects-v34.3.2-linux-armv7l.zip
92e2283c924ab523ffec3ea22513beaab6417f7fc3d570f57d51a1e1ceb7f510 *libcxx-objects-v34.3.2-linux-x64.zip
9bf3c6e8ad68f523fe086fada4148dd04e5bb0b9290d104873e66f2584a5cf50 *libcxx_headers.zip
34e4b44f9c5e08b557a2caed55456ce7690abab910196a783a2a47b58d2b9ac9 *libcxxabi_headers.zip
11f67635e6659f9188198e4086c51b89890b61a22f6c17c99eff35595ee8f51d *mksnapshot-v34.3.2-darwin-arm64.zip
c0add9ef4ac27c73fa043d04b4c9635fd3fd9f5c16d7a03e961864ba05252813 *mksnapshot-v34.3.2-darwin-x64.zip
6262adf86a340d8d28059937b01ef1117b93212e945fddbceea5c18d7e7c64f0 *mksnapshot-v34.3.2-linux-arm64-x64.zip
f7db8ebe91a1cc8d24ef6aad12949a18d8e4975ac296e3e5e9ecd88c9bccb143 *mksnapshot-v34.3.2-linux-armv7l-x64.zip
6642038e86bda362980ff1c8973a404e2b02efdd87de9e35b650fc1e743833da *mksnapshot-v34.3.2-linux-x64.zip
15883bf8e8cd737c3682d1e719d7cbac92f50b525681aac324dca876861dfc7d *mksnapshot-v34.3.2-mas-arm64.zip
4da23a950bfcc377ef21c37d496017ab4c36da03f3b41049ac114042c42608ce *mksnapshot-v34.3.2-mas-x64.zip
fab59573d3c2f9bdf31146a1896d24ac0c51f736aad86d2f3c7ecef13c05a7fd *mksnapshot-v34.3.2-win32-arm64-x64.zip
66f25e07c6f8d5d2009577a129440255a3baf63c929a5b60b2e77cd52e46105b *mksnapshot-v34.3.2-win32-ia32.zip
8168bfbf61882cfac80aed1e71e364e1c7f2fccd11eac298e6abade8b46894ea *mksnapshot-v34.3.2-win32-x64.zip
+3 -1
View File
@@ -54,9 +54,11 @@ async function main(buildDir) {
...defaultOpts,
// TODO(deepak1556): Incorrectly declared type in electron-osx-sign
ignore: (filePath) => {
const ext = path_1.default.extname(filePath);
return filePath.includes(gpuHelperAppName) ||
filePath.includes(rendererHelperAppName) ||
filePath.includes(pluginHelperAppName);
filePath.includes(pluginHelperAppName) ||
ext === '.asar' || ext === '.dat' || ext === '.gif' || ext === '.icns' || ext === '.ico' || ext === '.json' || ext === '.mp3' || ext === '.nib' || ext === '.pak' || ext === '.png' || ext === '.scpt' || ext === '.ttf' || ext === '.wasm' || ext === '.woff' || ext === '.woff2';
}
};
const gpuHelperOpts = {
+3 -1
View File
@@ -58,9 +58,11 @@ async function main(buildDir?: string): Promise<void> {
...defaultOpts,
// TODO(deepak1556): Incorrectly declared type in electron-osx-sign
ignore: (filePath: string) => {
const ext = path.extname(filePath);
return filePath.includes(gpuHelperAppName) ||
filePath.includes(rendererHelperAppName) ||
filePath.includes(pluginHelperAppName);
filePath.includes(pluginHelperAppName) ||
ext === '.asar' || ext === '.dat' || ext === '.gif' || ext === '.icns' || ext === '.ico' || ext === '.json' || ext === '.mp3' || ext === '.nib' || ext === '.pak' || ext === '.png' || ext === '.scpt' || ext === '.ttf' || ext === '.wasm' || ext === '.woff' || ext === '.woff2';
}
};
+1
View File
@@ -139,6 +139,7 @@ module.exports.indentationFilter = [
'!extensions/simple-browser/media/*.js',
'!extensions/open-remote-ssh/out/*.js', // Void added this
'!extensions/open-remote-wsl/out/*.js', // Void added this
];
module.exports.copyrightFilter = [
+1
View File
@@ -73,6 +73,7 @@ const compilations = [
'.vscode/extensions/vscode-selfhost-import-aid/tsconfig.json',
'extensions/open-remote-ssh/tsconfig.json', // Void added this
'extensions/open-remote-wsl/tsconfig.json', // Void added this
];
+16 -4
View File
@@ -154,9 +154,20 @@ function getNodeChecksum(expectedName) {
}
function extractAlpinefromDocker(nodeVersion, platform, arch) {
const imageName = arch === 'arm64' ? 'arm64v8/node' : 'node';
let imageName = 'node';
let dockerPlatform = '';
if (arch === 'arm64') {
imageName = 'arm64v8/node';
const architecture = cp.execSync(`docker info --format '{{json .Architecture}}'`, { encoding: 'utf8' }).trim();
if (architecture !== '"aarch64"') {
dockerPlatform = '--platform=linux/arm64';
}
}
log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from docker image ${imageName}`);
const contents = cp.execSync(`docker run --rm ${imageName}:${nodeVersion}-alpine /bin/sh -c 'cat \`which node\`'`, { maxBuffer: 100 * 1024 * 1024, encoding: 'buffer' });
const contents = cp.execSync(`docker run --rm ${dockerPlatform} ${imageName}:${nodeVersion}-alpine /bin/sh -c 'cat \`which node\`'`, { maxBuffer: 100 * 1024 * 1024, encoding: 'buffer' });
return es.readArray([new File({ path: 'node', contents, stat: { mode: parseInt('755', 8) } })]);
}
@@ -308,10 +319,11 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa
}
const name = product.nameShort;
const release = packageJson.release;
let packageJsonContents;
const packageJsonStream = gulp.src(['remote/package.json'], { base: 'remote' })
.pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined, type: 'module' }))
.pipe(json({ name, version, release, dependencies: undefined, optionalDependencies: undefined, type: 'module' }))
.pipe(es.through(function (file) {
packageJsonContents = file.contents.toString();
this.emit('data', file);
@@ -319,7 +331,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa
let productJsonContents;
const productJsonStream = gulp.src(['product.json'], { base: '.' })
.pipe(json({ commit, date: readISODate('out-build'), version }))
.pipe(json({ commit, date: readISODate('out-build'), version, release }))
.pipe(es.through(function (file) {
productJsonContents = file.contents.toString();
this.emit('data', file);
+4 -3
View File
@@ -267,7 +267,8 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
}
const name = product.nameShort;
const packageJsonUpdates = { name, version };
const release = packageJson.release;
const packageJsonUpdates = { name, version, release };
if (platform === 'linux') {
packageJsonUpdates.desktopName = `${product.applicationName}.desktop`;
@@ -284,7 +285,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
// Void - this is important, creates the product.json in .app
let productJsonContents;
const productJsonStream = gulp.src(['product.json'], { base: '.' })
.pipe(json({ commit, date: readISODate('out-build'), checksums, version }))
.pipe(json({ commit, date: readISODate('out-build'), checksums, version, release }))
.pipe(es.through(function (file) {
productJsonContents = file.contents.toString();
this.emit('data', file);
@@ -372,7 +373,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
} 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/' + product.applicationName));
all = es.merge(all, shortcut);
}
+3 -5
View File
@@ -25,7 +25,6 @@ const exec = util.promisify(cp.exec);
const root = path.dirname(__dirname);
const commit = getVersion(root);
const linuxPackageRevision = Math.floor(new Date().getTime() / 1000);
/**
* @param {string} arch
@@ -87,7 +86,7 @@ function prepareDebPackage(arch) {
const dependencies = await dependenciesGenerator.getDependencies('deb', binaryDir, product.applicationName, debArch);
gulp.src('resources/linux/debian/control.template', { base: '.' })
.pipe(replace('@@NAME@@', product.applicationName))
.pipe(replace('@@VERSION@@', packageJson.version + '-' + linuxPackageRevision))
.pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`))
.pipe(replace('@@ARCHITECTURE@@', debArch))
.pipe(replace('@@DEPENDS@@', dependencies.join(', ')))
.pipe(replace('@@RECOMMENDS@@', debianRecommendedDependencies.join(', ')))
@@ -202,8 +201,7 @@ function prepareRpmPackage(arch) {
.pipe(replace('@@NAME@@', product.applicationName))
.pipe(replace('@@NAME_LONG@@', product.nameLong))
.pipe(replace('@@ICON@@', product.linuxIconName))
.pipe(replace('@@VERSION@@', packageJson.version))
.pipe(replace('@@RELEASE@@', linuxPackageRevision))
.pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`))
.pipe(replace('@@ARCHITECTURE@@', rpmArch))
.pipe(replace('@@LICENSE@@', product.licenseName))
.pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@'))
@@ -279,7 +277,7 @@ function prepareSnapPackage(arch) {
const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' })
.pipe(replace('@@NAME@@', product.applicationName))
.pipe(replace('@@VERSION@@', commit.substr(0, 8)))
.pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`))
// Possible run-on values https://snapcraft.io/docs/architectures
.pipe(replace('@@ARCHITECTURE@@', arch === 'x64' ? 'amd64' : arch))
.pipe(rename('snap/snapcraft.yaml'));
+2 -2
View File
@@ -87,8 +87,8 @@ function buildWin32Setup(arch, target) {
NameLong: product.nameLong,
NameShort: product.nameShort,
DirName: product.win32DirName,
Version: pkg.version,
RawVersion: pkg.version.replace(/-\w+$/, ''),
Version: `${pkg.version}.${pkg.release}`,
RawVersion: `${pkg.version.replace(/-\w+$/, '')}.${pkg.release}`,
NameVersion: product.win32NameVersion + (target === 'user' ? ' (User)' : ''),
ExeBasename: product.nameShort,
RegValueName: product.win32RegValueName,
+1 -4
View File
@@ -85,10 +85,7 @@ function getExtensionDownloadStream(extension) {
if (extension.vsix) {
input = ext.fromVsix(path_1.default.join(root, extension.vsix), extension);
}
else if (productjson.extensionsGallery?.serviceUrl) {
input = ext.fromMarketplace(productjson.extensionsGallery.serviceUrl, extension);
}
else {
else { // Void - ext-from-gh.patch
input = ext.fromGithub(extension);
}
return input.pipe((0, gulp_rename_1.default)(p => p.dirname = `${extension.name}/${p.dirname}`));
+1 -3
View File
@@ -73,9 +73,7 @@ function getExtensionDownloadStream(extension: IExtensionDefinition) {
if (extension.vsix) {
input = ext.fromVsix(path.join(root, extension.vsix), extension);
} else if (productjson.extensionsGallery?.serviceUrl) {
input = ext.fromMarketplace(productjson.extensionsGallery.serviceUrl, extension);
} else {
} else { // Void - ext-from-gh.patch
input = ext.fromGithub(extension);
}
+1
View File
@@ -55,6 +55,7 @@ const dirs = [
'.vscode/extensions/vscode-selfhost-import-aid',
'.vscode/extensions/vscode-selfhost-test-provider',
'extensions/open-remote-ssh', // Void added this
'extensions/open-remote-wsl', // Void added this
];
+1 -1
View File
@@ -37,7 +37,7 @@ function getMangledFileContents(projectPath) {
* @type {webpack.LoaderDefinitionFunction}
*/
module.exports = async function (source, sourceMap, meta) {
if (this.mode !== 'production') {
if (true) { // Void - extensions-disable-mangler
// Only enable mangling in production builds
return source;
}
+1 -1
View File
@@ -71,7 +71,7 @@
"type": "string",
"description": "The URL from where the vscode server will be downloaded. You can use the following variables and they will be replaced dynamically:\n- ${quality}: vscode server quality, e.g. stable or insiders\n- ${version}: vscode server version, e.g. 1.69.0\n- ${commit}: vscode server release commit\n- ${arch}: vscode server arch, e.g. x64, armhf, arm64\n- ${release}: release number",
"scope": "application",
"default": "https://github.com/voideditor/${NAME_OF_REPO}/releases/download/${version}.${release}/void-server-${os}-${arch}-${version}.${release}.tar.gz"
"default": "https://github.com/voideditor/binaries/releases/download/${version}.${release}/void-reh-${os}-${arch}-${version}.${release}.tar.gz"
},
"remote.SSH.remotePlatform": {
"type": "object",
@@ -32,12 +32,14 @@ export async function getVSCodeServerConfig(): Promise<IServerConfig> {
const customServerBinaryName = vscode.workspace.getConfiguration('remote.SSH.experimental').get<string>('serverBinaryName', '');
return {
version: vscode.version.replace('-insider', ''),
// version: vscode.version.replace('-insider', ''),
commit: productJson.commit,
quality: productJson.quality,
release: productJson.release,
serverApplicationName: customServerBinaryName || productJson.serverApplicationName,
serverDataFolderName: productJson.serverDataFolderName,
serverDownloadUrlTemplate: productJson.serverDownloadUrlTemplate
serverDownloadUrlTemplate: productJson.serverDownloadUrlTemplate,
// Void changed this
version: productJson.voidVersion
};
}
@@ -39,7 +39,7 @@ export class ServerInstallError extends Error {
}
}
const DEFAULT_DOWNLOAD_URL_TEMPLATE = 'https://github.com/voideditor/void-updates-server/releases/download/test/void-server-${os}-${arch}.tar.gz';
const DEFAULT_DOWNLOAD_URL_TEMPLATE = 'https://github.com/voideditor/binaries/releases/download/${version}.${release}/void-reh-${os}-${arch}-${version}.${release}.tar.gz';
export async function installCodeServer(conn: SSHConnection, serverDownloadUrlTemplate: string | undefined, extensionIds: string[], envVariables: string[], platform: string | undefined, useSocketPath: boolean, logger: Log): Promise<ServerInstallResult> {
let shell = 'powershell';
+3
View File
@@ -0,0 +1,3 @@
# Remote - WSL Support
Inherited for Void from [Open Remote - WSL](https://github.com/jeanp413/open-remote-wsl).
@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withBrowserDefaults = require('../shared.webpack.config').browser;
module.exports = withBrowserDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
}
});
@@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
resolve: {
mainFields: ['module', 'main']
},
entry: {
extension: './src/extension.ts',
}
});
+15
View File
@@ -0,0 +1,15 @@
{
"name": "open-remote-wsl",
"version": "0.0.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-remote-wsl",
"version": "0.0.4",
"engines": {
"vscode": "^1.70.2"
}
}
}
}
+281
View File
@@ -0,0 +1,281 @@
{
"name": "open-remote-wsl",
"displayName": "Remote - WSL",
"description": "Open any folder in the Windows Subsystem for Linux (WSL).",
"version": "0.0.4",
"icon": "resources/icon.png",
"engines": {
"vscode": "^1.70.2"
},
"extensionKind": [
"ui"
],
"enabledApiProposals": [
"resolvers",
"contribViewsRemote"
],
"keywords": [
"remote development",
"remote",
"wsl"
],
"api": "none",
"activationEvents": [
"onCommand:openremotewsl.connect",
"onCommand:openremotewsl.connectInNewWindow",
"onCommand:openremotewsl.connectUsingDistro",
"onCommand:openremotewsl.connectUsingDistroInNewWindow",
"onCommand:openremotewsl.showLog",
"onResolveRemoteAuthority:wsl",
"onView:wslTargets"
],
"main": "./out/extension.js",
"contributes": {
"configuration": {
"title": "WSL",
"properties": {
"remote.WSL.serverDownloadUrlTemplate": {
"type": "string",
"description": "The URL from where the vscode server will be downloaded. You can use the following variables and they will be replaced dynamically:\n- ${quality}: vscode server quality, e.g. stable or insiders\n- ${version}: vscode server version, e.g. 1.69.0\n- ${commit}: vscode server release commit\n- ${arch}: vscode server arch, e.g. x64, armhf, arm64\n- ${release}: release number",
"scope": "application",
"default": "https://github.com/voideditor/binaries/releases/download/${version}.${release}/void-reh-${os}-${arch}-${version}.${release}.tar.gz"
}
}
},
"views": {
"remote": [
{
"id": "wslTargets",
"name": "WSL Targets",
"group": "targets@1",
"when": "(isWindows && !isWeb)",
"remoteName": "wsl"
}
]
},
"commands": [
{
"command": "openremotewsl.connect",
"title": "Connect to WSL",
"category": "Remote-WSL"
},
{
"command": "openremotewsl.connectInNewWindow",
"title": "Connect to WSL in New Window",
"category": "Remote-WSL"
},
{
"command": "openremotewsl.connectUsingDistro",
"title": "Connect to WSL using Distro...",
"category": "Remote-WSL"
},
{
"command": "openremotewsl.connectUsingDistroInNewWindow",
"title": "Connect to WSL using Distro in New Window...",
"category": "Remote-WSL"
},
{
"command": "openremotewsl.showLog",
"title": "Show Log",
"category": "Remote-WSL"
},
{
"command": "openremotewsl.explorer.emptyWindowInNewWindow",
"title": "Connect in New Window",
"icon": "$(empty-window)"
},
{
"command": "openremotewsl.explorer.emptyWindowInCurrentWindow",
"title": "Connect in Current Window"
},
{
"command": "openremotewsl.explorer.reopenFolderInCurrentWindow",
"title": "Open in Current Window"
},
{
"command": "openremotewsl.explorer.reopenFolderInNewWindow",
"title": "Open in New Window",
"icon": "$(folder-opened)"
},
{
"command": "openremotewsl.explorer.deleteFolderHistoryItem",
"title": "Remove From Recent List",
"icon": "$(x)"
},
{
"command": "openremotewsl.explorer.refresh",
"title": "Refresh",
"icon": "$(refresh)"
},
{
"command": "openremotewsl.explorer.addDistro",
"title": "Add a Distro",
"icon": "$(plus)"
},
{
"command": "openremotewsl.explorer.setDefaultDistro",
"title": "Set as Default Distro"
},
{
"command": "openremotewsl.explorer.deleteDistro",
"title": "Delete Distro"
}
],
"resourceLabelFormatters": [
{
"scheme": "vscode-remote",
"authority": "wsl+*",
"formatting": {
"label": "${path}",
"separator": "/",
"tildify": true,
"workspaceSuffix": "WSL"
}
}
],
"menus": {
"statusBar/remoteIndicator": [
{
"command": "openremotewsl.connect",
"when": "(isWindows && !isWeb)",
"group": "remote_20_wsl_1general@1"
},
{
"command": "openremotewsl.connectUsingDistro",
"when": "(isWindows && !isWeb)",
"group": "remote_20_wsl_1general@2"
},
{
"command": "openremotewsl.showLog",
"when": "remoteName =~ /^wsl$/",
"group": "remote_20_wsl_1general@4"
}
],
"commandPalette": [
{
"command": "openremotewsl.connect",
"when": "(isWindows && !isWeb)"
},
{
"command": "openremotewsl.connectInNewWindow",
"when": "(isWindows && !isWeb)"
},
{
"command": "openremotewsl.connectUsingDistro",
"when": "(isWindows && !isWeb)"
},
{
"command": "openremotewsl.connectUsingDistroInNewWindow",
"when": "(isWindows && !isWeb)"
},
{
"command": "openremotewsl.explorer.refresh",
"when": "false"
},
{
"command": "openremotewsl.explorer.addDistro",
"when": "false"
},
{
"command": "openremotewsl.explorer.emptyWindowInNewWindow",
"when": "false"
},
{
"command": "openremotewsl.explorer.emptyWindowInCurrentWindow",
"when": "false"
},
{
"command": "openremotewsl.explorer.reopenFolderInCurrentWindow",
"when": "false"
},
{
"command": "openremotewsl.explorer.reopenFolderInNewWindow",
"when": "false"
},
{
"command": "openremotewsl.explorer.deleteFolderHistoryItem",
"when": "false"
},
{
"command": "openremotewsl.explorer.setDefaultDistro",
"when": "false"
},
{
"command": "openremotewsl.explorer.deleteDistro",
"when": "false"
}
],
"view/title": [
{
"command": "openremotewsl.explorer.addDistro",
"when": "view == wslTargets",
"group": "navigation"
},
{
"command": "openremotewsl.explorer.refresh",
"when": "view == wslTargets",
"group": "navigation"
}
],
"view/item/context": [
{
"command": "openremotewsl.explorer.emptyWindowInNewWindow",
"when": "viewItem =~ /^openremotewsl.explorer.distro$/",
"group": "inline@1"
},
{
"command": "openremotewsl.explorer.emptyWindowInNewWindow",
"when": "viewItem =~ /^openremotewsl.explorer.distro$/",
"group": "navigation@2"
},
{
"command": "openremotewsl.explorer.emptyWindowInCurrentWindow",
"when": "viewItem =~ /^openremotewsl.explorer.distro$/",
"group": "navigation@1"
},
{
"command": "openremotewsl.explorer.setDefaultDistro",
"when": "viewItem =~ /^openremotewsl.explorer.distro$/",
"group": "management@1"
},
{
"command": "openremotewsl.explorer.deleteDistro",
"when": "viewItem =~ /^openremotewsl.explorer.distro$/",
"group": "management@2"
},
{
"command": "openremotewsl.explorer.reopenFolderInNewWindow",
"when": "viewItem == openremotewsl.explorer.folder",
"group": "inline@1"
},
{
"command": "openremotewsl.explorer.reopenFolderInNewWindow",
"when": "viewItem == openremotewsl.explorer.folder",
"group": "navigation@2"
},
{
"command": "openremotewsl.explorer.reopenFolderInCurrentWindow",
"when": "viewItem == openremotewsl.explorer.folder",
"group": "navigation@1"
},
{
"command": "openremotewsl.explorer.deleteFolderHistoryItem",
"when": "viewItem =~ /^openremotewsl.explorer.folder/",
"group": "navigation@3"
},
{
"command": "openremotewsl.explorer.deleteFolderHistoryItem",
"when": "viewItem =~ /^openremotewsl.explorer.folder/",
"group": "inline@2"
}
]
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "gulp compile-extension:open-remote-wsl",
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
"watch": "gulp watch-extension:open-remote-wsl",
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
}
}
@@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import Log from './common/logger';
import { installCodeServer, ServerInstallError } from './serverSetup';
import { WSLManager } from './wsl/wslManager';
export const REMOTE_WSL_AUTHORITY = 'wsl';
export function getRemoteAuthority(distro: string) {
return `${REMOTE_WSL_AUTHORITY}+${distro}`;
}
class Tunnel implements vscode.Tunnel {
private _onDidDisposeEmitter = new vscode.EventEmitter<void>();
readonly onDidDispose = this._onDidDisposeEmitter.event;
constructor(
readonly remoteAddress: { port: number; host: string },
readonly localAddress: { port: number; host: string }
) {
// If ipv6 localhost 0:0:0:0:0:0:0:1 or [::1] replace with localhost
if (localAddress.host !== 'localhost' && localAddress.host !== '127.0.0.1') {
localAddress.host = 'localhost';
}
}
dispose() {
this._onDidDisposeEmitter.fire();
}
}
export class RemoteWSLResolver implements vscode.RemoteAuthorityResolver, vscode.Disposable {
private labelFormatterDisposable: vscode.Disposable | undefined;
constructor(
private readonly wslManager: WSLManager,
private readonly logger: Log
) {
}
resolve(authority: string, context: vscode.RemoteAuthorityResolverContext): Thenable<vscode.ResolverResult> {
const [type, distroName] = authority.split('+');
if (type !== REMOTE_WSL_AUTHORITY) {
throw new Error(`Invalid authority type for WSL resolver: ${type}`);
}
this.logger.info(`Resolving wsl remote authority '${authority}' (attemp #${context.resolveAttempt})`);
// It looks like default values are not loaded yet when resolving a remote,
// so let's hardcode the default values here
const remoteSSHconfig = vscode.workspace.getConfiguration('remote.WSL');
const serverDownloadUrlTemplate = remoteSSHconfig.get<string>('serverDownloadUrlTemplate');
return vscode.window.withProgress({
title: `Setting up WSL Distro: ${distroName}`,
location: vscode.ProgressLocation.Notification,
cancellable: false
}, async () => {
try {
const installResult = await installCodeServer(this.wslManager, distroName, serverDownloadUrlTemplate, [], [], this.logger);
this.labelFormatterDisposable?.dispose();
this.labelFormatterDisposable = vscode.workspace.registerResourceLabelFormatter({
scheme: 'vscode-remote',
authority: `${REMOTE_WSL_AUTHORITY}+*`,
formatting: {
label: '${path}',
separator: '/',
tildify: true,
workspaceSuffix: `WSL: ${distroName}`,
workspaceTooltip: `Running in ${distroName}`
}
});
return new vscode.ResolvedAuthority('127.0.0.1', installResult.listeningOn, installResult.connectionToken);
} catch (e: unknown) {
this.logger.error(`Error resolving authority`, e);
// Initial connection
if (context.resolveAttempt === 1) {
this.logger.show();
const closeRemote = 'Close Remote';
const retry = 'Retry';
const result = await vscode.window.showErrorMessage(`Could not establish connection to WSL distro "${distroName}"`, { modal: true }, closeRemote, retry);
if (result === closeRemote) {
await vscode.commands.executeCommand('workbench.action.remote.close');
} else if (result === retry) {
await vscode.commands.executeCommand('workbench.action.reloadWindow');
}
}
if (e instanceof ServerInstallError || !(e instanceof Error)) {
throw vscode.RemoteAuthorityResolverError.NotAvailable(e instanceof Error ? e.message : String(e));
} else {
throw vscode.RemoteAuthorityResolverError.TemporarilyNotAvailable(e.message);
}
}
});
}
async tunnelFactory(tunnelOptions: vscode.TunnelOptions) {
return new Tunnel(
tunnelOptions.remoteAddress,
{
host: tunnelOptions.remoteAddress.host,
port: tunnelOptions.localAddressPort ?? tunnelOptions.remoteAddress.port
}
);
}
dispose() {
this.labelFormatterDisposable?.dispose();
}
}
@@ -0,0 +1,86 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { getRemoteAuthority } from './authResolver';
import { WSLDistro, WSLManager, WSLOnlineDistro } from './wsl/wslManager';
import wslTerminal from './wsl/wslTerminal';
async function showDistrosPicker(wslManager: WSLManager, placeHolder: string): Promise<WSLDistro | undefined> {
const pickItemsPromise = wslManager.listDistros()
.then(distros => distros.map(distroData => {
return {
...distroData,
label: `${distroData.name}`,
detail: distroData.isDefault ? 'default distro' : undefined,
};
}));
const picked = await vscode.window.showQuickPick(pickItemsPromise, { canPickMany: false, placeHolder });
return picked;
}
async function showOnlineDistrosPicker(wslManager: WSLManager, placeHolder: string): Promise<WSLOnlineDistro | undefined> {
const pickItemsPromise = Promise.all([wslManager.listOnlineDistros(), wslManager.listDistros()])
.then(([onlineDistros, localDistros]) => {
const distroToInstall = onlineDistros.filter(d => !localDistros.some(l => l.name === d.name));
return distroToInstall.map(distroData => {
return {
...distroData,
label: `${distroData.friendlyName}`,
};
});
});
const picked = await vscode.window.showQuickPick(pickItemsPromise, { canPickMany: false, placeHolder });
return picked;
}
export async function promptOpenRemoteWSLWindow(wslManager: WSLManager, useDefault: boolean, reuseWindow: boolean) {
let distroName: string | undefined;
if (useDefault) {
const distros = await wslManager.listDistros();
distroName = distros.find(distro => distro.isDefault)?.name;
} else {
distroName = (await showDistrosPicker(wslManager, 'Select WSL distro'))?.name;
}
if (!distroName) {
return;
}
openRemoteWSLWindow(distroName, reuseWindow);
}
export async function promptInstallNewWSLDistro(wslManager: WSLManager) {
const distroName = (await showOnlineDistrosPicker(wslManager, 'Select the WSL distro to install'))?.name;
if (!distroName) {
return;
}
wslTerminal.runCommand(`wsl.exe --install -d ${distroName}`);
}
export function openRemoteWSLWindow(distro: string, reuseWindow: boolean) {
vscode.commands.executeCommand('vscode.newWindow', { remoteAuthority: getRemoteAuthority(distro), reuseWindow });
}
export function openRemoteWSLLocationWindow(distro: string, path: string, reuseWindow: boolean) {
vscode.commands.executeCommand('vscode.openFolder', vscode.Uri.from({ scheme: 'vscode-remote', authority: getRemoteAuthority(distro), path }), { forceNewWindow: !reuseWindow });
}
export async function setDefaultWSLDistro(wslManager: WSLManager, distroName: string) {
await wslManager.setDefaultDistro(distroName);
}
export async function deleteWSLDistro(wslManager: WSLManager, distroName: string) {
const deleteAction = 'Delete';
const resp = await vscode.window.showInformationMessage(`Are you sure you want to permanently delete the distro "${distroName}" including all its data?`, { modal: true }, deleteAction);
if (resp === deleteAction) {
await wslManager.deleteDistro(distroName);
return true;
}
return false;
}
@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function timeout(millis: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, millis));
}
export interface ITask<T> {
(): T;
}
export async function retry<T>(task: ITask<Promise<T>>, delay: number, retries: number): Promise<T> {
let lastError: Error | undefined;
for (let i = 0; i < retries; i++) {
try {
return await task();
} catch (error) {
lastError = error;
await timeout(delay);
}
}
throw lastError;
}
@@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
export function disposeAll(disposables: vscode.Disposable[]): void {
while (disposables.length) {
const item = disposables.pop();
if (item) {
item.dispose();
}
}
}
export abstract class Disposable {
private _isDisposed = false;
protected _disposables: vscode.Disposable[] = [];
public dispose(): any {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
disposeAll(this._disposables);
}
protected _register<T extends vscode.Disposable>(value: T): T {
if (this._isDisposed) {
value.dispose();
} else {
this._disposables.push(value);
}
return value;
}
protected get isDisposed(): boolean {
return this._isDisposed;
}
}
@@ -0,0 +1,142 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IDisposable {
dispose(): void;
}
export interface Event<T> {
(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
}
/**
* Returns a promise that resolves when the event fires, or when cancellation
* is requested, whichever happens first.
*/
export function toPromise<T>(event: Event<T>): Promise<T>;
export function toPromise<T>(event: Event<T>, signal: AbortSignal): Promise<T | undefined>;
export function toPromise<T>(event: Event<T>, signal?: AbortSignal): Promise<T | undefined> {
if (!signal) {
return new Promise<T>((resolve) => once(event, resolve));
}
if (signal.aborted) {
return Promise.resolve(undefined);
}
return new Promise((resolve) => {
const d2 = once(event, (data) => {
(signal as any).removeEventListener('abort', d1);
resolve(data);
});
const d1 = () => {
d2.dispose();
(signal as any).removeEventListener('abort', d1);
resolve(undefined);
};
(signal as any).addEventListener('abort', d1);
});
}
/**
* Adds a handler that handles one event on the emitter, then disposes itself.
*/
export const once = <T>(event: Event<T>, listener: (data: T) => void): IDisposable => {
const disposable = event((value) => {
listener(value);
disposable.dispose();
});
return disposable;
};
/**
* Base event emitter. Calls listeners when data is emitted.
*/
export class EventEmitter<T> {
private listeners?: Array<(data: T) => void> | ((data: T) => void);
/**
* Event<T> function.
*/
public readonly event: Event<T> = (listener, thisArgs, disposables) => {
const d = this.add(thisArgs ? listener.bind(thisArgs) : listener);
disposables?.push(d);
return d;
};
/**
* Gets the number of event listeners.
*/
public get size() {
if (!this.listeners) {
return 0;
} else if (typeof this.listeners === 'function') {
return 1;
} else {
return this.listeners.length;
}
}
/**
* Emits event data.
*/
public fire(value: T) {
if (!this.listeners) {
// no-op
} else if (typeof this.listeners === 'function') {
this.listeners(value);
} else {
for (const listener of this.listeners) {
listener(value);
}
}
}
/**
* Disposes of the emitter.
*/
public dispose() {
this.listeners = undefined;
}
private add(listener: (data: T) => void): IDisposable {
if (!this.listeners) {
this.listeners = listener;
} else if (typeof this.listeners === 'function') {
this.listeners = [this.listeners, listener];
} else {
this.listeners.push(listener);
}
return { dispose: () => this.rm(listener) };
}
private rm(listener: (data: T) => void) {
if (!this.listeners) {
return;
}
if (typeof this.listeners === 'function') {
if (this.listeners === listener) {
this.listeners = undefined;
}
return;
}
const index = this.listeners.indexOf(listener);
if (index === -1) {
return;
}
if (this.listeners.length === 2) {
this.listeners = index === 0 ? this.listeners[1] : this.listeners[0];
} else {
this.listeners = this.listeners.slice(0, index).concat(this.listeners.slice(index + 1));
}
}
}
@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as os from 'os';
const homeDir = os.homedir();
export async function exists(path: string) {
try {
await fs.promises.access(path);
return true;
} catch {
return false;
}
}
export function untildify(path: string) {
return path.replace(/^~(?=$|\/|\\)/, homeDir);
}
export function normalizeToSlash(path: string) {
return path.replace(/\\/g, '/');
}
@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
type LogLevel = 'Trace' | 'Info' | 'Error';
export default class Log {
private output: vscode.OutputChannel;
constructor(name: string) {
this.output = vscode.window.createOutputChannel(name);
}
private data2String(data: any): string {
if (data instanceof Error) {
return data.stack || data.message;
}
if (data.success === false && data.message) {
return data.message;
}
return data.toString();
}
public trace(message: string, data?: any): void {
this.logLevel('Trace', message, data);
}
public info(message: string, data?: any): void {
this.logLevel('Info', message, data);
}
public error(message: string, data?: any): void {
this.logLevel('Error', message, data);
}
public logLevel(level: LogLevel, message: string, data?: any): void {
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
if (data) {
this.output.appendLine(this.data2String(data));
}
}
private now(): string {
const now = new Date();
return padLeft(now.getUTCHours() + '', 2, '0')
+ ':' + padLeft(now.getMinutes() + '', 2, '0')
+ ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds();
}
public show() {
this.output.show();
}
public dispose() {
this.output.dispose();
}
}
function padLeft(s: string, n: number, pad = ' ') {
return pad.repeat(Math.max(0, n - s.length)) + s;
}
@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const isWindows = process.platform === 'win32';
export const isMacintosh = process.platform === 'darwin';
export const isLinux = process.platform === 'linux';
@@ -0,0 +1,134 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as net from 'net';
/**
* Finds a random unused port assigned by the operating system. Will reject in case no free port can be found.
*/
export function findRandomPort(): Promise<number> {
return new Promise((resolve, reject) => {
const server = net.createServer({ pauseOnConnect: true });
server.on('error', reject);
server.on('listening', () => {
const port = (server.address() as net.AddressInfo).port;
server.close(() => resolve(port));
});
server.listen(0, '127.0.0.1');
});
}
/**
* Given a start point and a max number of retries, will find a port that
* is openable. Will return 0 in case no free port can be found.
*/
export function findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride = 1): Promise<number> {
let done = false;
return new Promise(resolve => {
const timeoutHandle = setTimeout(() => {
if (!done) {
done = true;
return resolve(0);
}
}, timeout);
doFindFreePort(startPort, giveUpAfter, stride, (port) => {
if (!done) {
done = true;
clearTimeout(timeoutHandle);
return resolve(port);
}
});
});
}
function doFindFreePort(startPort: number, giveUpAfter: number, stride: number, clb: (port: number) => void): void {
if (giveUpAfter === 0) {
return clb(0);
}
const client = new net.Socket();
// If we can connect to the port it means the port is already taken so we continue searching
client.once('connect', () => {
dispose(client);
return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb);
});
client.once('data', () => {
// this listener is required since node.js 8.x
});
client.once('error', (err: Error & { code?: string }) => {
dispose(client);
// If we receive any non ECONNREFUSED error, it means the port is used but we cannot connect
if (err.code !== 'ECONNREFUSED') {
return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb);
}
// Otherwise it means the port is free to use!
return clb(startPort);
});
client.connect(startPort, '127.0.0.1');
}
/**
* Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener.
*/
export function findFreePortFaster(startPort: number, giveUpAfter: number, timeout: number): Promise<number> {
let resolved = false;
let timeoutHandle: NodeJS.Timeout | undefined = undefined;
let countTried = 1;
const server = net.createServer({ pauseOnConnect: true });
function doResolve(port: number, resolve: (port: number) => void) {
if (!resolved) {
resolved = true;
server.removeAllListeners();
server.close();
if (timeoutHandle) {
clearTimeout(timeoutHandle);
}
resolve(port);
}
}
return new Promise<number>(resolve => {
timeoutHandle = setTimeout(() => {
doResolve(0, resolve);
}, timeout);
server.on('listening', () => {
doResolve(startPort, resolve);
});
server.on('error', err => {
if (err && ((<any>err).code === 'EADDRINUSE' || (<any>err).code === 'EACCES') && (countTried < giveUpAfter)) {
startPort++;
countTried++;
server.listen(startPort, '127.0.0.1');
} else {
doResolve(0, resolve);
}
});
server.on('close', () => {
doResolve(0, resolve);
});
server.listen(startPort, '127.0.0.1');
});
}
function dispose(socket: net.Socket): void {
try {
socket.removeAllListeners('connect');
socket.removeAllListeners('error');
socket.end();
socket.destroy();
socket.unref();
} catch (error) {
console.error(error); // otherwise this error would get lost in the callback chain
}
}
@@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as path from 'path';
import { RemoteLocationHistory } from './remoteLocationHistory';
import { Disposable } from './common/disposable';
import { openRemoteWSLWindow, openRemoteWSLLocationWindow, promptInstallNewWSLDistro, deleteWSLDistro, setDefaultWSLDistro } from './commands';
import { WSLManager } from './wsl/wslManager';
class DistroItem {
constructor(
public name: string,
public isDefault: boolean,
public locations: string[]
) {
}
}
class DistroLocationItem {
constructor(
public path: string,
public name: string
) {
}
}
type DataTreeItem = DistroItem | DistroLocationItem;
export class DistroTreeDataProvider extends Disposable implements vscode.TreeDataProvider<DataTreeItem> {
private readonly _onDidChangeTreeData = this._register(new vscode.EventEmitter<DataTreeItem | DataTreeItem[] | void>());
public readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
constructor(
private readonly locationHistory: RemoteLocationHistory,
private readonly wslManager: WSLManager
) {
super();
this._register(vscode.commands.registerCommand('openremotewsl.explorer.addDistro', () => promptInstallNewWSLDistro(wslManager)));
this._register(vscode.commands.registerCommand('openremotewsl.explorer.refresh', () => this.refresh()));
this._register(vscode.commands.registerCommand('openremotewsl.explorer.emptyWindowInNewWindow', e => this.openRemoteWSLWindow(e, false)));
this._register(vscode.commands.registerCommand('openremotewsl.explorer.emptyWindowInCurrentWindow', e => this.openRemoteWSLWindow(e, true)));
this._register(vscode.commands.registerCommand('openremotewsl.explorer.reopenFolderInNewWindow', e => this.openRemoteWSLocationWindow(e, false)));
this._register(vscode.commands.registerCommand('openremotewsl.explorer.reopenFolderInCurrentWindow', e => this.openRemoteWSLocationWindow(e, true)));
this._register(vscode.commands.registerCommand('openremotewsl.explorer.deleteFolderHistoryItem', e => this.deleteDistroLocation(e)));
this._register(vscode.commands.registerCommand('openremotewsl.explorer.setDefaultDistro', e => this.setDefaultDistro(e)));
this._register(vscode.commands.registerCommand('openremotewsl.explorer.deleteDistro', e => this.deleteDistro(e)));
}
getTreeItem(element: DataTreeItem): vscode.TreeItem {
if (element instanceof DistroLocationItem) {
const label = path.posix.basename(element.path).replace(/\.code-workspace$/, ' (Workspace)');
const treeItem = new vscode.TreeItem(label);
treeItem.description = path.posix.dirname(element.path);
treeItem.iconPath = new vscode.ThemeIcon('folder');
treeItem.contextValue = 'openremotewsl.explorer.folder';
return treeItem;
}
const treeItem = new vscode.TreeItem(element.name);
treeItem.description = element.isDefault ? 'default distro' : undefined;
treeItem.collapsibleState = element.locations.length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None;
treeItem.iconPath = new vscode.ThemeIcon('vm');
treeItem.contextValue = 'openremotewsl.explorer.distro';
return treeItem;
}
async getChildren(element?: DistroItem): Promise<DataTreeItem[]> {
if (!element) {
const distros = await this.wslManager.listDistros();
return distros.map(distro => new DistroItem(distro.name, distro.isDefault, this.locationHistory.getHistory(distro.name)));
}
if (element instanceof DistroItem) {
return element.locations.map(location => new DistroLocationItem(location, element.name));
}
return [];
}
private refresh() {
this._onDidChangeTreeData.fire();
}
private async deleteDistroLocation(element: DistroLocationItem) {
await this.locationHistory.removeLocation(element.name, element.path);
this.refresh();
}
private async openRemoteWSLWindow(element: DistroItem, reuseWindow: boolean) {
openRemoteWSLWindow(element.name, reuseWindow);
}
private async openRemoteWSLocationWindow(element: DistroLocationItem, reuseWindow: boolean) {
openRemoteWSLLocationWindow(element.name, element.path, reuseWindow);
}
private async setDefaultDistro(element: DistroItem) {
await setDefaultWSLDistro(this.wslManager, element.name);
this.refresh();
}
private async deleteDistro(element: DistroItem) {
await deleteWSLDistro(this.wslManager, element.name);
this.refresh();
}
}
@@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import Log from './common/logger';
import { RemoteWSLResolver, REMOTE_WSL_AUTHORITY } from './authResolver';
import { promptOpenRemoteWSLWindow } from './commands';
import { DistroTreeDataProvider } from './distroTreeView';
import { getRemoteWorkspaceLocationData, RemoteLocationHistory } from './remoteLocationHistory';
import { WSLManager } from './wsl/wslManager';
import { isWindows } from './common/platform';
export async function activate(context: vscode.ExtensionContext) {
if (!isWindows) {
return;
}
const logger = new Log('Remote - WSL');
context.subscriptions.push(logger);
const wslManager = new WSLManager(logger);
const remoteWSLResolver = new RemoteWSLResolver(wslManager, logger);
context.subscriptions.push(vscode.workspace.registerRemoteAuthorityResolver(REMOTE_WSL_AUTHORITY, remoteWSLResolver));
context.subscriptions.push(remoteWSLResolver);
const locationHistory = new RemoteLocationHistory(context);
const locationData = getRemoteWorkspaceLocationData();
if (locationData) {
await locationHistory.addLocation(locationData[0], locationData[1]);
}
const distroTreeDataProvider = new DistroTreeDataProvider(locationHistory, wslManager);
context.subscriptions.push(vscode.window.createTreeView('wslTargets', { treeDataProvider: distroTreeDataProvider }));
context.subscriptions.push(distroTreeDataProvider);
context.subscriptions.push(vscode.commands.registerCommand('openremotewsl.connect', () => promptOpenRemoteWSLWindow(wslManager, true, true)));
context.subscriptions.push(vscode.commands.registerCommand('openremotewsl.connectInNewWindow', () => promptOpenRemoteWSLWindow(wslManager, true, false)));
context.subscriptions.push(vscode.commands.registerCommand('openremotewsl.connectUsingDistro', () => promptOpenRemoteWSLWindow(wslManager, false, true)));
context.subscriptions.push(vscode.commands.registerCommand('openremotewsl.connectUsingDistroInNewWindow', () => promptOpenRemoteWSLWindow(wslManager, false, false)));
context.subscriptions.push(vscode.commands.registerCommand('openremotewsl.showLog', () => logger.show()));
}
export function deactivate() {
}
@@ -0,0 +1,56 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { REMOTE_WSL_AUTHORITY } from './authResolver';
export class RemoteLocationHistory {
private static STORAGE_KEY = 'remoteLocationHistory_v0';
private remoteLocationHistory: Record<string, string[]> = {};
constructor(private context: vscode.ExtensionContext) {
// context.globalState.update(RemoteLocationHistory.STORAGE_KEY, undefined);
this.remoteLocationHistory = context.globalState.get(RemoteLocationHistory.STORAGE_KEY) || {};
}
getHistory(host: string): string[] {
return this.remoteLocationHistory[host] || [];
}
async addLocation(host: string, path: string) {
const hostLocations = this.remoteLocationHistory[host] || [];
if (!hostLocations.includes(path)) {
hostLocations.unshift(path);
this.remoteLocationHistory[host] = hostLocations;
await this.context.globalState.update(RemoteLocationHistory.STORAGE_KEY, this.remoteLocationHistory);
}
}
async removeLocation(host: string, path: string) {
let hostLocations = this.remoteLocationHistory[host] || [];
hostLocations = hostLocations.filter(l => l !== path);
this.remoteLocationHistory[host] = hostLocations;
await this.context.globalState.update(RemoteLocationHistory.STORAGE_KEY, this.remoteLocationHistory);
}
}
export function getRemoteWorkspaceLocationData(): [string, string] | undefined {
let location = vscode.workspace.workspaceFile;
if (location && location.scheme === 'vscode-remote' && location.authority.startsWith(REMOTE_WSL_AUTHORITY) && location.path.endsWith('.code-workspace')) {
const [, distroName] = location.authority.split('+');
return [distroName, location.path];
}
location = vscode.workspace.workspaceFolders?.[0].uri;
if (location && location.scheme === 'vscode-remote' && location.authority.startsWith(REMOTE_WSL_AUTHORITY)) {
const [, distroName] = location.authority.split('+');
return [distroName, location.path];
}
return undefined;
}
@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
let vscodeProductJson: any;
async function getVSCodeProductJson() {
if (!vscodeProductJson) {
const productJsonStr = await fs.promises.readFile(path.join(vscode.env.appRoot, 'product.json'), 'utf8');
vscodeProductJson = JSON.parse(productJsonStr);
}
return vscodeProductJson;
}
export interface IServerConfig {
version: string;
commit: string;
quality: string;
release?: string; // void-like specific
serverApplicationName: string;
serverDataFolderName: string;
serverDownloadUrlTemplate?: string; // void-like specific
}
export async function getVSCodeServerConfig(): Promise<IServerConfig> {
const productJson = await getVSCodeProductJson();
return {
// version: vscode.version.replace('-insider', ''),
commit: productJson.commit,
quality: productJson.quality,
release: productJson.release,
serverApplicationName: productJson.serverApplicationName,
serverDataFolderName: productJson.serverDataFolderName,
serverDownloadUrlTemplate: productJson.serverDownloadUrlTemplate,
// Void changed this
version: productJson.voidVersion
};
}
@@ -0,0 +1,353 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as crypto from 'crypto';
import Log from './common/logger';
import { getVSCodeServerConfig } from './serverConfig';
import { WSLManager } from './wsl/wslManager';
export interface ServerInstallOptions {
id: string;
quality: string;
commit: string;
version: string;
release?: string; // void specific
extensionIds: string[];
envVariables: string[];
serverApplicationName: string;
serverDataFolderName: string;
serverDownloadUrlTemplate: string;
}
export interface ServerInstallResult {
exitCode: number;
listeningOn: number;
connectionToken: string;
logFile: string;
osReleaseId: string;
arch: string;
platform: string;
tmpDir: string;
[key: string]: any;
}
export class ServerInstallError extends Error {
constructor(message: string) {
super(message);
}
}
const DEFAULT_DOWNLOAD_URL_TEMPLATE = 'https://github.com/voideditor/binaries/releases/download/${version}.${release}/void-reh-${os}-${arch}-${version}.${release}.tar.gz';
export async function installCodeServer(wslManager: WSLManager, distroName: string, serverDownloadUrlTemplate: string | undefined, extensionIds: string[], envVariables: string[], logger: Log): Promise<ServerInstallResult> {
const scriptId = crypto.randomBytes(12).toString('hex');
const vscodeServerConfig = await getVSCodeServerConfig();
const installOptions: ServerInstallOptions = {
id: scriptId,
version: vscodeServerConfig.version,
commit: vscodeServerConfig.commit,
quality: vscodeServerConfig.quality,
release: vscodeServerConfig.release,
extensionIds,
envVariables,
serverApplicationName: vscodeServerConfig.serverApplicationName,
serverDataFolderName: vscodeServerConfig.serverDataFolderName,
serverDownloadUrlTemplate: serverDownloadUrlTemplate ?? vscodeServerConfig.serverDownloadUrlTemplate ?? DEFAULT_DOWNLOAD_URL_TEMPLATE,
};
const installServerScript = generateBashInstallScript(installOptions);
// Fish shell does not support heredoc so let's workaround it using -c option,
// also replace single quotes (') within the script with ('\'') as there's no quoting within single quotes, see https://unix.stackexchange.com/a/24676
const resp = await wslManager.exec('bash', ['-c', `'${installServerScript.replace(/'/g, `'\\''`)}'`], distroName);
const endScriptRegex = new RegExp(`${scriptId}: Server installation script done`, 'm');
const commandOutput = await Promise.race([
resp.exitPromise.then(result => ({ stdout: resp.stdout, stderr: resp.stderr, exitCode: result.exitCode })),
new Promise<{ stdout: string; stderr: string; exitCode: number }>((resolve) => {
resp.onStdoutData(buffer => {
if (endScriptRegex.test(buffer.toString('utf8'))) {
resolve({ stdout: resp.stdout, stderr: resp.stderr, exitCode: 0 });
}
});
})
]);
if (commandOutput.exitCode) {
logger.trace('Server install command stderr:', commandOutput.stderr);
}
logger.trace('Server install command stdout:', commandOutput.stdout);
const resultMap = parseServerInstallOutput(commandOutput.stdout, scriptId);
if (!resultMap) {
throw new ServerInstallError(`Failed parsing install script output`);
}
const exitCode = parseInt(resultMap.exitCode, 10);
if (exitCode !== 0) {
throw new ServerInstallError(`Couldn't install void server on remote server, install script returned non-zero exit status`);
}
const listeningOn = parseInt(resultMap.listeningOn, 10);
const remoteEnvVars = Object.fromEntries(Object.entries(resultMap).filter(([key,]) => envVariables.includes(key)));
return {
exitCode,
listeningOn,
connectionToken: resultMap.connectionToken,
logFile: resultMap.logFile,
osReleaseId: resultMap.osReleaseId,
arch: resultMap.arch,
platform: resultMap.platform,
tmpDir: resultMap.tmpDir,
...remoteEnvVars
};
}
function parseServerInstallOutput(str: string, scriptId: string): { [k: string]: string } | undefined {
const startResultStr = `${scriptId}: start`;
const endResultStr = `${scriptId}: end`;
const startResultIdx = str.indexOf(startResultStr);
if (startResultIdx < 0) {
return undefined;
}
const endResultIdx = str.indexOf(endResultStr, startResultIdx + startResultStr.length);
if (endResultIdx < 0) {
return undefined;
}
const installResult = str.substring(startResultIdx + startResultStr.length, endResultIdx);
const resultMap: { [k: string]: string } = {};
const resultArr = installResult.split(/\r?\n/);
for (const line of resultArr) {
const [key, value] = line.split('==');
resultMap[key] = value;
}
return resultMap;
}
function generateBashInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate }: ServerInstallOptions) {
const extensions = extensionIds.map(id => '--install-extension ' + id).join(' ');
return `
# Server installation script
TMP_DIR="\${XDG_RUNTIME_DIR:-"/tmp"}"
DISTRO_VERSION="${version}"
DISTRO_COMMIT="${commit}"
DISTRO_QUALITY="${quality}"
DISTRO_VSCODIUM_RELEASE="${release ?? ''}"
SERVER_APP_NAME="${serverApplicationName}"
SERVER_INITIAL_EXTENSIONS="${extensions}"
SERVER_LISTEN_FLAG="--port=0"
SERVER_DATA_DIR="$HOME/${serverDataFolderName}"
SERVER_DIR="$SERVER_DATA_DIR/bin/$DISTRO_COMMIT"
SERVER_SCRIPT="$SERVER_DIR/bin/$SERVER_APP_NAME"
SERVER_LOGFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.log"
SERVER_PIDFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.pid"
SERVER_TOKENFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.token"
SERVER_OS=
SERVER_ARCH=
SERVER_CONNECTION_TOKEN=
SERVER_DOWNLOAD_URL=
LISTENING_ON=
OS_RELEASE_ID=
ARCH=
PLATFORM=
# Mimic output from logs of remote-ssh extension
print_install_results_and_exit() {
echo "${id}: start"
echo "exitCode==$1=="
echo "listeningOn==$LISTENING_ON=="
echo "connectionToken==$SERVER_CONNECTION_TOKEN=="
echo "logFile==$SERVER_LOGFILE=="
echo "osReleaseId==$OS_RELEASE_ID=="
echo "arch==$ARCH=="
echo "platform==$PLATFORM=="
echo "tmpDir==$TMP_DIR=="
${envVariables.map(envVar => `echo "${envVar}==$${envVar}=="`).join('\n')}
echo "${id}: end"
exit 0
}
# Check if platform is supported
PLATFORM="$(uname -s)"
case $PLATFORM in
Linux)
SERVER_OS="linux"
;;
*)
echo "Error platform not supported: $PLATFORM"
print_install_results_and_exit 1
;;
esac
# Check machine architecture
ARCH="$(uname -m)"
case $ARCH in
x86_64 | amd64)
SERVER_ARCH="x64"
;;
armv7l | armv8l)
SERVER_ARCH="armhf"
;;
arm64 | aarch64)
SERVER_ARCH="arm64"
;;
*)
echo "Error architecture not supported: $ARCH"
print_install_results_and_exit 1
;;
esac
# https://www.freedesktop.org/software/systemd/man/os-release.html
OS_RELEASE_ID="$(grep -i '^ID=' /etc/os-release 2>/dev/null | sed 's/^ID=//gi' | sed 's/"//g')"
if [[ -z $OS_RELEASE_ID ]]; then
OS_RELEASE_ID="$(grep -i '^ID=' /usr/lib/os-release 2>/dev/null | sed 's/^ID=//gi' | sed 's/"//g')"
if [[ -z $OS_RELEASE_ID ]]; then
OS_RELEASE_ID="unknown"
fi
fi
# Create installation folder
if [[ ! -d $SERVER_DIR ]]; then
mkdir -p $SERVER_DIR
if (( $? > 0 )); then
echo "Error creating server install directory"
print_install_results_and_exit 1
fi
fi
SERVER_DOWNLOAD_URL="$(echo "${serverDownloadUrlTemplate.replace(/\$\{/g, '\\${')}" | sed "s/\\\${quality}/$DISTRO_QUALITY/g" | sed "s/\\\${version}/$DISTRO_VERSION/g" | sed "s/\\\${commit}/$DISTRO_COMMIT/g" | sed "s/\\\${os}/$SERVER_OS/g" | sed "s/\\\${arch}/$SERVER_ARCH/g" | sed "s/\\\${release}/$DISTRO_VSCODIUM_RELEASE/g")"
# Check if server script is already installed
if [[ ! -f $SERVER_SCRIPT ]]; then
if [[ "$SERVER_OS" = "dragonfly" ]] || [[ "$SERVER_OS" = "freebsd" ]]; then
echo "Error "$SERVER_OS" needs manual installation of remote extension host"
print_install_results_and_exit 1
fi
pushd $SERVER_DIR > /dev/null
if [[ ! -z $(which wget) ]]; then
wget --tries=3 --timeout=10 --continue --no-verbose -O vscode-server.tar.gz $SERVER_DOWNLOAD_URL
elif [[ ! -z $(which curl) ]]; then
curl --retry 3 --connect-timeout 10 --location --show-error --silent --output vscode-server.tar.gz $SERVER_DOWNLOAD_URL
else
echo "Error no tool to download server binary"
print_install_results_and_exit 1
fi
if (( $? > 0 )); then
echo "Error downloading server from $SERVER_DOWNLOAD_URL"
print_install_results_and_exit 1
fi
tar -xf vscode-server.tar.gz --strip-components 1
if (( $? > 0 )); then
echo "Error while extracting server contents"
print_install_results_and_exit 1
fi
if [[ ! -f $SERVER_SCRIPT ]]; then
echo "Error server contents are corrupted"
print_install_results_and_exit 1
fi
rm -f vscode-server.tar.gz
popd > /dev/null
else
echo "Server script already installed in $SERVER_SCRIPT"
fi
# Try to find if server is already running
if [[ -f $SERVER_PIDFILE ]]; then
SERVER_PID="$(cat $SERVER_PIDFILE)"
SERVER_RUNNING_PROCESS="$(ps -o pid,args -p $SERVER_PID | grep $SERVER_SCRIPT)"
else
SERVER_RUNNING_PROCESS="$(ps -o pid,args -A | grep $SERVER_SCRIPT | grep -v grep)"
fi
if [[ -z $SERVER_RUNNING_PROCESS ]]; then
if [[ -f $SERVER_LOGFILE ]]; then
rm $SERVER_LOGFILE
fi
if [[ -f $SERVER_TOKENFILE ]]; then
rm $SERVER_TOKENFILE
fi
touch $SERVER_TOKENFILE
chmod 600 $SERVER_TOKENFILE
SERVER_CONNECTION_TOKEN="${crypto.randomUUID()}"
echo $SERVER_CONNECTION_TOKEN > $SERVER_TOKENFILE
$SERVER_SCRIPT --start-server --host=127.0.0.1 $SERVER_LISTEN_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file $SERVER_TOKENFILE --telemetry-level off --use-host-proxy --disable-websocket-compression --without-browser-env-var --enable-remote-auto-shutdown --accept-server-license-terms &> $SERVER_LOGFILE &
echo $! > $SERVER_PIDFILE
else
echo "Server script is already running $SERVER_SCRIPT"
fi
if [[ -f $SERVER_TOKENFILE ]]; then
SERVER_CONNECTION_TOKEN="$(cat $SERVER_TOKENFILE)"
else
echo "Error server token file not found $SERVER_TOKENFILE"
print_install_results_and_exit 1
fi
if [[ -f $SERVER_LOGFILE ]]; then
for i in {1..5}; do
LISTENING_ON="$(cat $SERVER_LOGFILE | grep -E 'Extension host agent listening on .+' | sed 's/Extension host agent listening on //')"
if [[ -n $LISTENING_ON ]]; then
break
fi
sleep 0.5
done
if [[ -z $LISTENING_ON ]]; then
echo "Error server did not start sucessfully"
print_install_results_and_exit 1
fi
else
echo "Error server log file not found $SERVER_LOGFILE"
print_install_results_and_exit 1
fi
# Finish server setup and keep script running
if [[ -z $SERVER_RUNNING_PROCESS ]]; then
echo "${id}: start"
echo "exitCode==0=="
echo "listeningOn==$LISTENING_ON=="
echo "connectionToken==$SERVER_CONNECTION_TOKEN=="
echo "logFile==$SERVER_LOGFILE=="
echo "osReleaseId==$OS_RELEASE_ID=="
echo "arch==$ARCH=="
echo "platform==$PLATFORM=="
echo "tmpDir==$TMP_DIR=="
${envVariables.map(envVar => `echo "${envVar}==$${envVar}=="`).join('\n')}
echo "${id}: end"
echo "${id}: Server installation script done"
SERVER_PID="$(cat $SERVER_PIDFILE)"
SERVER_RUNNING_PROCESS="$(ps -o pid,args -p $SERVER_PID | grep $SERVER_SCRIPT)"
while [[ -n $SERVER_RUNNING_PROCESS ]]; do
sleep 300;
SERVER_RUNNING_PROCESS="$(ps -o pid,args -p $SERVER_PID | grep $SERVER_SCRIPT)"
done
else
print_install_results_and_exit 0
fi
`;
}
@@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import Log from '../common/logger';
import { EventEmitter } from '../common/event';
const wslBinary = 'wsl.exe';
export interface WSLDistro {
isDefault: boolean;
name: string;
state: string;
version: string;
}
export interface WSLOnlineDistro {
name: string;
friendlyName: string;
}
export class WSLManager {
constructor(private readonly logger: Log) {
}
async listDistros() {
const resp = this._runWSLCommand(['--list', '--verbose'], 'utf16le');
const { exitCode } = await resp.exitPromise;
const { stdout, stderr } = resp;
if (exitCode) {
this.logger.trace(`Command wsl listDistros exited with code ${exitCode}`, stdout + '\n\n' + stderr);
throw new Error(`Command wsl listDistros exited with code ${exitCode}`);
}
const regex = /(?<default>\*|\s)\s+(?<name>[\w\.-]+)\s+(?<state>[\w]+)\s+(?<version>\d)/;
const distros: WSLDistro[] = [];
for (const line of stdout.split(/\r?\n/)) {
const matches = line.match(regex);
if (matches && matches.groups) {
distros.push({
isDefault: matches.groups.default === '*',
name: matches.groups.name,
state: matches.groups.state,
version: matches.groups.version,
});
}
}
return distros;
}
async listOnlineDistros() {
const resp = this._runWSLCommand(['--list', '--online'], 'utf16le');
const { exitCode } = await resp.exitPromise;
const { stdout, stderr } = resp;
if (exitCode) {
this.logger.trace(`Command wsl listOnlineDistros exited with code ${exitCode}`, stdout + '\n\n' + stderr);
throw new Error(`Command wsl listOnlineDistros exited with code ${exitCode}`);
}
let lines = stdout.split(/\r?\n/);
const idx = lines.findIndex(l => /\s*NAME\s+FRIENDLY NAME\s*/.test(l));
lines = lines.slice(idx + 1);
const regex = /(?<name>[\w\.-]+)\s+(?<friendlyName>\w.+\w)/;
const distros: WSLOnlineDistro[] = [];
for (const line of lines) {
const matches = line.match(regex);
if (matches && matches.groups) {
distros.push({
name: matches.groups.name,
friendlyName: matches.groups.friendlyName,
});
}
}
return distros;
}
async setDefaultDistro(distroName: string) {
const resp = this._runWSLCommand(['--set-default', distroName], 'utf16le');
const { exitCode } = await resp.exitPromise;
const { stdout, stderr } = resp;
if (exitCode) {
this.logger.trace(`Command wsl setDefaultDistro exited with code ${exitCode}`, stdout + '\n\n' + stderr);
throw new Error(`Command wsl setDefaultDistro exited with code ${exitCode}`);
}
}
async deleteDistro(distroName: string) {
const resp = this._runWSLCommand(['--unregister', distroName], 'utf16le');
const { exitCode } = await resp.exitPromise;
const { stdout, stderr } = resp;
if (exitCode) {
this.logger.trace(`Command wsl deleteDistro exited with code ${exitCode}`, stdout + '\n\n' + stderr);
throw new Error(`Command wsl deleteDistro exited with code ${exitCode}`);
}
}
async exec(cmd: string, args: string[], distro: string) {
return this._runWSLCommand(['--distribution', distro, '--', cmd, ...args], 'utf8');
}
private _runWSLCommand(args: string[], encoding: 'utf8' | 'utf16le') {
this.logger.trace(`Running WSL command: ${wslBinary} ${args.join(' ')}`);
const cmd = cp.spawn(wslBinary, args, { windowsHide: true, windowsVerbatimArguments: true });
const stdoutDataEmitter = new EventEmitter<Buffer>();
const stdoutData: Buffer[] = [];
const stderrDataEmitter = new EventEmitter<Buffer>();
const stderrData: Buffer[] = [];
cmd.stdout.on('data', (data: Buffer) => {
stdoutData.push(data);
stdoutDataEmitter.fire(data);
});
cmd.stderr.on('data', (data: Buffer) => {
stderrData.push(data);
stderrDataEmitter.fire(data);
});
const exitPromise = new Promise<{ exitCode: number }>((resolve, reject) => {
cmd.on('error', (err) => {
this.logger.error(`Error running WSL command: ${wslBinary} ${args.join(' ')}`, err);
reject(err);
});
cmd.on('exit', (code, _signal) => {
resolve({ exitCode: code ?? 0 });
});
});
return {
get stdout() {
return Buffer.concat(stdoutData).toString(encoding);
},
get stderr() {
return Buffer.concat(stderrData).toString(encoding);
},
get onStdoutData() {
return stdoutDataEmitter.event;
},
get onStderrData() {
return stderrDataEmitter.event;
},
exitPromise
};
}
}
@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
class WSLTerminal {
static NAME = 'WSL';
private getTerminal() {
const wslTerminal = vscode.window.terminals.find(t => t.name === WSLTerminal.NAME);
if (wslTerminal) {
return wslTerminal;
}
return vscode.window.createTerminal(WSLTerminal.NAME);
}
runCommand(command: string) {
const wslTerminal = this.getTerminal();
wslTerminal.show(false);
wslTerminal.sendText(command, true);
}
}
export default new WSLTerminal();
+12
View File
@@ -0,0 +1,12 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
},
"include": [
"src/**/*",
"../../src/vscode-dts/vscode.d.ts",
"../../src/vscode-dts/vscode.proposed.resolvers.d.ts",
"../../src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts",
]
}
+93 -18
View File
@@ -62,6 +62,7 @@
"posthog-node": "^4.8.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-tooltip": "^5.28.1",
"tas-client-umd": "0.2.0",
"v8-inspect-profiler": "^0.1.1",
"vscode-html-languageservice": "^5.3.1",
@@ -75,6 +76,7 @@
"devDependencies": {
"@playwright/test": "^1.50.0",
"@stylistic/eslint-plugin-ts": "^2.8.0",
"@tailwindcss/typography": "^0.5.16",
"@types/cookie": "^0.3.3",
"@types/debug": "^4.1.5",
"@types/diff": "^7.0.1",
@@ -116,7 +118,7 @@
"cssnano": "^6.0.3",
"debounce": "^1.0.0",
"deemon": "^1.8.0",
"electron": "34.2.0",
"electron": "34.3.2",
"eslint": "^9.11.1",
"eslint-formatter-compact": "^8.40.0",
"eslint-plugin-header": "3.1.1",
@@ -168,7 +170,7 @@
"pump": "^1.0.1",
"rcedit": "^1.1.0",
"rimraf": "^2.7.1",
"scope-tailwind": "^1.0.6",
"scope-tailwind": "^1.0.9",
"sinon": "^12.0.1",
"sinon-test": "^3.1.3",
"source-map": "0.6.1",
@@ -3586,6 +3588,13 @@
"tslib": "^2.8.0"
}
},
"node_modules/@swc/helpers/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
@@ -3598,6 +3607,36 @@
"node": ">=10"
}
},
"node_modules/@tailwindcss/typography": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
"integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
"dev": true,
"license": "MIT",
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"postcss-selector-parser": "6.0.10"
},
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
}
},
"node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
"version": "6.0.10",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@thisismanta/pessimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@thisismanta/pessimist/-/pessimist-1.2.0.tgz",
@@ -6583,6 +6622,12 @@
"node": ">=0.10.0"
}
},
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
"license": "MIT"
},
"node_modules/cli-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
@@ -7970,9 +8015,9 @@
"dev": true
},
"node_modules/electron": {
"version": "34.2.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-34.2.0.tgz",
"integrity": "sha512-SYwBJNeXBTm1q/ErybQMUBZAYqEreBUqBwTrNkw1rV4YatDZk5Aittpcus3PPeC4UoI/tqmJ946uG8AKHTd6CA==",
"version": "34.3.2",
"resolved": "https://registry.npmjs.org/electron/-/electron-34.3.2.tgz",
"integrity": "sha512-n9tzmFexVLxipZXwMTY30H10f0X9k2OP0SkpSwL5VvnDZi0l/Hc+8CEArKkQPbbSf/IS7nxgc96gtTaR+XoSBg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -14039,6 +14084,13 @@
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY= sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
"dev": true
},
"node_modules/lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.clone": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
@@ -14057,6 +14109,13 @@
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
"dev": true
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -15042,10 +15101,11 @@
}
},
"node_modules/nan": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/nanoid": {
@@ -17947,6 +18007,20 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/react-tooltip": {
"version": "5.28.1",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.1.tgz",
"integrity": "sha512-ZA4oHwoIIK09TS7PvSLFcRlje1wGZaxw6xHvfrzn6T82UcMEfEmHVCad16Gnr4NDNDh93HyN037VK4HDi5odfQ==",
"license": "MIT",
"dependencies": {
"@floating-ui/dom": "^1.6.1",
"classnames": "^2.3.0"
},
"peerDependencies": {
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -18755,10 +18829,11 @@
}
},
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"dev": true,
"license": "ISC"
},
"node_modules/scheduler": {
"version": "0.25.0",
@@ -18820,9 +18895,9 @@
"dev": true
},
"node_modules/scope-tailwind": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/scope-tailwind/-/scope-tailwind-1.0.6.tgz",
"integrity": "sha512-tkISLsaesYKKXL9YrLsRWFOD/FhrRVGKeinjgTuFtEidryLzwlBB3G17ArmHWHYcfdMp00XwnRMcGFkF8wwG6w==",
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/scope-tailwind/-/scope-tailwind-1.0.9.tgz",
"integrity": "sha512-sxtAKxJq143lYK/RCE36YGq13ficBZ9/9Z0TZa78k0AEiKNT5nH4kfhD8YAfEXR/qPR+G7tl9KL4UoHh+Cs93g==",
"dev": true,
"license": "AGPL-3.0-only",
"dependencies": {
@@ -21398,9 +21473,9 @@
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
"dev": true,
"license": "0BSD"
},
+4 -2
View File
@@ -123,6 +123,7 @@
"posthog-node": "^4.8.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-tooltip": "^5.28.1",
"tas-client-umd": "0.2.0",
"v8-inspect-profiler": "^0.1.1",
"vscode-html-languageservice": "^5.3.1",
@@ -136,6 +137,7 @@
"devDependencies": {
"@playwright/test": "^1.50.0",
"@stylistic/eslint-plugin-ts": "^2.8.0",
"@tailwindcss/typography": "^0.5.16",
"@types/cookie": "^0.3.3",
"@types/debug": "^4.1.5",
"@types/diff": "^7.0.1",
@@ -177,7 +179,7 @@
"cssnano": "^6.0.3",
"debounce": "^1.0.0",
"deemon": "^1.8.0",
"electron": "34.2.0",
"electron": "34.3.2",
"eslint": "^9.11.1",
"eslint-formatter-compact": "^8.40.0",
"eslint-plugin-header": "3.1.1",
@@ -229,7 +231,7 @@
"pump": "^1.0.1",
"rcedit": "^1.1.0",
"rimraf": "^2.7.1",
"scope-tailwind": "^1.0.6",
"scope-tailwind": "^1.0.9",
"sinon": "^12.0.1",
"sinon-test": "^3.1.3",
"source-map": "0.6.1",
+6 -1
View File
@@ -1,6 +1,7 @@
{
"nameShort": "Void",
"nameLong": "Void",
"voidVersion": "1.2.1",
"applicationName": "void",
"dataFolderName": ".void-editor",
"win32MutexName": "voideditor",
@@ -34,5 +35,9 @@
"serviceUrl": "https://marketplace.visualstudio.com/_apis/public/gallery",
"itemUrl": "https://marketplace.visualstudio.com/items"
},
"builtInExtensions": []
"builtInExtensions": [],
"linkProtectionTrustedDomains": [
"https://voideditor.com",
"https://voideditor.dev"
]
}
+1231 -1039
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 200 KiB

+36 -3
View File
@@ -107,13 +107,46 @@ find . -maxdepth 1 ! -name VoidApp.AppDir ! -name "." ! -name ".." -exec cp -r {
cp void.png VoidApp.AppDir/ && \
echo "[Desktop Entry]" > VoidApp.AppDir/void.desktop && \
echo "Name=Void" >> VoidApp.AppDir/void.desktop && \
echo "Exec=void" >> VoidApp.AppDir/void.desktop && \
echo "Comment=Open source AI code editor." >> VoidApp.AppDir/void.desktop && \
echo "GenericName=Text Editor" >> VoidApp.AppDir/void.desktop && \
echo "Exec=void %F" >> VoidApp.AppDir/void.desktop && \
echo "Icon=void" >> VoidApp.AppDir/void.desktop && \
echo "Type=Application" >> VoidApp.AppDir/void.desktop && \
echo "Categories=Utility;" >> VoidApp.AppDir/void.desktop && \
echo "Comment=Void Linux Application" >> VoidApp.AppDir/void.desktop && \
echo "StartupNotify=false" >> VoidApp.AppDir/void.desktop && \
echo "StartupWMClass=Void" >> VoidApp.AppDir/void.desktop && \
echo "Categories=TextEditor;Development;IDE;" >> VoidApp.AppDir/void.desktop && \
echo "MimeType=application/x-void-workspace;" >> VoidApp.AppDir/void.desktop && \
echo "Keywords=void;" >> VoidApp.AppDir/void.desktop && \
echo "Actions=new-empty-window;" >> VoidApp.AppDir/void.desktop && \
echo "[Desktop Action new-empty-window]" >> VoidApp.AppDir/void.desktop && \
echo "Name=New Empty Window" >> VoidApp.AppDir/void.desktop && \
echo "Name[de]=Neues leeres Fenster" >> VoidApp.AppDir/void.desktop && \
echo "Name[es]=Nueva ventana vacía" >> VoidApp.AppDir/void.desktop && \
echo "Name[fr]=Nouvelle fenêtre vide" >> VoidApp.AppDir/void.desktop && \
echo "Name[it]=Nuova finestra vuota" >> VoidApp.AppDir/void.desktop && \
echo "Name[ja]=新しい空のウィンドウ" >> VoidApp.AppDir/void.desktop && \
echo "Name[ko]=새 빈 창" >> VoidApp.AppDir/void.desktop && \
echo "Name[ru]=Новое пустое окно" >> VoidApp.AppDir/void.desktop && \
echo "Name[zh_CN]=新建空窗口" >> VoidApp.AppDir/void.desktop && \
echo "Name[zh_TW]=開新空視窗" >> VoidApp.AppDir/void.desktop && \
echo "Exec=void --new-window %F" >> VoidApp.AppDir/void.desktop && \
echo "Icon=void" >> VoidApp.AppDir/void.desktop && \
chmod +x VoidApp.AppDir/void.desktop && \
cp VoidApp.AppDir/void.desktop VoidApp.AppDir/usr/share/applications/ && \
echo "[Desktop Entry]" > VoidApp.AppDir/void-url-handler.desktop && \
echo "Name=Void - URL Handler" > VoidApp.AppDir/void-url-handler.desktop && \
echo "Comment=Open source AI code editor." > VoidApp.AppDir/void-url-handler.desktop && \
echo "GenericName=Text Editor" > VoidApp.AppDir/void-url-handler.desktop && \
echo "Exec=void --open-url %U" > VoidApp.AppDir/void-url-handler.desktop && \
echo "Icon=void" > VoidApp.AppDir/void-url-handler.desktop && \
echo "Type=Application" > VoidApp.AppDir/void-url-handler.desktop && \
echo "NoDisplay=true" > VoidApp.AppDir/void-url-handler.desktop && \
echo "StartupNotify=true" > VoidApp.AppDir/void-url-handler.desktop && \
echo "Categories=Utility;TextEditor;Development;IDE;" > VoidApp.AppDir/void-url-handler.desktop && \
echo "MimeType=x-scheme-handler/void;" > VoidApp.AppDir/void-url-handler.desktop && \
echo "Keywords=void;" > VoidApp.AppDir/void-url-handler.desktop && \
chmod +x VoidApp.AppDir/void-url-handler.desktop && \
cp VoidApp.AppDir/void-url-handler.desktop VoidApp.AppDir/usr/share/applications/ && \
echo "#!/bin/bash" > VoidApp.AppDir/AppRun && \
echo "HERE=\$(dirname \"\$(readlink -f \"\${0}\")\")" >> VoidApp.AppDir/AppRun && \
echo "export PATH=\${HERE}/usr/bin:\${PATH}" >> VoidApp.AppDir/AppRun && \
+12
View File
@@ -0,0 +1,12 @@
[Desktop Entry]
Name=Void - URL Handler
Comment=Open source AI code editor.
GenericName=Text Editor
Exec=void --open-url %U
Icon=void
Type=Application
NoDisplay=true
StartupNotify=true
Categories=Utility;TextEditor;Development;IDE;
MimeType=x-scheme-handler/void;
Keywords=void;
+24 -4
View File
@@ -1,7 +1,27 @@
[Desktop Entry]
Name=void
Exec=void
Name=Void
Comment=Open source AI code editor.
GenericName=Text Editor
Exec=void %F
Icon=void
Type=Application
Categories=Utility;
Comment=Void Linux Application
StartupNotify=false
StartupWMClass=Void
Categories=TextEditor;Development;IDE;
MimeType=application/x-void-workspace;
Keywords=void;
Actions=new-empty-window;
[Desktop Action new-empty-window]
Name=New Empty Window
Name[de]=Neues leeres Fenster
Name[es]=Nueva ventana vacía
Name[fr]=Nouvelle fenêtre vide
Name[it]=Nuova finestra vuota
Name[ja]=新しい空のウィンドウ
Name[ko]=새 빈 창
Name[ru]=Новое пустое окно
Name[zh_CN]=新建空窗口
Name[zh_TW]=開新空視窗
Exec=void --new-window %F
Icon=void
+3
View File
@@ -3,6 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// This bootstrap-fork module handles the initialization of a forked process in VS Code.
// It sets up logging, exception handling, and loads the ESM module system.
import * as performance from './vs/base/common/performance.js';
import { removeGlobalNodeJsModuleLookupPaths, devInjectNodeModuleLookupPath } from './bootstrap-node.js';
import { bootstrapESM } from './bootstrap-esm.js';
+14
View File
@@ -6,6 +6,7 @@
import * as path from 'path';
import * as fs from 'original-fs';
import * as os from 'os';
import { createRequire } from 'node:module';
import { performance } from 'perf_hooks';
import { configurePortable } from './bootstrap-node.js';
import { bootstrapESM } from './bootstrap-esm.js';
@@ -22,6 +23,7 @@ import { INLSConfiguration } from './vs/nls.js';
import { NativeParsedArgs } from './vs/platform/environment/common/argv.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
perf.mark('code/didStartMain');
@@ -110,6 +112,17 @@ protocol.registerSchemesAsPrivileged([
// Global app listeners
registerListeners();
function resolveUserProduct() {
const userProductPath = path.join(userDataPath, 'product.json');
try {
// Assign the product configuration to the global scope
const productJson = require(userProductPath);
// @ts-expect-error
globalThis._VSCODE_USER_PRODUCT_JSON = productJson;
} catch (ex) {
}
}
/**
* We can resolve the NLS configuration early if it is defined
* in argv.json before `app.ready` event. Otherwise we can only
@@ -206,6 +219,7 @@ async function onReady() {
async function startup(codeCachePath: string | undefined, nlsConfig: INLSConfiguration): Promise<void> {
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig);
process.env['VSCODE_CODE_CACHE_PATH'] = codeCachePath || '';
resolveUserProduct();
// Bootstrap ESM
await bootstrapESM();
@@ -26,7 +26,7 @@
}
/* custom speed & easing for loading icon */
.codicon-loading,
.codicon-loading:not(.codicon-no-default-spin), /* Void changed this as it is literally broken to the !important */
.codicon-tree-item-loading::before {
animation-duration: 1s !important;
animation-timing-function: cubic-bezier(0.53, 0.21, 0.29, 0.67) !important;
+2
View File
@@ -56,6 +56,8 @@ export type ExtensionVirtualWorkspaceSupport = {
export interface IProductConfiguration {
readonly version: string;
readonly voidVersion: string; // Void added this
readonly release: string; // VSCodium added this
readonly date?: string;
readonly quality?: string;
readonly commit?: string;
+3 -3
View File
@@ -125,7 +125,7 @@ import { IMetricsService } from '../../workbench/contrib/void/common/metricsServ
import { IVoidUpdateService } from '../../workbench/contrib/void/common/voidUpdateService.js';
import { MetricsMainService } from '../../workbench/contrib/void/electron-main/metricsMainService.js';
import { VoidMainUpdateService } from '../../workbench/contrib/void/electron-main/voidUpdateMainService.js';
import { LLMMessageChannel } from '../../workbench/contrib/void/electron-main/llmMessageChannel.js';
import { LLMMessageChannel } from '../../workbench/contrib/void/electron-main/sendLLMMessageChannel.js';
/**
* The main VS Code application. There will only ever be one instance,
@@ -1256,8 +1256,8 @@ export class CodeApplication extends Disposable {
const voidUpdatesChannel = ProxyChannel.fromService(accessor.get(IVoidUpdateService), disposables);
mainProcessElectronServer.registerChannel('void-channel-update', voidUpdatesChannel);
const llmMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService));
mainProcessElectronServer.registerChannel('void-channel-llmMessageService', llmMessageChannel);
const sendLLMMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService));
mainProcessElectronServer.registerChannel('void-channel-llmMessage', sendLLMMessageChannel);
// Extension Host Debug Broadcasting
const electronExtensionHostDebugBroadcastChannel = new ElectronExtensionHostDebugBroadcastChannel(accessor.get(IWindowsMainService));
+1 -1
View File
@@ -3570,7 +3570,7 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
const defaults: InternalQuickSuggestionsOptions = {
other: 'on',
comments: 'off',
strings: 'off'
strings: 'on' // Void changed this setting
};
const types: IJSONSchema[] = [
{ type: 'boolean' },
@@ -121,6 +121,93 @@ export class SmartSelectController implements IEditorContribution {
}
this._state = this._state.map(state => state.mov(forward));
const newSelections = this._state.map(state => Selection.fromPositions(state.ranges[state.index].getStartPosition(), state.ranges[state.index].getEndPosition()));
// Void changed this to skip over added whitespace when using smartSelect
// // Store the original selections for comparison
// const originalSelections = selections;
// // Keep skipping while we're only adding/removing whitespace
// let keepSkipping = true;
// let skipCount = 0;
// const MAX_SKIPS = 5; // Avoid infinite loops by setting a reasonable limit
// while (keepSkipping && skipCount < MAX_SKIPS) {
// keepSkipping = false; // Reset for each iteration
// // Check if all selections only added/removed whitespace
// if (originalSelections.length === newSelections.length) {
// for (let i = 0; i < originalSelections.length; i++) {
// const oldSel = originalSelections[i];
// const newSel = newSelections[i];
// if (forward) { // For expanding (^+Shift+Right)
// // Skip if only whitespace was added
// const oldText = model.getValueInRange(oldSel).trim();
// const newText = model.getValueInRange(newSel).trim();
// const onlyWhitespaceAdded = oldText === newText && oldText.length > 0;
// if (onlyWhitespaceAdded) {
// console.log(`SMART SELECT - SKIPPING (EXPAND) [${skipCount + 1}]:`, {
// reason: 'only whitespace added',
// oldText: model.getValueInRange(oldSel),
// newText: model.getValueInRange(newSel)
// });
// keepSkipping = true;
// break;
// }
// } else { // For shrinking (^+Shift+Left)
// // Skip if only whitespace was removed
// const oldText = model.getValueInRange(oldSel).trim();
// const newText = model.getValueInRange(newSel).trim();
// const onlyWhitespaceRemoved = oldText === newText && newText.length > 0;
// if (onlyWhitespaceRemoved) {
// console.log(`SMART SELECT - SKIPPING (SHRINK) [${skipCount + 1}]:`, {
// reason: 'only whitespace removed',
// oldText: model.getValueInRange(oldSel),
// newText: model.getValueInRange(newSel)
// });
// keepSkipping = true;
// break;
// }
// }
// }
// }
// // If we need to skip, move one more time
// if (keepSkipping) {
// skipCount++;
// // Try to move to the next range
// const prevState = this._state;
// this._state = this._state.map(state => state.mov(forward));
// // Check if we've reached the end of available ranges
// const stateUnchanged = this._state.every((state, idx) =>
// state.index === prevState[idx].index
// );
// if (stateUnchanged) {
// // We can't move any further, so stop skipping
// keepSkipping = false;
// } else {
// // Update selections for the next iteration
// newSelections = this._state.map(state => Selection.fromPositions(
// state.ranges[state.index].getStartPosition(),
// state.ranges[state.index].getEndPosition()
// ));
// }
// }
// }
// // Print AFTER selection (before actually setting it)
// console.log('SMART SELECT - AFTER:', newSelections.map(s => {
// return {
// range: `(${s.startLineNumber},${s.startColumn}) -> (${s.endLineNumber},${s.endColumn})`,
// text: model.getValueInRange(s)
// };
// }));
this._ignoreSelection = true;
try {
this._editor.setSelections(newSelections);
@@ -230,7 +230,7 @@ export class DiagnosticsService implements IDiagnosticsService {
private formatEnvironment(info: IMainProcessDiagnostics): string {
const output: string[] = [];
output.push(`Version: ${this.productService.nameShort} ${this.productService.version} (${this.productService.commit || 'Commit unknown'}, ${this.productService.date || 'Date unknown'})`);
output.push(`Version: ${this.productService.nameShort} ${this.productService.version} ${this.productService.release || 'Release unknown'} (${this.productService.commit || 'Commit unknown'}, ${this.productService.date || 'Date unknown'})`);
output.push(`OS Version: ${osLib.type()} ${osLib.arch()} ${osLib.release()}`);
const cpus = osLib.cpus();
if (cpus && cpus.length > 0) {
@@ -52,7 +52,6 @@ import { IProductService } from '../../product/common/productService.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js';
import { IUserDataProfilesService } from '../../userDataProfile/common/userDataProfile.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
import { isLinux } from '../../../base/common/platform.js';
export const INativeServerExtensionManagementService = refineServiceDecorator<IExtensionManagementService, INativeServerExtensionManagementService>(IExtensionManagementService);
@@ -85,7 +84,6 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
@IDownloadService private downloadService: IDownloadService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IFileService private readonly fileService: IFileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IProductService productService: IProductService,
@IAllowedExtensionsService allowedExtensionsService: IAllowedExtensionsService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
@@ -324,8 +322,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
private async downloadExtension(extension: IGalleryExtension, operation: InstallOperation, verifySignature: boolean, clientTargetPlatform?: TargetPlatform): Promise<{ readonly location: URI; readonly verificationStatus: ExtensionSignatureVerificationCode | undefined }> {
if (verifySignature) {
const value = this.configurationService.getValue('extensions.verifySignature');
verifySignature = isBoolean(value) ? value : true;
verifySignature = false; // Void disable-signature-verification-patch
}
const { location, verificationStatus } = await this.extensionsDownloader.download(extension, operation, verifySignature, clientTargetPlatform);
@@ -404,7 +404,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
}
private async getShellCommandLink(): Promise<{ readonly source: string; readonly target: string }> {
const target = resolve(this.environmentMainService.appRoot, 'bin', 'code');
const target = resolve(this.environmentMainService.appRoot, 'bin', this.productService.applicationName);
const source = `/usr/local/bin/${this.productService.applicationName}`;
// Ensure source exists
@@ -639,7 +639,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
// macOS
if (this.environmentMainService.isBuilt) {
return join(this.environmentMainService.appRoot, 'bin', 'code');
return join(this.environmentMainService.appRoot, 'bin', `${this.productService.applicationName}`);
}
return join(this.environmentMainService.appRoot, 'scripts', 'code-cli.sh');
+3 -2
View File
@@ -43,10 +43,11 @@ else if (globalThis._VSCODE_PRODUCT_JSON && globalThis._VSCODE_PACKAGE_JSON) {
// want to have it running out of sources so we
// read it from package.json only when we need it.
if (!product.version) {
const pkg = globalThis._VSCODE_PACKAGE_JSON as { version: string };
const pkg = globalThis._VSCODE_PACKAGE_JSON as { version: string, release: string };
Object.assign(product, {
version: pkg.version
version: pkg.version,
release: pkg.release
});
}
}
+40 -1
View File
@@ -49,7 +49,8 @@ export const enum StateType {
export const enum UpdateType {
Setup,
Archive,
Snap
Snap,
WindowsInstaller
}
export const enum DisablementReason {
@@ -108,3 +109,41 @@ export interface IUpdateService {
isLatestVersion(): Promise<boolean | undefined>;
_applySpecificUpdate(packagePath: string): Promise<void>;
}
export type Architecture =
| 'arm'
| 'arm64'
| 'ia32'
| 'loong64'
| 'mips'
| 'mipsel'
| 'ppc'
| 'ppc64'
| 'riscv64'
| 's390'
| 's390x'
| 'x64';
export type Platform =
| 'aix'
| 'android'
| 'darwin'
| 'freebsd'
| 'haiku'
| 'linux'
| 'openbsd'
| 'sunos'
| 'win32'
| 'cygwin'
| 'netbsd';
export type Quality =
| 'insider'
| 'stable';
export type Target =
| 'archive'
| 'msi'
| 'system'
| 'user';
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
// import { timeout } from '../../../base/common/async.js';
import { timeout } from '../../../base/common/async.js';
import { CancellationToken } from '../../../base/common/cancellation.js';
import { Emitter, Event } from '../../../base/common/event.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
@@ -13,13 +14,16 @@ import { ILifecycleMainService, LifecycleMainPhase } from '../../lifecycle/elect
import { ILogService } from '../../log/common/log.js';
import { IProductService } from '../../product/common/productService.js';
import { IRequestService } from '../../request/common/request.js';
import { AvailableForDownload, DisablementReason, IUpdateService, State, StateType, UpdateType } from '../common/update.js';
import { Architecture, AvailableForDownload, DisablementReason, IUpdateService, Platform, State, StateType, Target, UpdateType } from '../common/update.js';
export function createUpdateURL(platform: string, quality: string, productService: IProductService): string {
// return `https://voideditor.dev/api/update/${platform}/stable`;
// return `${productService.updateUrl}/api/update/${platform}/${quality}/${productService.commit}`;
// https://github.com/VSCodium/update-api
return `https://updates.voideditor.dev/api/update/${platform}/${quality}/${productService.commit}`;
// Void - VSCodium's version-1-update.patch
export function createUpdateURL(productService: IProductService, quality: string, platform: Platform, architecture: Architecture, target?: Target): string { // return `https://voideditor.dev/api/update/${platform}/stable`;
if (target) {
return `${productService.updateUrl}/${quality}/${platform}/${architecture}/${target}/latest.json`;
} else { // we shouldn't usually have a target:
// https://raw.githubusercontent.com/voideditor/versions/refs/heads/main/stable/darwin/arm64/latest.json
return `${productService.updateUrl}/${quality}/${platform}/${architecture}/latest.json`;
}
}
export type UpdateErrorClassification = {
@@ -81,22 +85,20 @@ export abstract class AbstractUpdateService implements IUpdateService {
return;
}
this.setState(State.Disabled(DisablementReason.ManuallyDisabled));
// Void - re-enabled auto updates
// this.setState(State.Disabled(DisablementReason.ManuallyDisabled));
// Void - temporarily disabled while we figure out how to do this the right way
// this.setState(State.Idle(this.getUpdateType()));
this.setState(State.Idle(this.getUpdateType()));
// start checking for updates after 10 seconds
// this.scheduleCheckForUpdates(10 * 1000).then(undefined, err => this.logService.error(err));
this.scheduleCheckForUpdates(10 * 1000).then(undefined, err => this.logService.error(err));
}
// private async scheduleCheckForUpdates(delay = 60 * 60 * 1000): Promise<void> {
// await timeout(delay);
// await this.checkForUpdates(false);
// return await this.scheduleCheckForUpdates(60 * 60 * 1000);
// }
private async scheduleCheckForUpdates(delay = 60 * 60 * 1000): Promise<void> {
await timeout(delay);
await this.checkForUpdates(false);
return await this.scheduleCheckForUpdates(60 * 60 * 1000);
}
async checkForUpdates(explicit: boolean): Promise<void> {
this.logService.trace('update#checkForUpdates, state = ', this.state.type);
@@ -13,11 +13,12 @@ import { IEnvironmentMainService } from '../../environment/electron-main/environ
import { ILifecycleMainService, IRelaunchHandler, IRelaunchOptions } from '../../lifecycle/electron-main/lifecycleMainService.js';
import { ILogService } from '../../log/common/log.js';
import { IProductService } from '../../product/common/productService.js';
import { IRequestService } from '../../request/common/request.js';
import { IRequestService, asJson } from '../../request/common/request.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
import { IUpdate, State, StateType, UpdateType } from '../common/update.js';
import { AbstractUpdateService, createUpdateURL, UpdateErrorClassification } from './abstractUpdateService.js';
import { CancellationToken } from '../../../base/common/cancellation.js';
import * as semver from 'semver';
export class DarwinUpdateService extends AbstractUpdateService implements IRelaunchHandler {
private readonly disposables = new DisposableStore();
@@ -75,13 +76,7 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau
// Void: buildUpdateFeedUrl -> doBuildUpdateFeedUrl
protected doBuildUpdateFeedUrl(quality: string): string | undefined {
let assetID: string;
if (!this.productService.darwinUniversalAssetId) {
assetID = process.arch === 'x64' ? 'darwin' : 'darwin-arm64';
} else {
assetID = this.productService.darwinUniversalAssetId;
}
const url = createUpdateURL(assetID, quality, this.productService);
const url = createUpdateURL(this.productService, quality, process.platform, process.arch);
try {
electron.autoUpdater.setFeedURL({ url });
} catch (e) {
@@ -93,8 +88,36 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau
}
protected doCheckForUpdates(context: any): void {
if (!this.url) {
return;
}
this.setState(State.CheckingForUpdates(context));
electron.autoUpdater.checkForUpdates();
// electron.autoUpdater.checkForUpdates();
this.requestService.request({ url: this.url }, CancellationToken.None)
.then<IUpdate | null>(asJson)
.then(update => {
if (!update || !update.url || !update.version || !update.productVersion) {
this.setState(State.Idle(UpdateType.Setup));
return Promise.resolve(null);
}
const fetchedVersion = update.productVersion.replace(/(\d+\.\d+\.\d+)(?:\.(\d+))(\-\w+)?/, '$1$3+$2');
const currentVersion = `${this.productService.voidVersion}+${this.productService.release}`;
// Void compares voidVersion, not VSCode version
// const currentVersion = `${this.productService.version}+${this.productService.release}`;
if (semver.compareBuild(currentVersion, fetchedVersion) >= 0) {
this.setState(State.Idle(UpdateType.Setup));
}
else {
electron.autoUpdater.checkForUpdates();
}
return Promise.resolve(null);
})
}
private onUpdateAvailable(): void {
@@ -30,7 +30,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
// Void: buildUpdateFeedUrl -> doBuildUpdateFeedUrl
protected doBuildUpdateFeedUrl(quality: string): string {
return createUpdateURL(`linux-${process.arch}`, quality, this.productService);
return createUpdateURL(this.productService, quality, process.platform, process.arch);
}
protected doCheckForUpdates(context: any): void {
@@ -9,7 +9,6 @@ import { tmpdir } from 'os';
import { timeout } from '../../../base/common/async.js';
import { CancellationToken } from '../../../base/common/cancellation.js';
import { memoize } from '../../../base/common/decorators.js';
import { hash } from '../../../base/common/hash.js';
import * as path from '../../../base/common/path.js';
import { URI } from '../../../base/common/uri.js';
import { checksum } from '../../../base/node/crypto.js';
@@ -23,8 +22,9 @@ import { INativeHostMainService } from '../../native/electron-main/nativeHostMai
import { IProductService } from '../../product/common/productService.js';
import { asJson, IRequestService } from '../../request/common/request.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, UpdateType } from '../common/update.js';
import { AbstractUpdateService, createUpdateURL, UpdateErrorClassification } from './abstractUpdateService.js';
import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, Target, UpdateType } from '../common/update.js';
import { AbstractUpdateService, createUpdateURL } from './abstractUpdateService.js';
import * as semver from 'semver';
async function pollUntil(fn: () => boolean, millis = 1000): Promise<void> {
while (!fn()) {
@@ -40,9 +40,13 @@ interface IAvailableUpdate {
let _updateType: UpdateType | undefined = undefined;
function getUpdateType(): UpdateType {
if (typeof _updateType === 'undefined') {
_updateType = fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))
? UpdateType.Setup
: UpdateType.Archive;
if (fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))) {
_updateType = UpdateType.Setup;
} else if (path.basename(path.normalize(path.join(process.execPath, '..', '..'))) === 'Program Files') {
_updateType = UpdateType.WindowsInstaller;
} else {
_updateType = UpdateType.Archive;
}
}
return _updateType;
@@ -61,6 +65,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
constructor(
@ILifecycleMainService lifecycleMainService: ILifecycleMainService,
@IConfigurationService configurationService: IConfigurationService,
// @ts-expect-error
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentMainService environmentMainService: IEnvironmentMainService,
@IRequestService requestService: IRequestService,
@@ -101,15 +106,24 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
// Void: buildUpdateFeedUrl -> doBuildUpdateFeedUrl
protected doBuildUpdateFeedUrl(quality: string): string | undefined {
let platform = `win32-${process.arch}`;
if (getUpdateType() === UpdateType.Archive) {
platform += '-archive';
} else if (this.productService.target === 'user') {
platform += '-user';
let target: Target;
switch (getUpdateType()) {
case UpdateType.Archive:
target = 'archive'
break;
case UpdateType.WindowsInstaller:
target = 'msi'
break;
default:
if (this.productService.target === 'user') {
target = 'user'
}
else {
target = 'system'
}
}
return createUpdateURL(platform, quality, this.productService);
return createUpdateURL(this.productService, quality, process.platform, process.arch, target);
}
protected doCheckForUpdates(context: any): void {
@@ -129,6 +143,16 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
return Promise.resolve(null);
}
const fetchedVersion = update.productVersion.replace(/(\d+\.\d+\.\d+)(?:\.(\d+))(\-\w+)?/, '$1$3+$2');
const currentVersion = `${this.productService.voidVersion}+${this.productService.release}`;
// Void compares voidVersion, not VSCode version
// const currentVersion = `${this.productService.version}+${this.productService.release}`;
if (semver.compareBuild(currentVersion, fetchedVersion) >= 0) {
this.setState(State.Idle(updateType));
return Promise.resolve(null);
}
if (updateType === UpdateType.Archive) {
this.setState(State.AvailableForDownload(update));
return Promise.resolve(null);
@@ -155,7 +179,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
this.availableUpdate = { packagePath };
this.setState(State.Downloaded(update));
const fastUpdatesEnabled = this.configurationService.getValue('update.enableWindowsBackgroundUpdates');
const fastUpdatesEnabled = getUpdateType() === UpdateType.Setup && this.configurationService.getValue('update.enableWindowsBackgroundUpdates');
if (fastUpdatesEnabled) {
if (this.productService.target === 'user') {
this.doApplyUpdate();
@@ -167,7 +191,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
});
})
.then(undefined, err => {
this.telemetryService.publicLog2<{ messageHash: string }, UpdateErrorClassification>('update:error', { messageHash: String(hash(String(err))) });
this.logService.error(err);
// only show message when explicitly checking for updates
@@ -251,10 +274,18 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun
if (this.availableUpdate.updateFilePath) {
fs.unlinkSync(this.availableUpdate.updateFilePath);
} else {
spawn(this.availableUpdate.packagePath, ['/silent', '/log', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore']
});
const type = getUpdateType();
if (type === UpdateType.WindowsInstaller) {
spawn('msiexec.exe', ['/i', this.availableUpdate.packagePath], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore']
});
} else {
spawn(this.availableUpdate.packagePath, ['/silent', '/log', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore']
});
}
}
}
@@ -52,7 +52,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart {
static readonly viewContainersWorkspaceStateKey = 'workbench.auxiliarybar.viewContainersWorkspaceState';
// Use the side bar dimensions
override readonly minimumWidth: number = 230; // Void changed this (was 170)
override readonly minimumWidth: number = 280; // Void changed this (was 170)
override readonly maximumWidth: number = Number.POSITIVE_INFINITY;
override readonly minimumHeight: number = 0;
override readonly maximumHeight: number = Number.POSITIVE_INFINITY;
@@ -21,6 +21,7 @@ import { MarkdownRenderer, openLinkFromMarkdown } from '../../../../editor/brows
import { defaultButtonStyles, defaultCheckboxStyles, defaultDialogStyles, defaultInputBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js';
import { ResultKind } from '../../../../platform/keybinding/common/keybindingResolver.js';
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
import { getReleaseString } from '../../../../workbench/common/release.js';
export class BrowserDialogHandler extends AbstractDialogHandler {
@@ -79,13 +80,14 @@ export class BrowserDialogHandler extends AbstractDialogHandler {
async about(): Promise<void> {
const detailString = (useAgo: boolean): string => {
const releaseString = getReleaseString();
return localize('aboutDetail',
"Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
this.productService.version || 'Unknown',
this.productService.commit || 'Unknown',
this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown',
navigator.userAgent
);
).replace('\n', `\n${releaseString} ${this.productService.release || 'Unknown'}\n`);
};
const detail = detailString(true);
@@ -250,7 +250,9 @@ export class EditorGroupWatermark extends Disposable {
linkSpan.appendChild(dirSpan);
return linkSpan
}).filter(v => !!v)
})
.filter(v => !!v)
.slice(0, 5) // take 5 most recent
)
}
@@ -612,7 +612,7 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
'description': localize('workbench.hover.delay', "Controls the delay in milliseconds after which the hover is shown for workbench items (ex. some extension provided tree view items). Already visible items may require a refresh before reflecting this setting change."),
// Testing has indicated that on Windows and Linux 500 ms matches the native hovers most closely.
// On Mac, the delay is 1500.
'default': isMacintosh ? 1500 : 500,
'default': 300, // Void changed this from isMacintosh ? 1500 : 500,
'minimum': 0
},
'workbench.reduceMotion': {
+15
View File
@@ -0,0 +1,15 @@
// added by VSCodium
import { language } from '../../../vs/base/common/platform.js';
const DEFAULT_LABEL = 'Release:';
const LABELS: { [key: string]: string } = {
'en': DEFAULT_LABEL,
'fr': 'Révision :',
'ru': 'Релиз:',
'zh-hans': '发布版本:',
'zh-hant': '發布版本:',
};
export function getReleaseString(): string {
return LABELS[language] ?? DEFAULT_LABEL;
}
@@ -648,15 +648,18 @@ const defaultChat = {
providerSetting: product.defaultChatAgent?.providerSetting ?? '',
};
// Void commented this out - copilot head
// Add next to the command center if command center is disabled
MenuRegistry.appendMenuItem(MenuId.CommandCenter, {
/* MenuRegistry.appendMenuItem(MenuId.CommandCenter, {
submenu: MenuId.ChatTitleBarMenu,
title: localize('title4', "Copilot"),
icon: Codicon.copilot,
when: ContextKeyExpr.and(
ChatContextKeys.supported,
ContextKeyExpr.has('config.chat.commandCenter.enabled')
),
// Void commented this out - copilot head
when: ContextKeyExpr.false(),
// when: ContextKeyExpr.and(
// ChatContextKeys.supported,
// ContextKeyExpr.has('config.chat.commandCenter.enabled')
// ),
order: 10001 // to the right of command center
});
@@ -666,13 +669,15 @@ MenuRegistry.appendMenuItem(MenuId.TitleBar, {
title: localize('title4', "Copilot"),
group: 'navigation',
icon: Codicon.copilot,
when: ContextKeyExpr.and(
ChatContextKeys.supported,
ContextKeyExpr.has('config.chat.commandCenter.enabled'),
ContextKeyExpr.has('config.window.commandCenter').negate(),
),
when: ContextKeyExpr.false(),
// Void commented this out - copilot head
// when: ContextKeyExpr.and(
// ChatContextKeys.supported,
// ContextKeyExpr.has('config.chat.commandCenter.enabled'),
// ContextKeyExpr.has('config.window.commandCenter').negate(),
// ),
order: 1
});
}); */
registerAction2(class ToggleCopilotControl extends ToggleTitleBarConfigAction {
constructor() {
@@ -4,23 +4,20 @@
*--------------------------------------------------------------------------------------------*/
import { coalesce, isNonEmptyArray } from '../../../../base/common/arrays.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { toErrorMessage } from '../../../../base/common/errorMessage.js';
import { Event } from '../../../../base/common/event.js';
import { MarkdownString } from '../../../../base/common/htmlContent.js';
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
import { Disposable, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js';
import * as strings from '../../../../base/common/strings.js';
import { localize, localize2 } from '../../../../nls.js';
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import { localize } from '../../../../nls.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import { ExtensionIdentifier, IExtensionManifest } from '../../../../platform/extensions/common/extensions.js';
import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
import { ILogService } from '../../../../platform/log/common/log.js';
import { IProductService } from '../../../../platform/product/common/productService.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js';
import { IWorkbenchContribution } from '../../../common/contributions.js';
import { IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer, ViewContainerLocation, Extensions as ViewExtensions } from '../../../common/views.js';
import { IViewsRegistry, Extensions as ViewExtensions } from '../../../common/views.js';
import { IExtensionFeatureTableRenderer, IRenderedData, ITableData, IRowData, IExtensionFeaturesRegistry, Extensions } from '../../../services/extensionManagement/common/extensionFeatures.js';
import { isProposedApiEnabled } from '../../../services/extensions/common/extensions.js';
import * as extensionsRegistry from '../../../services/extensions/common/extensionsRegistry.js';
@@ -30,90 +27,91 @@ import { ChatAgentLocation, IChatAgentData, IChatAgentService } from '../common/
import { ChatContextKeys } from '../common/chatContextKeys.js';
import { IRawChatParticipantContribution } from '../common/chatParticipantContribTypes.js';
import { ChatViewId } from './chat.js';
import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID, ChatViewPane } from './chatViewPane.js';
// --- Chat Container & View Registration
const chatViewContainer: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
id: CHAT_SIDEBAR_PANEL_ID,
title: localize2('chat.viewContainer.label', "Chat"),
icon: Codicon.commentDiscussion,
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [CHAT_SIDEBAR_PANEL_ID, { mergeViewWithContainerWhenSingleView: true }]),
storageId: CHAT_SIDEBAR_PANEL_ID,
hideIfEmpty: true,
order: 100,
}, ViewContainerLocation.AuxiliaryBar, { isDefault: true, doNotRegisterOpenCommand: true });
// Void commented this out
// const chatViewContainer: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
// id: CHAT_SIDEBAR_PANEL_ID,
// title: localize2('chat.viewContainer.label', "Chat"),
// icon: Codicon.commentDiscussion,
// ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [CHAT_SIDEBAR_PANEL_ID, { mergeViewWithContainerWhenSingleView: true }]),
// storageId: CHAT_SIDEBAR_PANEL_ID,
// hideIfEmpty: true,
// order: 100,
// }, ViewContainerLocation.AuxiliaryBar, { isDefault: true, doNotRegisterOpenCommand: true });
const chatViewDescriptor: IViewDescriptor[] = [{
id: ChatViewId,
containerIcon: chatViewContainer.icon,
containerTitle: chatViewContainer.title.value,
singleViewPaneContainerTitle: chatViewContainer.title.value,
name: localize2('chat.viewContainer.label', "Chat"),
canToggleVisibility: false,
canMoveView: true,
openCommandActionDescriptor: {
id: CHAT_SIDEBAR_PANEL_ID,
title: chatViewContainer.title,
mnemonicTitle: localize({ key: 'miToggleChat', comment: ['&& denotes a mnemonic'] }, "&&Chat"),
keybindings: {
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyI,
mac: {
primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyI
}
},
order: 1
},
ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.Panel }]),
when: ContextKeyExpr.or(
ChatContextKeys.Setup.hidden.negate(),
ChatContextKeys.Setup.installed,
ChatContextKeys.panelParticipantRegistered,
ChatContextKeys.extensionInvalid
)
}];
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(chatViewDescriptor, chatViewContainer);
// const chatViewDescriptor: IViewDescriptor[] = [{
// id: ChatViewId,
// containerIcon: chatViewContainer.icon,
// containerTitle: chatViewContainer.title.value,
// singleViewPaneContainerTitle: chatViewContainer.title.value,
// name: localize2('chat.viewContainer.label', "Chat"),
// canToggleVisibility: false,
// canMoveView: true,
// openCommandActionDescriptor: {
// id: CHAT_SIDEBAR_PANEL_ID,
// title: chatViewContainer.title,
// mnemonicTitle: localize({ key: 'miToggleChat', comment: ['&& denotes a mnemonic'] }, "&&Chat"),
// keybindings: {
// primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyI,
// mac: {
// primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyI
// }
// },
// order: 1
// },
// ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.Panel }]),
// when: ContextKeyExpr.or(
// ChatContextKeys.Setup.hidden.negate(),
// ChatContextKeys.Setup.installed,
// ChatContextKeys.panelParticipantRegistered,
// ChatContextKeys.extensionInvalid
// )
// }];
// Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(chatViewDescriptor, chatViewContainer);
// --- Edits Container & View Registration
const editsViewContainer: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
id: CHAT_EDITING_SIDEBAR_PANEL_ID,
title: localize2('chatEditing.viewContainer.label', "Copilot Edits"),
icon: Codicon.editSession,
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [CHAT_EDITING_SIDEBAR_PANEL_ID, { mergeViewWithContainerWhenSingleView: true }]),
storageId: CHAT_EDITING_SIDEBAR_PANEL_ID,
hideIfEmpty: true,
order: 101,
}, ViewContainerLocation.AuxiliaryBar, { doNotRegisterOpenCommand: true });
// Void commented this out
// const editsViewContainer: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
// id: CHAT_EDITING_SIDEBAR_PANEL_ID,
// title: localize2('chatEditing.viewContainer.label', "Copilot Edits"),
// icon: Codicon.editSession,
// ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [CHAT_EDITING_SIDEBAR_PANEL_ID, { mergeViewWithContainerWhenSingleView: true }]),
// storageId: CHAT_EDITING_SIDEBAR_PANEL_ID,
// hideIfEmpty: true,
// order: 101,
// }, ViewContainerLocation.AuxiliaryBar, { doNotRegisterOpenCommand: true });
const editsViewDescriptor: IViewDescriptor[] = [{
id: 'workbench.panel.chat.view.edits',
containerIcon: editsViewContainer.icon,
containerTitle: editsViewContainer.title.value,
singleViewPaneContainerTitle: editsViewContainer.title.value,
name: editsViewContainer.title,
canToggleVisibility: false,
canMoveView: true,
openCommandActionDescriptor: {
id: CHAT_EDITING_SIDEBAR_PANEL_ID,
title: editsViewContainer.title,
mnemonicTitle: localize({ key: 'miToggleEdits', comment: ['&& denotes a mnemonic'] }, "Copilot Ed&&its"),
keybindings: {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI,
linux: {
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.KeyI
}
},
order: 2
},
ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.EditingSession }]),
when: ContextKeyExpr.or(
ChatContextKeys.Setup.hidden.negate(),
ChatContextKeys.Setup.installed,
ChatContextKeys.editingParticipantRegistered
)
}];
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(editsViewDescriptor, editsViewContainer);
// const editsViewDescriptor: IViewDescriptor[] = [{
// id: 'workbench.panel.chat.view.edits',
// containerIcon: editsViewContainer.icon,
// containerTitle: editsViewContainer.title.value,
// singleViewPaneContainerTitle: editsViewContainer.title.value,
// name: editsViewContainer.title,
// canToggleVisibility: false,
// canMoveView: true,
// openCommandActionDescriptor: {
// id: CHAT_EDITING_SIDEBAR_PANEL_ID,
// title: editsViewContainer.title,
// mnemonicTitle: localize({ key: 'miToggleEdits', comment: ['&& denotes a mnemonic'] }, "Copilot Ed&&its"),
// keybindings: {
// primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI,
// linux: {
// primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.KeyI
// }
// },
// order: 2
// },
// ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.EditingSession }]),
// when: ContextKeyExpr.or(
// ChatContextKeys.Setup.hidden.negate(),
// ChatContextKeys.Setup.installed,
// ChatContextKeys.editingParticipantRegistered
// )
// }];
// Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(editsViewDescriptor, editsViewContainer);
const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawChatParticipantContribution[]>({
extensionPoint: 'chatParticipants',
@@ -5,7 +5,7 @@
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
import { localize } from '../../../../nls.js';
import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';
import { IFileService } from '../../../../platform/files/common/files.js';
@@ -15,7 +15,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo
import { createSyncHeaders, IAuthenticationProvider, IResourceRefHandle } from '../../../../platform/userDataSync/common/userDataSync.js';
import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from '../../../services/authentication/common/authentication.js';
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, IEditSessionsStorageService, EDIT_SESSIONS_SIGNED_IN_KEY, IEditSessionsLogService, SyncResource, EDIT_SESSIONS_PENDING_KEY } from '../common/editSessions.js';
import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, IEditSessionsStorageService, EDIT_SESSIONS_SIGNED_IN_KEY, IEditSessionsLogService, SyncResource } from '../common/editSessions.js';
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
import { generateUuid } from '../../../../base/common/uuid.js';
import { getCurrentAuthenticationSessionInfo } from '../../../services/authentication/browser/authenticationService.js';
@@ -91,7 +91,6 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
// If another window changes the preferred session storage, reset our cached auth state in memory
this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION, EditSessionsWorkbenchService.CACHED_SESSION_STORAGE_KEY, this._store)(() => this.onDidChangeStorage()));
this.registerSignInAction();
this.registerResetAuthenticationAction();
this.signedInContext = EDIT_SESSIONS_SIGNED_IN.bindTo(this.contextKeyService);
@@ -454,46 +453,6 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
}
}
private registerSignInAction() {
if (!this.serverConfiguration?.url) {
return;
}
const that = this;
const id = 'workbench.editSessions.actions.signIn';
const when = ContextKeyExpr.and(ContextKeyExpr.equals(EDIT_SESSIONS_PENDING_KEY, false), ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false));
this._register(registerAction2(class ResetEditSessionAuthenticationAction extends Action2 {
constructor() {
super({
id,
title: localize('sign in', 'Turn on Cloud Changes...'),
category: EDIT_SESSION_SYNC_CATEGORY,
precondition: when,
menu: [{
id: MenuId.CommandPalette,
},
{
id: MenuId.AccountsContext,
group: '2_editSessions',
when,
}]
});
}
async run() {
return await that.initialize('write', false);
}
}));
this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
group: '2_editSessions',
command: {
id,
title: localize('sign in badge', 'Turn on Cloud Changes... (1)'),
},
when: ContextKeyExpr.and(ContextKeyExpr.equals(EDIT_SESSIONS_PENDING_KEY, true), ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false))
}));
}
private registerResetAuthenticationAction() {
const that = this;
this._register(registerAction2(class ResetEditSessionAuthenticationAction extends Action2 {
@@ -27,6 +27,7 @@ import { ThemeIcon } from '../../../../base/common/themables.js';
import { IExplorerService } from './files.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';
import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../void/browser/voidSettingsPane.js';
// Contribute Global Actions
@@ -675,6 +676,18 @@ for (const menuId of [MenuId.EmptyEditorGroupContext, MenuId.EditorTabsBarContex
// File menu
// Void added this:
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '0_void',
command: {
id: VOID_OPEN_SETTINGS_ACTION_ID,
title: nls.localize({ key: 'openVoid', comment: ['&& denotes a mnemonic'] }, "&&Open Void Settings"),
},
order: 1
});
MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, {
group: '1_new',
command: {
@@ -0,0 +1,64 @@
/*--------------------------------------------------------------------------------------
* 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 { Disposable } from '../../../../base/common/lifecycle.js';
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
import { localize2 } from '../../../../nls.js';
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
// to change this, just Cmd+Shift+F and replace DummyService with YourServiceName, and create a unique ID below
export interface IDummyService {
readonly _serviceBrand: undefined; // services need this, just leave it undefined
}
export const IDummyService = createDecorator<IDummyService>('DummyService');
// An example of an action (delete if you're not using an action):
registerAction2(class extends Action2 {
constructor() {
super({
f1: true,
id: 'void.dummy',
title: localize2('dummy', 'dummy: Init'),
keybinding: {
primary: KeyMod.CtrlCmd | KeyCode.Digit0,
weight: KeybindingWeight.VoidExtension,
}
});
}
async run(accessor: ServicesAccessor): Promise<void> {
console.log('hi')
const n = accessor.get(IDummyService)
console.log('Hi', n._serviceBrand)
}
})
class DummyService extends Disposable implements IWorkbenchContribution, IDummyService {
static readonly ID = 'workbench.contrib.void.dummy' // workbenchContributions need this, services do not
_serviceBrand: undefined;
constructor(
@ICodeEditorService codeEditorService: ICodeEditorService,
) {
super()
}
}
// pick one and delete the other:
registerSingleton(IDummyService, DummyService, InstantiationType.Eager);
registerWorkbenchContribution2(DummyService.ID, DummyService, WorkbenchPhase.BlockRestore);
@@ -39,7 +39,7 @@ class MarkerCheckService extends Disposable implements IMarkerCheckService {
console.log(`----------------------------------------------`);
console.log(`${error.resource.toString()}: ${error.startLineNumber} ${error.message} ${error.severity}`); // ! all errors in the file
console.log(`${error.resource.fsPath}: ${error.startLineNumber} ${error.message} ${error.severity}`); // ! all errors in the file
try {
// Get the text model for the file
@@ -122,11 +122,11 @@ class MarkerCheckService extends Disposable implements IMarkerCheckService {
// const markers = this._markerService.read({ resource });
// if (markers.length === 0) {
// console.log(`${resource.toString()}: No diagnostics`);
// console.log(`${resource.fsPath}: No diagnostics`);
// continue;
// }
// console.log(`Diagnostics for ${resource.toString()}:`);
// console.log(`Diagnostics for ${resource.fsPath}:`);
// markers.forEach(marker => this._logMarker(marker));
// }
// };
@@ -34,7 +34,7 @@
// // const result = await new Promise((res, rej) => {
// // sendLLMMessage({
// // messages,
// // tools: ['search'],
// // tools: ['search_for_files'],
// // onFinalMessage: ({ result: r, }) => {
// // res(r)
// // },
@@ -73,7 +73,7 @@
// // const result = new Promise((res, rej) => {
// // sendLLMMessage({
// // messages,
// // tools: ['search'],
// // tools: ['search_for_files'],
// // onResult: (r) => {
// // res(r)
// // }
@@ -8,19 +8,26 @@ import { ILanguageFeaturesService } from '../../../../editor/common/services/lan
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { ITextModel } from '../../../../editor/common/model.js';
import { Position } from '../../../../editor/common/core/position.js';
import { InlineCompletion, InlineCompletionContext, } from '../../../../editor/common/languages.js';
import { CancellationToken } from '../../../../base/common/cancellation.js';
import { InlineCompletion, } from '../../../../editor/common/languages.js';
import { Range } from '../../../../editor/common/core/range.js';
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 { extractCodeFromRegular } from './helpers/extractCodeFromResult.js';
import { extractCodeFromRegular } from '../common/helpers/extractCodeFromResult.js';
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
import { ILLMMessageService } from '../common/llmMessageService.js';
import { _ln, allLinebreakSymbols } from '../common/voidFileService.js';
import { ILLMMessageService } from '../common/sendLLMMessageService.js';
import { isWindows } from '../../../../base/common/platform.js';
import { IVoidSettingsService } from '../common/voidSettingsService.js';
import { FeatureName } from '../common/voidSettingsTypes.js';
import { IConvertToLLMMessageService } from './convertToLLMMessageService.js';
// import { IContextGatheringService } from './contextGatheringService.js';
const allLinebreakSymbols = ['\r\n', '\n']
const _ln = isWindows ? allLinebreakSymbols[0] : allLinebreakSymbols[1]
// The extension this was called from is here - https://github.com/voideditor/void/blob/autocomplete/extensions/void/src/extension/extension.ts
@@ -626,13 +633,14 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
async _provideInlineCompletionItems(
model: ITextModel,
position: Position,
context: InlineCompletionContext,
token: CancellationToken,
): Promise<InlineCompletion[]> {
const isEnabled = this._settingsService.state.globalSettings.enableAutocomplete
if (!isEnabled) return []
const testMode = false
const docUriStr = model.uri.toString();
const docUriStr = model.uri.fsPath;
const prefixAndSuffix = getPrefixAndSuffixInfo(model, position)
const { prefix, suffix } = prefixAndSuffix
@@ -762,8 +770,6 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
// console.log('B')
// create a new autocompletion and add it to cache
const newAutocompletion: Autocompletion = {
id: this._autocompletionId++,
@@ -783,17 +789,26 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
console.log('starting autocomplete...', predictionType)
const featureName: FeatureName = 'Autocomplete'
const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName]
const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined
const aiInstructions = this._settingsService.state.globalSettings.aiInstructions
// set parameters of `newAutocompletion` appropriately
newAutocompletion.llmPromise = new Promise((resolve, reject) => {
const requestId = this._llmMessageService.sendLLMMessage({
messagesType: 'FIMMessage',
messages: {
prefix: llmPrefix,
suffix: llmSuffix,
stopTokens: stopTokens,
},
useProviderFor: 'Autocomplete',
messages: this._convertToLLMMessageService.prepareFIMMessage({
messages: {
prefix: llmPrefix,
suffix: llmSuffix,
stopTokens: stopTokens,
},
aiInstructions
}),
modelSelection,
modelSelectionOptions,
logging: { loggingName: 'Autocomplete' },
onText: () => { }, // unused in FIMMessage
// onText: async ({ fullText, newText }) => {
@@ -838,6 +853,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
newAutocompletion.status = 'error'
reject(message)
},
onAbort: () => { reject('Aborted autocomplete') },
})
newAutocompletion.requestId = requestId
@@ -877,13 +893,15 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
@ILLMMessageService private readonly _llmMessageService: ILLMMessageService,
@IEditorService private readonly _editorService: IEditorService,
@IModelService private readonly _modelService: IModelService,
@IVoidSettingsService private readonly _settingsService: IVoidSettingsService,
@IConvertToLLMMessageService private readonly _convertToLLMMessageService: IConvertToLLMMessageService
// @IContextGatheringService private readonly _contextGatheringService: IContextGatheringService,
) {
super()
this._langFeatureService.inlineCompletionsProvider.register('*', {
this._register(this._langFeatureService.inlineCompletionsProvider.register('*', {
provideInlineCompletions: async (model, position, context, token) => {
const items = await this._provideInlineCompletionItems(model, position, context, token)
const items = await this._provideInlineCompletionItems(model, position)
// console.log('item: ', items?.[0]?.insertText)
return { items: items, }
@@ -900,7 +918,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
if (!resource) return;
const model = this._modelService.getModel(resource)
if (!model) return;
const docUriStr = resource.toString();
const docUriStr = resource.fsPath;
if (!this._autocompletionsOfDocument[docUriStr]) return;
const { prefix, } = getPrefixAndSuffixInfo(model, position)
@@ -920,7 +938,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
});
},
})
}))
}
@@ -929,4 +947,3 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
registerWorkbenchContribution2(AutocompleteService.ID, AutocompleteService, WorkbenchPhase.BlockRestore);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,607 @@
import { Disposable } from '../../../../base/common/lifecycle.js';
import { deepClone } from '../../../../base/common/objects.js';
import { IModelService } from '../../../../editor/common/services/model.js';
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
import { IEditorService } from '../../../services/editor/common/editorService.js';
import { ChatMessage } from '../common/chatThreadServiceTypes.js';
import { getIsReasoningEnabledState, getMaxOutputTokens, getModelCapabilities } from '../common/modelCapabilities.js';
import { toolCallXMLStr, chat_systemMessage, ToolName } from '../common/prompt/prompts.js';
import { AnthropicLLMChatMessage, AnthropicReasoning, LLMChatMessage, LLMFIMMessage, OpenAILLMChatMessage, RawToolParamsObj } from '../common/sendLLMMessageTypes.js';
import { IVoidSettingsService } from '../common/voidSettingsService.js';
import { ChatMode, FeatureName, ModelSelection } from '../common/voidSettingsTypes.js';
import { IDirectoryStrService } from './directoryStrService.js';
import { ITerminalToolService } from './terminalToolService.js';
type SimpleLLMMessage = {
role: 'tool';
content: string;
id: string;
name: ToolName;
rawParams: RawToolParamsObj;
} | {
role: 'user';
content: string;
} | {
role: 'assistant';
content: string;
anthropicReasoning: AnthropicReasoning[] | null;
}
const EMPTY_MESSAGE = '(empty message)'
const CHARS_PER_TOKEN = 4
const TRIM_TO_LEN = 60
// convert messages as if about to send to openai
/*
reference - https://platform.openai.com/docs/guides/function-calling#function-calling-steps
openai MESSAGE (role=assistant):
"tool_calls":[{
"type": "function",
"id": "call_12345xyz",
"function": {
"name": "get_weather",
"arguments": "{\"latitude\":48.8566,\"longitude\":2.3522}"
}]
openai RESPONSE (role=user):
{ "role": "tool",
"tool_call_id": tool_call.id,
"content": str(result) }
also see
openai on prompting - https://platform.openai.com/docs/guides/reasoning#advice-on-prompting
openai on developer system message - https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command
*/
const prepareMessages_openai_tools = (messages: SimpleLLMMessage[]): LLMChatMessage[] => {
const newMessages: OpenAILLMChatMessage[] = [];
for (let i = 0; i < messages.length; i += 1) {
const currMsg = messages[i]
if (currMsg.role !== 'tool') {
newMessages.push(currMsg)
continue
}
// edit previous assistant message to have called the tool
const prevMsg = 0 <= i - 1 && i - 1 <= newMessages.length ? newMessages[i - 1] : undefined
if (prevMsg?.role === 'assistant') {
prevMsg.tool_calls = [{
type: 'function',
id: currMsg.id,
function: {
name: currMsg.name,
arguments: JSON.stringify(currMsg.rawParams)
}
}]
}
// add the tool
newMessages.push({
role: 'tool',
tool_call_id: currMsg.id,
content: currMsg.content,
})
}
return newMessages
}
// convert messages as if about to send to anthropic
/*
https://docs.anthropic.com/en/docs/build-with-claude/tool-use#tool-use-examples
anthropic MESSAGE (role=assistant):
"content": [{
"type": "text",
"text": "<thinking>I need to call the get_weather function, and the user wants SF, which is likely San Francisco, CA.</thinking>"
}, {
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "location": "San Francisco, CA", "unit": "celsius" }
}]
anthropic RESPONSE (role=user):
"content": [{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "15 degrees"
}]
Converts:
assistant: ...content
tool: (id, name, params)
->
assistant: ...content, call(name, id, params)
user: ...content, result(id, content)
*/
const prepareMessages_anthropic_tools = (messages: SimpleLLMMessage[], supportsAnthropicReasoning: boolean): LLMChatMessage[] => {
const newMessages: (AnthropicLLMChatMessage | (SimpleLLMMessage & { role: 'tool' }))[] = messages;
for (let i = 0; i < messages.length; i += 1) {
const currMsg = messages[i]
// add anthropic reasoning
if (currMsg.role === 'assistant') {
if (currMsg.anthropicReasoning && supportsAnthropicReasoning) {
const content = currMsg.content
newMessages[i] = {
role: 'assistant',
content: content ? [...currMsg.anthropicReasoning, { type: 'text' as const, text: content }] : currMsg.anthropicReasoning
}
}
continue
}
if (currMsg.role === 'user') {
newMessages[i] = {
role: 'user',
content: currMsg.content,
}
continue
}
if (currMsg.role === 'tool') {
// add anthropic tools
const prevMsg = 0 <= i - 1 && i - 1 <= newMessages.length ? newMessages[i - 1] : undefined
// make it so the assistant called the tool
if (prevMsg?.role === 'assistant') {
if (typeof prevMsg.content === 'string') prevMsg.content = [{ type: 'text', text: prevMsg.content }]
prevMsg.content.push({ type: 'tool_use', id: currMsg.id, name: currMsg.name, input: currMsg.rawParams })
}
// turn each tool into a user message with tool results at the end
newMessages[i] = {
role: 'user',
content: [{ type: 'tool_result', tool_use_id: currMsg.id, content: currMsg.content }]
}
continue
}
}
// we just removed the tools
return newMessages as AnthropicLLMChatMessage[]
}
const prepareMessages_XML_tools = (messages: SimpleLLMMessage[], supportsAnthropicReasoning: boolean): LLMChatMessage[] => {
const llmChatMessages: LLMChatMessage[] = [];
for (let i = 0; i < messages.length; i += 1) {
const c = messages[i]
const next = 0 <= i + 1 && i + 1 <= messages.length - 1 ? messages[i + 1] : null
if (c.role === 'assistant') {
// if called a tool (message after it), re-add its XML to the message
// alternatively, could just hold onto the original output, but this way requires less piping raw strings everywhere
let content: LLMChatMessage['content'] = c.content
if (next?.role === 'tool') {
content = `${content}\n\n${toolCallXMLStr(next.name, next.rawParams)}`
}
// anthropic reasoning
if (c.anthropicReasoning && supportsAnthropicReasoning) {
content = content ? [...c.anthropicReasoning, { type: 'text' as const, text: content }] : c.anthropicReasoning
}
llmChatMessages.push({
role: 'assistant',
content
})
}
// add user or tool to the previous user message
else if (c.role === 'user' || c.role === 'tool') {
if (c.role === 'tool')
c.content = `<${c.name}_result>\n${c.content}\n</${c.name}_result>`
if (llmChatMessages.length === 0 || llmChatMessages[llmChatMessages.length - 1].role !== 'user')
llmChatMessages.push({
role: 'user',
content: c.content
})
else
llmChatMessages[llmChatMessages.length - 1].content += '\n\n' + c.content
}
}
return llmChatMessages
}
const prepareMessages_providerSpecific = (messages: SimpleLLMMessage[], specialToolFormat: 'openai-style' | 'anthropic-style' | undefined, supportsAnthropicReasoning: boolean): LLMChatMessage[] => {
const llmChatMessages: LLMChatMessage[] = []
if (!specialToolFormat) { // XML tool behavior
return prepareMessages_XML_tools(messages, supportsAnthropicReasoning)
}
else if (specialToolFormat === 'anthropic-style') {
return prepareMessages_anthropic_tools(messages, supportsAnthropicReasoning)
}
else if (specialToolFormat === 'openai-style') {
return prepareMessages_openai_tools(messages)
}
return llmChatMessages
}
// --- CHAT ---
const prepareMessages = ({
messages,
systemMessage,
aiInstructions,
supportsSystemMessage,
specialToolFormat,
supportsAnthropicReasoning,
contextWindow,
maxOutputTokens,
}: {
messages: SimpleLLMMessage[],
systemMessage: string,
aiInstructions: string,
supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated',
specialToolFormat: 'openai-style' | 'anthropic-style' | undefined,
supportsAnthropicReasoning: boolean,
contextWindow: number,
maxOutputTokens: number | null | undefined,
}): { messages: LLMChatMessage[], separateSystemMessage: string | undefined } => {
maxOutputTokens = maxOutputTokens ?? 4_096 // default to 4096
// ================ trim ================
messages = deepClone(messages)
messages = messages.map(m => ({ ...m, content: m.role !== 'tool' ? m.content.trim() : m.content }))
// ================ fit into context ================
// the higher the weight, the higher the desire to truncate - TRIM HIGHEST WEIGHT MESSAGES
const alreadyTrimmedIdxes = new Set<number>()
const weight = (message: SimpleLLMMessage, messages: SimpleLLMMessage[], idx: number) => {
const base = message.content.length
let multiplier: number
multiplier = 1 + (messages.length - 1 - idx) / messages.length // slow rampdown from 2 to 1 as index increases
if (message.role === 'user') {
multiplier *= 1
}
else {
multiplier *= 10 // llm tokens are far less valuable than user tokens
}
// 1st message, last 3 msgs, any already modified message should be low in weight
if (idx === 0 || idx >= messages.length - 1 - 3 || alreadyTrimmedIdxes.has(idx)) {
multiplier *= .05
}
return base * multiplier
}
const _findLargestByWeight = (messages: SimpleLLMMessage[]) => {
let largestIndex = -1
let largestWeight = -Infinity
for (let i = 0; i < messages.length; i += 1) {
const m = messages[i]
const w = weight(m, messages, i)
if (w > largestWeight) {
largestWeight = w
largestIndex = i
}
}
return largestIndex
}
let totalLen = 0
for (const m of messages) { totalLen += m.content.length }
const charsNeedToTrim = totalLen - (contextWindow - maxOutputTokens) * CHARS_PER_TOKEN
// <----------------------------------------->
// 0 | | |
// | contextWindow |
// contextWindow - maxOut|putTokens
// totalLen
let remainingCharsToTrim = charsNeedToTrim
let i = 0
while (remainingCharsToTrim > 0) {
i += 1
if (i > 100) break
const trimIdx = _findLargestByWeight(messages)
const m = messages[trimIdx]
// if can finish here, do
const numCharsWillTrim = m.content.length - TRIM_TO_LEN
if (numCharsWillTrim > remainingCharsToTrim) {
m.content = m.content.slice(0, m.content.length - remainingCharsToTrim).trim()
break
}
remainingCharsToTrim -= numCharsWillTrim
m.content = m.content.substring(0, TRIM_TO_LEN - 3) + '...'
alreadyTrimmedIdxes.add(trimIdx)
}
// ================ tools and anthropicReasoning ================
const llmMessages: LLMChatMessage[] = prepareMessages_providerSpecific(messages, specialToolFormat, supportsAnthropicReasoning)
// ================ system message concat ================
// find system messages and concatenate them
const newSystemMessage = aiInstructions ?
`${(systemMessage ? `${systemMessage}\n\n` : '')}GUIDELINES\n${aiInstructions}`
: systemMessage
let separateSystemMessageStr: string | undefined = undefined
// if supports system message
if (supportsSystemMessage) {
if (supportsSystemMessage === 'separated')
separateSystemMessageStr = newSystemMessage
else if (supportsSystemMessage === 'system-role')
llmMessages.unshift({ role: 'system', content: newSystemMessage }) // add new first message
else if (supportsSystemMessage === 'developer-role')
llmMessages.unshift({ role: 'developer', content: newSystemMessage }) // add new first message
}
// if does not support system message
else {
const newFirstMessage = {
role: 'user',
content: `<SYSTEM_MESSAGE>\n${newSystemMessage}\n</SYSTEM_MESSAGE>\n${llmMessages[0].content}`
} as const
llmMessages.splice(0, 1) // delete first message
llmMessages.unshift(newFirstMessage) // add new first message
}
// ================ no empty message ================
for (const currMsg of llmMessages) {
if (currMsg.role === 'tool') continue
// if content is a string, replace string with empty msg
if (typeof currMsg.content === 'string')
currMsg.content = currMsg.content || EMPTY_MESSAGE
else {
// if content is an array, replace any empty text entries with empty msg, and make sure there's at least 1 entry
for (const c of currMsg.content) {
if (c.type === 'text') c.text = c.text || EMPTY_MESSAGE
}
if (currMsg.content.length === 0) currMsg.content = [{ type: 'text', text: EMPTY_MESSAGE }]
}
}
return {
messages: llmMessages,
separateSystemMessage: separateSystemMessageStr,
} as const
}
export interface IConvertToLLMMessageService {
readonly _serviceBrand: undefined;
prepareLLMSimpleMessages: (opts: { simpleMessages: SimpleLLMMessage[], systemMessage: string, modelSelection: ModelSelection | null, featureName: FeatureName }) => { messages: LLMChatMessage[], separateSystemMessage: string | undefined };
prepareLLMChatMessages: (opts: { chatMessages: ChatMessage[], chatMode: ChatMode, modelSelection: ModelSelection | null }) => Promise<{ messages: LLMChatMessage[], separateSystemMessage: string | undefined }>
prepareFIMMessage(opts: { messages: LLMFIMMessage, aiInstructions: string, }): { prefix: string, suffix: string, stopTokens: string[] }
}
export const IConvertToLLMMessageService = createDecorator<IConvertToLLMMessageService>('ConvertToLLMMessageService');
class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMessageService {
_serviceBrand: undefined;
constructor(
@IModelService private readonly modelService: IModelService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IEditorService private readonly editorService: IEditorService,
@IDirectoryStrService private readonly directoryStrService: IDirectoryStrService,
@ITerminalToolService private readonly terminalToolService: ITerminalToolService,
@IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService,
) {
super()
}
// system message
private _generateChatMessagesSystemMessage = async (chatMode: ChatMode, specialToolFormat: 'openai-style' | 'anthropic-style' | undefined) => {
const workspaceFolders = this.workspaceContextService.getWorkspace().folders.map(f => f.uri.fsPath)
const openedURIs = this.modelService.getModels().filter(m => m.isAttachedToEditor()).map(m => m.uri.fsPath) || [];
const activeURI = this.editorService.activeEditor?.resource?.fsPath;
const directoryStr = await this.directoryStrService.getAllDirectoriesStr({
cutOffMessage: chatMode === 'agent' || chatMode === 'gather' ?
`...Directories string cut off, use tools to read more...`
: `...Directories string cut off, ask user for more if necessary...`
})
const includeXMLToolDefinitions = specialToolFormat === undefined
const runningTerminalIds = this.terminalToolService.listTerminalIds()
const systemMessage = chat_systemMessage({ workspaceFolders, openedURIs, directoryStr, activeURI, runningTerminalIds, chatMode, includeXMLToolDefinitions })
return systemMessage
}
// --- LLM Chat messages ---
private _chatMessagesToSimpleMessages(chatMessages: ChatMessage[]): SimpleLLMMessage[] {
const simpleLLMMessages: SimpleLLMMessage[] = []
for (const m of chatMessages) {
if (m.role === 'checkpoint') continue
if (m.role === 'interrupted_streaming_tool') continue
if (m.role === 'assistant') {
simpleLLMMessages.push({
role: m.role,
content: m.displayContent,
anthropicReasoning: m.anthropicReasoning,
})
}
else if (m.role === 'tool') {
simpleLLMMessages.push({
role: m.role,
content: m.content,
name: m.name,
id: m.id,
rawParams: m.rawParams,
})
}
else if (m.role === 'user') {
simpleLLMMessages.push({
role: m.role,
content: m.content,
})
}
}
return simpleLLMMessages
}
prepareLLMSimpleMessages: IConvertToLLMMessageService['prepareLLMSimpleMessages'] = ({ simpleMessages, systemMessage, modelSelection, featureName }) => {
if (modelSelection === null) return { messages: [], separateSystemMessage: undefined }
const { providerName, modelName } = modelSelection
const {
specialToolFormat,
contextWindow,
supportsSystemMessage,
} = getModelCapabilities(providerName, modelName)
const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName]
const aiInstructions = this.voidSettingsService.state.globalSettings.aiInstructions
const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions)
const maxOutputTokens = getMaxOutputTokens(providerName, modelName, { isReasoningEnabled })
const { messages, separateSystemMessage } = prepareMessages({
messages: simpleMessages,
systemMessage,
aiInstructions,
supportsSystemMessage,
specialToolFormat,
supportsAnthropicReasoning: providerName === 'anthropic',
contextWindow,
maxOutputTokens,
})
return { messages, separateSystemMessage };
}
prepareLLMChatMessages: IConvertToLLMMessageService['prepareLLMChatMessages'] = async ({ chatMessages, chatMode, modelSelection }) => {
if (modelSelection === null) return { messages: [], separateSystemMessage: undefined }
const { providerName, modelName } = modelSelection
const {
specialToolFormat,
contextWindow,
supportsSystemMessage,
} = getModelCapabilities(providerName, modelName)
const systemMessage = await this._generateChatMessagesSystemMessage(chatMode, specialToolFormat)
const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection['Chat'][modelSelection.providerName]?.[modelSelection.modelName]
const aiInstructions = this.voidSettingsService.state.globalSettings.aiInstructions
const isReasoningEnabled = getIsReasoningEnabledState('Chat', providerName, modelName, modelSelectionOptions)
const maxOutputTokens = getMaxOutputTokens(providerName, modelName, { isReasoningEnabled })
const llmMessages = this._chatMessagesToSimpleMessages(chatMessages)
const { messages, separateSystemMessage } = prepareMessages({
messages: llmMessages,
systemMessage,
aiInstructions,
supportsSystemMessage,
specialToolFormat,
supportsAnthropicReasoning: providerName === 'anthropic',
contextWindow,
maxOutputTokens,
})
return { messages, separateSystemMessage };
}
// --- FIM ---
prepareFIMMessage: IConvertToLLMMessageService['prepareFIMMessage'] = ({ messages, aiInstructions }) => {
let prefix = `\
${!aiInstructions ? '' : `\
// Instructions:
// Do not output an explanation. Try to avoid outputting comments. Only output the middle code.
${aiInstructions.split('\n').map(line => `//${line}`).join('\n')}`}
${messages.prefix}`
const suffix = messages.suffix
const stopTokens = messages.stopTokens
return { prefix, suffix, stopTokens }
}
}
// pick one and delete the other:
registerSingleton(IConvertToLLMMessageService, ConvertToLLMMessageService, InstantiationType.Eager);
/*
Gemini has this, but they're openai-compat so we don't need to implement this
gemini request:
{ "role": "assistant",
"content": null,
"function_call": {
"name": "get_weather",
"arguments": {
"latitude": 48.8566,
"longitude": 2.3522
}
}
}
gemini response:
{ "role": "assistant",
"function_response": {
"name": "get_weather",
"response": {
"temperature": "15°C",
"condition": "Cloudy"
}
}
}
*/
@@ -0,0 +1,429 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { URI } from '../../../../base/common/uri.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { IFileService } from '../../../../platform/files/common/files.js';
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
import { ShallowDirectoryItem, ToolCallParams, ToolResultType } from '../common/toolsServiceTypes.js';
import { MAX_CHILDREN_URIs_PAGE } from './toolsService.js';
import { IExplorerService } from '../../files/browser/files.js';
import { SortOrder } from '../../files/common/files.js';
import { ExplorerItem } from '../../files/common/explorerModel.js';
import { MAX_DIRSTR_CHARS_TOTAL_BEGINNING, MAX_DIRSTR_CHARS_TOTAL_TOOL } from '../common/prompt/prompts.js';
const MAX_FILES_TOTAL = 300;
const DEFAULT_MAX_DEPTH = 3;
const DEFAULT_MAX_ITEMS_PER_DIR = 3;
const START_MAX_DEPTH = Infinity;
const START_MAX_ITEMS_PER_DIR = Infinity; // Add start value as Infinity
export interface IDirectoryStrService {
readonly _serviceBrand: undefined;
getDirectoryStrTool(uri: URI, options?: { maxItemsPerDir?: number }): Promise<string>
getAllDirectoriesStr(opts: { cutOffMessage: string, maxItemsPerDir?: number }): Promise<string>
}
export const IDirectoryStrService = createDecorator<IDirectoryStrService>('voidDirectoryStrService');
// Check if it's a known filtered type like .git
const shouldExcludeDirectory = (item: ExplorerItem) => {
if (item.name === '.git' ||
item.name === 'node_modules' ||
item.name.startsWith('.') ||
item.name === 'dist' ||
item.name === 'build' ||
item.name === 'out' ||
item.name === 'bin' ||
item.name === 'coverage' ||
item.name === '__pycache__' ||
item.name === 'env' ||
item.name === 'venv' ||
item.name === 'tmp' ||
item.name === 'temp' ||
item.name === 'artifacts' ||
item.name === 'target' ||
item.name === 'obj' ||
item.name === 'vendor' ||
item.name === 'logs' ||
item.name === 'cache' ||
item.name === 'resource' ||
item.name === 'resources'
) {
return true;
}
if (item.name.match(/\bout\b/)) return true
if (item.name.match(/\bbuild\b/)) return true
return false;
}
// ---------- ONE LAYER DEEP ----------
export const computeDirectoryTree1Deep = async (
fileService: IFileService,
rootURI: URI,
pageNumber: number = 1,
): Promise<ToolResultType['ls_dir']> => {
const stat = await fileService.resolve(rootURI, { resolveMetadata: false });
if (!stat.isDirectory) {
return { children: null, hasNextPage: false, hasPrevPage: false, itemsRemaining: 0 };
}
const nChildren = stat.children?.length ?? 0;
const fromChildIdx = MAX_CHILDREN_URIs_PAGE * (pageNumber - 1);
const toChildIdx = MAX_CHILDREN_URIs_PAGE * pageNumber - 1; // INCLUSIVE
const listChildren = stat.children?.slice(fromChildIdx, toChildIdx + 1);
const children: ShallowDirectoryItem[] = listChildren?.map(child => ({
name: child.name,
uri: child.resource,
isDirectory: child.isDirectory,
isSymbolicLink: child.isSymbolicLink
})) ?? [];
const hasNextPage = (nChildren - 1) > toChildIdx;
const hasPrevPage = pageNumber > 1;
const itemsRemaining = Math.max(0, nChildren - (toChildIdx + 1));
return {
children,
hasNextPage,
hasPrevPage,
itemsRemaining
};
};
export const stringifyDirectoryTree1Deep = (params: ToolCallParams['ls_dir'], result: ToolResultType['ls_dir']): string => {
if (!result.children) {
return `Error: ${params.rootURI} is not a directory`;
}
let output = '';
const entries = result.children;
if (!result.hasPrevPage) { // is first page
output += `${params.rootURI.fsPath}\n`;
}
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
const isLast = i === entries.length - 1 && !result.hasNextPage;
const prefix = isLast ? '└── ' : '├── ';
output += `${prefix}${entry.name}${entry.isDirectory ? '/' : ''}${entry.isSymbolicLink ? ' (symbolic link)' : ''}\n`;
}
if (result.hasNextPage) {
output += `└── (${result.itemsRemaining} results remaining...)\n`;
}
return output;
};
// ---------- IN GENERAL ----------
// Remove the old computeDirectoryTree function and replace with a combined version that handles both computation and rendering
const computeAndStringifyDirectoryTree = async (
eItem: ExplorerItem,
explorerService: IExplorerService,
MAX_CHARS: number,
fileCount: { count: number } = { count: 0 },
options: { maxDepth?: number, currentDepth?: number, maxItemsPerDir?: number } = {}
): Promise<{ content: string, wasCutOff: boolean }> => {
// Set default values for options
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
const currentDepth = options.currentDepth ?? 0;
const maxItemsPerDir = options.maxItemsPerDir ?? DEFAULT_MAX_ITEMS_PER_DIR;
// Check if we've reached the max depth
if (currentDepth > maxDepth) {
return { content: '', wasCutOff: true };
}
// Check if we've reached the file limit
if (fileCount.count >= MAX_FILES_TOTAL) {
return { content: '', wasCutOff: true };
}
// If we're already exceeding the max characters, return immediately
if (MAX_CHARS <= 0) {
return { content: '', wasCutOff: true };
}
// Increment file count
fileCount.count++;
// Add the root node first (without tree characters)
const nodeLine = `${eItem.name}${eItem.isDirectory ? '/' : ''}${eItem.isSymbolicLink ? ' (symbolic link)' : ''}\n`;
if (nodeLine.length > MAX_CHARS) {
return { content: '', wasCutOff: true };
}
let content = nodeLine;
let wasCutOff = false;
let remainingChars = MAX_CHARS - nodeLine.length;
// Check if it's a directory we should skip
const isGitIgnoredDirectory = eItem.isDirectory && shouldExcludeDirectory(eItem);
// Fetch and process children if not a filtered directory
if (eItem.isDirectory && !isGitIgnoredDirectory) {
// Fetch children with Modified sort order to show recently modified first
const eChildren = await eItem.fetchChildren(SortOrder.Modified);
// Then recursively add all children with proper tree formatting
if (eChildren && eChildren.length > 0) {
const { childrenContent, childrenCutOff } = await renderChildrenCombined(
eChildren,
remainingChars,
'',
explorerService,
fileCount,
{ maxDepth, currentDepth, maxItemsPerDir } // Pass maxItemsPerDir to the render function
);
content += childrenContent;
wasCutOff = childrenCutOff;
}
}
return { content, wasCutOff };
};
// Helper function to render children with proper tree formatting
const renderChildrenCombined = async (
children: ExplorerItem[],
maxChars: number,
parentPrefix: string,
explorerService: IExplorerService,
fileCount: { count: number },
options: { maxDepth: number, currentDepth: number, maxItemsPerDir?: number }
): Promise<{ childrenContent: string, childrenCutOff: boolean }> => {
const { maxDepth, currentDepth } = options; // Remove maxItemsPerDir from destructuring
// Get maxItemsPerDir separately and make sure we use it
// For first level (currentDepth = 0), always use Infinity regardless of what was passed
const maxItemsPerDir = currentDepth === 0 ?
Infinity :
(options.maxItemsPerDir ?? DEFAULT_MAX_ITEMS_PER_DIR);
const nextDepth = currentDepth + 1;
let childrenContent = '';
let childrenCutOff = false;
let remainingChars = maxChars;
// Check if we've reached max depth
if (nextDepth > maxDepth) {
return { childrenContent: '', childrenCutOff: true };
}
// Apply maxItemsPerDir limit - only process the specified number of items
const itemsToProcess = maxItemsPerDir === Infinity ? children : children.slice(0, maxItemsPerDir);
const hasMoreItems = children.length > itemsToProcess.length;
for (let i = 0; i < itemsToProcess.length; i++) {
// Check if we've reached the file limit
if (fileCount.count >= MAX_FILES_TOTAL) {
childrenCutOff = true;
break;
}
const child = itemsToProcess[i];
const isLast = (i === itemsToProcess.length - 1) && !hasMoreItems;
// Create the tree branch symbols
const branchSymbol = isLast ? '└── ' : '├── ';
const childLine = `${parentPrefix}${branchSymbol}${child.name}${child.isDirectory ? '/' : ''}${child.isSymbolicLink ? ' (symbolic link)' : ''}\n`;
// Check if adding this line would exceed the limit
if (childLine.length > remainingChars) {
childrenCutOff = true;
break;
}
childrenContent += childLine;
remainingChars -= childLine.length;
fileCount.count++;
const nextLevelPrefix = parentPrefix + (isLast ? ' ' : '│ ');
// Skip processing children for git ignored directories
const isGitIgnoredDirectory = child.isDirectory && shouldExcludeDirectory(child);
// Create the prefix for the next level (continuation line or space)
if (child.isDirectory && !isGitIgnoredDirectory) {
// Fetch children with Modified sort order to show recently modified first
const eChildren = await child.fetchChildren(SortOrder.Modified);
if (eChildren && eChildren.length > 0) {
const {
childrenContent: grandChildrenContent,
childrenCutOff: grandChildrenCutOff
} = await renderChildrenCombined(
eChildren,
remainingChars,
nextLevelPrefix,
explorerService,
fileCount,
{ maxDepth, currentDepth: nextDepth, maxItemsPerDir }
);
if (grandChildrenContent.length > 0) {
childrenContent += grandChildrenContent;
remainingChars -= grandChildrenContent.length;
}
if (grandChildrenCutOff) {
childrenCutOff = true;
}
}
}
}
// Add a message if we truncated the items due to maxItemsPerDir
if (hasMoreItems) {
const remainingCount = children.length - itemsToProcess.length;
const truncatedLine = `${parentPrefix}└── (${remainingCount} more items not shown...)\n`;
if (truncatedLine.length <= remainingChars) {
childrenContent += truncatedLine;
remainingChars -= truncatedLine.length;
}
childrenCutOff = true;
}
return { childrenContent, childrenCutOff };
};
// ---------------------------------------------------
class DirectoryStrService extends Disposable implements IDirectoryStrService {
_serviceBrand: undefined;
constructor(
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IExplorerService private readonly explorerService: IExplorerService,
) {
super();
}
async getDirectoryStrTool(uri: URI, options?: { maxItemsPerDir?: number }) {
const eRoot = this.explorerService.findClosest(uri)
if (!eRoot) throw new Error(`There was a problem reading the URI: ${uri.fsPath}.`)
const maxItemsPerDir = options?.maxItemsPerDir ?? START_MAX_ITEMS_PER_DIR; // Use START_MAX_ITEMS_PER_DIR
// First try with START_MAX_DEPTH
const { content: initialContent, wasCutOff: initialCutOff } = await computeAndStringifyDirectoryTree(
eRoot,
this.explorerService,
MAX_DIRSTR_CHARS_TOTAL_TOOL,
{ count: 0 },
{ maxDepth: START_MAX_DEPTH, currentDepth: 0, maxItemsPerDir }
);
// If cut off, try again with DEFAULT_MAX_DEPTH and DEFAULT_MAX_ITEMS_PER_DIR
let content, wasCutOff;
if (initialCutOff) {
const result = await computeAndStringifyDirectoryTree(
eRoot,
this.explorerService,
MAX_DIRSTR_CHARS_TOTAL_TOOL,
{ count: 0 },
{ maxDepth: DEFAULT_MAX_DEPTH, currentDepth: 0, maxItemsPerDir: DEFAULT_MAX_ITEMS_PER_DIR }
);
content = result.content;
wasCutOff = result.wasCutOff;
} else {
content = initialContent;
wasCutOff = initialCutOff;
}
let c = content.substring(0, MAX_DIRSTR_CHARS_TOTAL_TOOL)
c = `Directory of ${uri.fsPath}:\n${content}`
if (wasCutOff) c = `${c}\n...Result was truncated...`
return c
}
async getAllDirectoriesStr({ cutOffMessage, maxItemsPerDir }: { cutOffMessage: string, maxItemsPerDir?: number }) {
let str: string = '';
let cutOff = false;
const folders = this.workspaceContextService.getWorkspace().folders;
if (folders.length === 0)
return '(NO WORKSPACE OPEN)';
// Use START_MAX_ITEMS_PER_DIR if not specified
const startMaxItemsPerDir = maxItemsPerDir ?? START_MAX_ITEMS_PER_DIR;
for (let i = 0; i < folders.length; i += 1) {
if (i > 0) str += '\n';
// this prioritizes filling 1st workspace before any other, etc
const f = folders[i];
str += `Directory of ${f.uri.fsPath}:\n`;
const rootURI = f.uri;
const eRoot = this.explorerService.findClosestRoot(rootURI);
if (!eRoot) continue;
// First try with START_MAX_DEPTH and startMaxItemsPerDir
const { content: initialContent, wasCutOff: initialCutOff } = await computeAndStringifyDirectoryTree(
eRoot,
this.explorerService,
MAX_DIRSTR_CHARS_TOTAL_BEGINNING - str.length,
{ count: 0 },
{ maxDepth: START_MAX_DEPTH, currentDepth: 0, maxItemsPerDir: startMaxItemsPerDir }
);
// If cut off, try again with DEFAULT_MAX_DEPTH and DEFAULT_MAX_ITEMS_PER_DIR
let content, wasCutOff;
if (initialCutOff) {
const result = await computeAndStringifyDirectoryTree(
eRoot,
this.explorerService,
MAX_DIRSTR_CHARS_TOTAL_BEGINNING - str.length,
{ count: 0 },
{ maxDepth: DEFAULT_MAX_DEPTH, currentDepth: 0, maxItemsPerDir: DEFAULT_MAX_ITEMS_PER_DIR }
);
content = result.content;
wasCutOff = result.wasCutOff;
} else {
content = initialContent;
wasCutOff = initialCutOff;
}
str += content;
if (wasCutOff) {
cutOff = true;
break;
}
}
if (cutOff) {
return `${str}\n${cutOffMessage}`
}
return str
}
}
registerSingleton(IDirectoryStrService, DirectoryStrService, InstantiationType.Delayed);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,72 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Event } from '../../../../base/common/event.js';
import { URI } from '../../../../base/common/uri.js';
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { Diff, DiffArea, VoidFileSnapshot } from '../common/editCodeServiceTypes.js';
export type StartBehavior = 'accept-conflicts' | 'reject-conflicts' | 'keep-conflicts'
export type CallBeforeStartApplyingOpts = {
from: 'QuickEdit';
diffareaid: number; // id of the CtrlK area (contains text selection)
} | {
from: 'ClickApply';
uri: 'current' | URI;
}
export type StartApplyingOpts = {
from: 'QuickEdit';
diffareaid: number; // id of the CtrlK area (contains text selection)
startBehavior: StartBehavior;
} | {
from: 'ClickApply';
applyStr: string;
uri: 'current' | URI;
startBehavior: StartBehavior;
}
export type AddCtrlKOpts = {
startLine: number,
endLine: number,
editor: ICodeEditor,
}
export const IEditCodeService = createDecorator<IEditCodeService>('editCodeService');
export interface IEditCodeService {
readonly _serviceBrand: undefined;
callBeforeStartApplying(opts: CallBeforeStartApplyingOpts): Promise<void>;
startApplying(opts: StartApplyingOpts): [URI, Promise<void>] | null;
addCtrlKZone(opts: AddCtrlKOpts): number | undefined;
removeCtrlKZone(opts: { diffareaid: number }): void;
diffAreaOfId: Record<string, DiffArea>;
diffAreasOfURI: Record<string, Set<string> | undefined>;
diffOfId: Record<string, Diff>;
acceptOrRejectAllDiffAreas(opts: { uri: URI, removeCtrlKs: boolean, behavior: 'reject' | 'accept', _addToHistory?: boolean }): void;
// events
onDidAddOrDeleteDiffZones: Event<{ uri: URI }>;
onDidChangeDiffsInDiffZoneNotStreaming: Event<{ uri: URI; diffareaid: number }>; // only fires when not streaming!!! streaming would be too much
onDidChangeStreamingInDiffZone: Event<{ uri: URI; diffareaid: number }>;
onDidChangeStreamingInCtrlKZone: Event<{ uri: URI; diffareaid: number }>;
// CtrlKZone streaming state
isCtrlKZoneStreaming(opts: { diffareaid: number }): boolean;
interruptCtrlKStreaming(opts: { diffareaid: number }): void;
// // DiffZone codeBoxId streaming state
interruptURIStreaming(opts: { uri: URI }): void;
// testDiffs(): void;
getVoidFileSnapshot(uri: URI): VoidFileSnapshot;
restoreVoidFileSnapshot(uri: URI, snapshot: VoidFileSnapshot): void;
}
@@ -25,7 +25,7 @@ export interface IConsistentItemService {
export const IConsistentItemService = createDecorator<IConsistentItemService>('ConsistentItemService');
export class ConsistentItemService extends Disposable {
export class ConsistentItemService extends Disposable implements IConsistentItemService {
readonly _serviceBrand: undefined
@@ -1,174 +0,0 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
// eg "bash" -> "shell"
export const nameToVscodeLanguage: { [key: string]: string } = {
// Web Technologies
'html': 'html',
'css': 'css',
'scss': 'scss',
'sass': 'scss',
'less': 'less',
'javascript': 'typescript',
'js': 'typescript', // use more general renderer
'jsx': 'typescript',
'typescript': 'typescript',
'ts': 'typescript',
'tsx': 'typescript',
'json': 'json',
'jsonc': 'json',
// Programming Languages
'python': 'python',
'py': 'python',
'java': 'java',
'cpp': 'cpp',
'c++': 'cpp',
'c': 'c',
'csharp': 'csharp',
'cs': 'csharp',
'c#': 'csharp',
'go': 'go',
'golang': 'go',
'rust': 'rust',
'rs': 'rust',
'ruby': 'ruby',
'rb': 'ruby',
'php': 'php',
'shell': 'shell',
'bash': 'shell',
'sh': 'shell',
'zsh': 'shell',
// Markup and Config
'markdown': 'markdown',
'md': 'markdown',
'xml': 'xml',
'svg': 'xml',
'yaml': 'yaml',
'yml': 'yaml',
'ini': 'ini',
'toml': 'ini',
// Database and Query Languages
'sql': 'sql',
'mysql': 'sql',
'postgresql': 'sql',
'graphql': 'graphql',
'gql': 'graphql',
// Others
'dockerfile': 'dockerfile',
'docker': 'dockerfile',
'makefile': 'makefile',
'plaintext': 'plaintext',
'text': 'plaintext'
};
// eg ".ts" -> "typescript"
const fileExtensionToVscodeLanguage: { [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',
'c': 'c',
'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',
'mk': 'makefile',
// Config Files and Dot Files
'npmrc': 'ini',
'env': 'ini',
'gitignore': 'ignore',
'dockerignore': 'ignore',
'eslintrc': 'json',
'babelrc': 'json',
'prettierrc': 'json',
'stylelintrc': 'json',
'editorconfig': 'ini',
'htaccess': 'apacheconf',
'conf': 'ini',
'config': 'ini',
// Package Files
'package': 'json',
'package-lock': 'json',
'gemfile': 'ruby',
'podfile': 'ruby',
'rakefile': 'ruby',
// Build Systems
'cmake': 'cmake',
'makefile': 'makefile',
'gradle': 'groovy',
// Shell Scripts
'bashrc': 'shell',
'zshrc': 'shell',
'fish': 'shell',
// Version Control
'gitconfig': 'ini',
'hgrc': 'ini',
'svnconfig': 'ini',
// Web Server
'nginx': 'nginx',
// Misc Config
'properties': 'properties',
'cfg': 'ini',
'reg': 'ini'
};
export function filenameToVscodeLanguage(filename: string): string | undefined {
const ext = filename.toLowerCase().split('.').pop();
if (!ext) return undefined;
return fileExtensionToVscodeLanguage[ext];
}
@@ -3,34 +3,9 @@
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { ComputedDiff } from '../../common/editCodeServiceTypes.js';
import { diffLines } from '../react/out/diff/index.js'
export type ComputedDiff = {
type: 'edit';
originalCode: string;
originalStartLine: number;
originalEndLine: number;
code: string;
startLine: number; // 1-indexed
endLine: number;
} | {
type: 'insertion';
// originalCode: string;
originalStartLine: number; // insertion starts on column 0 of this
// originalEndLine: number;
code: string;
startLine: number;
endLine: number;
} | {
type: 'deletion';
originalCode: string;
originalStartLine: number;
originalEndLine: number;
// code: string;
startLine: number; // deletion starts on column 0 of this
// endLine: number;
}
export function findDiffs(oldStr: string, newStr: string) {
// this makes it so the end of the file always ends with a \n (if you don't have this, then diffing E vs E\n gives an "edit". With it, you end up diffing E\n vs E\n\n which now properly gives an insertion)
@@ -1,52 +0,0 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { URI } from '../../../../../base/common/uri.js'
import { EndOfLinePreference } from '../../../../../editor/common/model.js'
import { IModelService } from '../../../../../editor/common/services/model.js'
import { IFileService } from '../../../../../platform/files/common/files.js'
// attempts to read URI of currently opened model, then of raw file
export const VSReadFile = async (uri: URI, modelService: IModelService, fileService: IFileService) => {
const modelResult = await _VSReadModel(modelService, uri)
if (modelResult) return modelResult
const fileResult = await _VSReadFileRaw(fileService, uri)
if (fileResult) return fileResult
return ''
}
// read files from VSCode. preferred (but appears to only work if the model of this URI already exists. If it doesn't use the other function.)
const _VSReadModel = async (modelService: IModelService, uri: URI): Promise<string | null> => {
// attempt to read saved model (doesn't work if application was reloaded...)
const model = modelService.getModel(uri)
if (model) {
return model.getValue(EndOfLinePreference.LF)
}
// backup logic - look at all opened models and check if they have the same `fsPath`
const models = modelService.getModels()
for (const model of models) {
if (model.uri.fsPath === uri.fsPath)
return model.getValue(EndOfLinePreference.LF);
}
return null
}
const _VSReadFileRaw = async (fileService: IFileService, uri: URI) => {
try {
const res = await fileService.readFile(uri)
const str = res.value.toString()
return str
} catch (e) {
return null
}
}
@@ -76,93 +76,107 @@
opacity: 80%;
}
/* styles for all containers used by void */
.void-scope {
--scrollbar-vertical-width: 8px;
--scrollbar-horizontal-height: 6px;
}
/* Target both void-scope and all its descendants with scrollbars */
.void-scope,
.void-scope * {
scrollbar-width: thin !important;
scrollbar-color: var(--void-bg-1) var(--void-bg-3) !important; /* For Firefox */
}
.void-scope::-webkit-scrollbar,
.void-scope *::-webkit-scrollbar {
width: var(--scrollbar-vertical-width) !important;
height: var(--scrollbar-horizontal-height) !important;
background-color: var(--void-bg-3) !important;
}
.void-scope::-webkit-scrollbar-thumb,
.void-scope *::-webkit-scrollbar-thumb {
background-color: var(--void-bg-1) !important;
border-radius: 4px !important;
border: none !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.void-scope::-webkit-scrollbar-thumb:hover,
.void-scope *::-webkit-scrollbar-thumb:hover {
background-color: var(--void-bg-1) !important;
filter: brightness(1.1) !important;
}
.void-scope::-webkit-scrollbar-thumb:active,
.void-scope *::-webkit-scrollbar-thumb:active {
background-color: var(--void-bg-1) !important;
filter: brightness(1.2) !important;
}
.void-scope::-webkit-scrollbar-track,
.void-scope *::-webkit-scrollbar-track {
background-color: var(--void-bg-3) !important;
border: none !important;
}
.void-scope::-webkit-scrollbar-corner,
.void-scope *::-webkit-scrollbar-corner {
background-color: var(--void-bg-3) !important;
}
/* Add void-scrollable-element styles to match */
.void-scrollable-element {
background-color: var(--vscode-editor-background);
--scrollbar-vertical-width: 14px;
--scrollbar-horizontal-height: 6px;
overflow: auto; /* Ensure scrollbars are shown when needed */
}
.void-scrollable-element,
.void-scrollable-element * {
scrollbar-width: thin !important; /* For Firefox */
scrollbar-color: var(--void-bg-1) var(--void-bg-3) !important; /* For Firefox */
}
.void-scrollable-element::-webkit-scrollbar,
.void-scrollable-element *::-webkit-scrollbar {
width: 14px !important;
height: 14px !important;
}
.void-scrollable-element::-webkit-scrollbar-track,
.void-scrollable-element *::-webkit-scrollbar-track {
background: transparent !important;
width: var(--scrollbar-vertical-width) !important;
height: var(--scrollbar-horizontal-height) !important;
background-color: var(--void-bg-3) !important;
}
.void-scrollable-element::-webkit-scrollbar-thumb,
.void-scrollable-element *::-webkit-scrollbar-thumb {
background-color: transparent !important;
border-radius: 0px !important;
background-color: var(--void-bg-1) !important;
border-radius: 4px !important;
border: none !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.void-scrollable-element::-webkit-scrollbar-thumb:hover,
.void-scrollable-element *::-webkit-scrollbar-thumb:hover {
background-color: var(--vscode-scrollbarSlider-hoverBackground) !important;
background-color: var(--void-bg-1) !important;
filter: brightness(1.1) !important;
}
.void-scrollable-element::-webkit-scrollbar-thumb:active,
.void-scrollable-element *::-webkit-scrollbar-thumb:active {
background-color: var(--vscode-scrollbarSlider-activeBackground) !important;
background-color: var(--void-bg-1) !important;
filter: brightness(1.2) !important;
}
.void-scrollable-element::-webkit-scrollbar-track,
.void-scrollable-element *::-webkit-scrollbar-track {
background-color: var(--void-bg-3) !important;
border: none !important;
}
.void-scrollable-element::-webkit-scrollbar-corner,
.void-scrollable-element *::-webkit-scrollbar-corner {
background-color: transparent !important;
}
.void-scrollable-element.show-scrollbar-0::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-0 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 0%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-1::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-1 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 10%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-2::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-2 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 20%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-3::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-3 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 30%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-4::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-4 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 40%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-5::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-5 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 50%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-6::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-6 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 60%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-7::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-7 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 70%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-8::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-8 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 80%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-9::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-9 *::-webkit-scrollbar-thumb {
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 90%, transparent) !important;
}
.void-scrollable-element.show-scrollbar-10::-webkit-scrollbar-thumb,
.void-scrollable-element.show-scrollbar-10 *::-webkit-scrollbar-thumb {
background-color: var(--vscode-scrollbarSlider-background) !important;
background-color: var(--void-bg-3) !important;
}
@@ -0,0 +1,56 @@
/*--------------------------------------------------------------------------------------
* 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 { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
import * as dom from '../../../../base/browser/dom.js';
import { IMetricsService } from '../common/metricsService.js';
export interface IMetricsPollService {
readonly _serviceBrand: undefined;
}
const PING_EVERY_MS = 15 * 1000 * 60 // 15 minutes
export const IMetricsPollService = createDecorator<IMetricsPollService>('voidMetricsPollService');
class MetricsPollService extends Disposable implements IMetricsPollService {
_serviceBrand: undefined;
static readonly ID = 'voidMetricsPollService';
private readonly intervalID: number
constructor(
@IMetricsService private readonly metricsService: IMetricsService,
) {
super()
// initial state
const { window } = dom.getActiveWindow()
let i = 1
this.intervalID = window.setInterval(() => {
this.metricsService.capture('Alive', { iv1: i })
i += 1
}, PING_EVERY_MS)
}
override dispose() {
super.dispose()
const { window } = dom.getActiveWindow()
window.clearInterval(this.intervalID)
}
}
registerWorkbenchContribution2(MetricsPollService.ID, MetricsPollService, WorkbenchPhase.BlockRestore);
@@ -1,525 +0,0 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { URI } from '../../../../../base/common/uri.js';
import { filenameToVscodeLanguage } from '../helpers/detectLanguage.js';
import { CodeSelection, StagingSelectionItem, FileSelection } from '../../common/chatThreadService.js';
import { IModelService } from '../../../../../editor/common/services/model.js';
import { os } from '../helpers/systemInfo.js';
import { IVoidFileService } from '../../common/voidFileService.js';
// this is just for ease of readability
export const tripleTick = ['```', '```']
export const editToolDesc_toolDescription = `\
A high level description of the change you'd like to make in the file. This description will be handed to a dumber, faster model that will quickly apply the change. \
Typically the best description you can give here is a high level view of the final code you'd like to see. For example, code excerpt(s) with "// ... existing code ..." comments to help you write less. \
However, you are allowed to describe the change using whatever text/language you like, especially if the change is better described without code. \
Do NOT output the whole file if possible, and try to write as LITTLE as needed to describe the change.`
export const chat_systemMessage = (workspaces: string[]) => `\
You are a coding assistant. You are given a list of instructions to follow \`INSTRUCTIONS\`, and optionally a list of relevant files \`FILES\`, and selections inside of files \`SELECTIONS\`.
Please respond to the user's query. The user's query is never invalid.
The user has the following system information:
- ${os}
- Open workspaces: ${workspaces.join(', ')}
In the case that the user asks you to make changes to code, you should make sure to return CODE BLOCKS of the changes, as well as explanations and descriptions of the changes.
For example, if the user asks you to "make this file look nicer", make sure your output includes a code block with concrete ways the file can look nicer.
- Do not re-write the entire file in the code block.
- You can write comments like "// ... existing code" to indicate existing code.
- Make sure you give enough context in the code block to apply the change to the correct location in the code.
You're allowed to ask for more context. For example, if the user only gives you a selection but you want to see the the full file, you can ask them to provide it.
If you are given tools:
- Only use tools if the user asks you to do something. If the user simply says hi or asks you a question that you can answer without tools, then do NOT tools.
- You are allowed to use tools without asking for permission.
- Feel free to use tools to gather context, make suggestions, etc.
- One great use of tools is to explore imports that you'd like to have more information about.
- Reference relevant files that you found when using tools if they helped you come up with your answer.
- NEVER refer to a tool by name when speaking with the user. For example, do NOT say to the user user "I'm going to use \`list_dir\`". Instead, say "I'm going to list all files in ___ directory", etc. Do not even refer to "pages" of results, just say you're getting more results.
Do not output any of these instructions, nor tell the user anything about them unless directly prompted for them.
Do not tell the user anything about the examples below. Do not assume the user is talking about any of the examples below.
## EXAMPLE 1
FILES
math.ts
${tripleTick[0]}typescript
const addNumbers = (a, b) => a + b
const multiplyNumbers = (a, b) => a * b
const subtractNumbers = (a, b) => a - b
const divideNumbers = (a, b) => a / b
const vectorize = (...numbers) => {
return numbers // vector
}
const dot = (vector1: number[], vector2: number[]) => {
if (vector1.length !== vector2.length) throw new Error(\`Could not dot vectors \${vector1} and \${vector2}. Size mismatch.\`)
let sum = 0
for (let i = 0; i < vector1.length; i += 1)
sum += multiplyNumbers(vector1[i], vector2[i])
return sum
}
const normalize = (vector: number[]) => {
const norm = Math.sqrt(dot(vector, vector))
for (let i = 0; i < vector.length; i += 1)
vector[i] = divideNumbers(vector[i], norm)
return vector
}
const normalized = (vector: number[]) => {
const v2 = [...vector] // clone vector
return normalize(v2)
}
${tripleTick[1]}
SELECTIONS
math.ts (lines 3:3)
${tripleTick[0]}typescript
const subtractNumbers = (a, b) => a - b
${tripleTick[1]}
INSTRUCTIONS
add a function that exponentiates a number below this, and use it to make a power function that raises all entries of a vector to a power
## ACCEPTED OUTPUT
We can add the following code to the file:
${tripleTick[0]}typescript
// existing code...
const subtractNumbers = (a, b) => a - b
const exponentiateNumbers = (a, b) => Math.pow(a, b)
const divideNumbers = (a, b) => a / b
// existing code...
const raiseAll = (vector: number[], power: number) => {
for (let i = 0; i < vector.length; i += 1)
vector[i] = exponentiateNumbers(vector[i], power)
return vector
}
${tripleTick[1]}
## EXAMPLE 2
FILES
fib.ts
${tripleTick[0]}typescript
const dfs = (root) => {
if (!root) return;
console.log(root.val);
dfs(root.left);
dfs(root.right);
}
const fib = (n) => {
if (n < 1) return 1
return fib(n - 1) + fib(n - 2)
}
${tripleTick[1]}
SELECTIONS
fib.ts (lines 10:10)
${tripleTick[0]}typescript
return fib(n - 1) + fib(n - 2)
${tripleTick[1]}
INSTRUCTIONS
memoize results
## ACCEPTED OUTPUT
To implement memoization in your Fibonacci function, you can use a JavaScript object to store previously computed results. This will help avoid redundant calculations and improve performance. Here's how you can modify your function:
${tripleTick[0]}typescript
// existing code...
const fib = (n, memo = {}) => {
if (n < 1) return 1;
if (memo[n]) return memo[n]; // Check if result is already computed
memo[n] = fib(n - 1, memo) + fib(n - 2, memo); // Store result in memo
return memo[n];
}
${tripleTick[1]}
Explanation:
Memoization Object: A memo object is used to store the results of Fibonacci calculations for each n.
Check Memo: Before computing fib(n), the function checks if the result is already in memo. If it is, it returns the stored result.
Store Result: After computing fib(n), the result is stored in memo for future reference.
## END EXAMPLES\
`
type FileSelnLocal = { fileURI: URI, content: string }
const stringifyFileSelection = ({ fileURI, content }: FileSelnLocal) => {
return `\
${fileURI.fsPath}
${tripleTick[0]}${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
${content}
${tripleTick[1]}
`
}
const stringifyCodeSelection = ({ fileURI, selectionStr, range }: CodeSelection) => {
return `\
${fileURI.fsPath} (lines ${range.startLineNumber}:${range.endLineNumber})
${tripleTick[0]}${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
${selectionStr}
${tripleTick[1]}
`
}
const failToReadStr = 'Could not read content. This file may have been deleted. If you expected content here, you can tell the user about this as they might not know.'
const stringifyFileSelections = async (fileSelections: FileSelection[], voidFileService: IVoidFileService) => {
if (fileSelections.length === 0) return null
const fileSlns: FileSelnLocal[] = await Promise.all(fileSelections.map(async (sel) => {
const content = await voidFileService.readFile(sel.fileURI) ?? failToReadStr
return { ...sel, content }
}))
return fileSlns.map(sel => stringifyFileSelection(sel)).join('\n')
}
const stringifyCodeSelections = (codeSelections: CodeSelection[]) => {
return codeSelections.map(sel => stringifyCodeSelection(sel)).join('\n') || null
}
const stringifySelectionNames = (currSelns: StagingSelectionItem[] | null): string => {
if (!currSelns) return ''
return currSelns.map(s => `${s.fileURI.fsPath}${s.range ? ` (lines ${s.range.startLineNumber}:${s.range.endLineNumber})` : ''}`).join('\n')
}
export const chat_userMessageContent = async (instructions: string, currSelns: StagingSelectionItem[] | null) => {
const selnsStr = stringifySelectionNames(currSelns)
let str = ''
if (selnsStr) { str += `SELECTIONS\n${selnsStr}\n` }
str += `\nINSTRUCTIONS\n${instructions}`
return str;
};
export const chat_selectionsString = async (prevSelns: StagingSelectionItem[] | null, currSelns: StagingSelectionItem[] | null, voidFileService: IVoidFileService) => {
// ADD IN FILES AT TOP
const allSelections = [...currSelns || [], ...prevSelns || []]
if (allSelections.length === 0) return null
const codeSelections: CodeSelection[] = []
const fileSelections: FileSelection[] = []
const filesURIs = new Set<string>()
for (const selection of allSelections) {
if (selection.type === 'Selection') {
codeSelections.push(selection)
}
else if (selection.type === 'File') {
const fileSelection = selection
const path = fileSelection.fileURI.fsPath
if (!filesURIs.has(path)) {
filesURIs.add(path)
fileSelections.push(fileSelection)
}
}
}
const filesStr = await stringifyFileSelections(fileSelections, voidFileService)
const selnsStr = stringifyCodeSelections(codeSelections)
if (filesStr || selnsStr) return `\
ALL FILE CONTENTS
${filesStr}
${selnsStr}`
return null
}
export const chat_userMessageContentWithAllFilesToo = (userMessage: string, selectionsString: string | null) => {
if (userMessage) return `${userMessage}${selectionsString ? `\n${selectionsString}` : ''}`
else return userMessage
}
export const rewriteCode_systemMessage = `\
You are a coding assistant that re-writes an entire file to make a change. You are given the original file \`ORIGINAL_FILE\` and a change \`CHANGE\`.
Directions:
1. Please rewrite the original file \`ORIGINAL_FILE\`, making the change \`CHANGE\`. You must completely re-write the whole file.
2. Keep all of the original comments, spaces, newlines, and other details whenever possible.
3. ONLY output the full new file. Do not add any other explanations or text.
`
export const rewriteCode_userMessage = ({ originalCode, applyStr, uri }: { originalCode: string, applyStr: string, uri: URI }) => {
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
return `\
ORIGINAL_FILE
${tripleTick[0]}${language}
${originalCode}
${tripleTick[1]}
CHANGE
${tripleTick[0]}
${applyStr}
${tripleTick[1]}
INSTRUCTIONS
Please finish writing the new file by applying the change to the original file. Return ONLY the completion of the file, without any explanation.
`
}
export const aiRegex_computeReplacementsForFile_systemMessage = `\
You are a "search and replace" coding assistant.
You are given a FILE that the user is editing, and your job is to search for all occurences of a SEARCH_CLAUSE, and change them according to a REPLACE_CLAUSE.
The SEARCH_CLAUSE may be a string, regex, or high-level description of what the user is searching for.
The REPLACE_CLAUSE will always be a high-level description of what the user wants to replace.
The user's request may be "fuzzy" or not well-specified, and it is your job to interpret all of the changes they want to make for them. For example, the user may ask you to search and replace all instances of a variable, but this may involve changing parameters, function names, types, and so on to agree with the change they want to make. Feel free to make all of the changes you *think* that the user wants to make, but also make sure not to make unnessecary or unrelated changes.
## Instructions
1. If you do not want to make any changes, you should respond with the word "no".
2. If you want to make changes, you should return a single CODE BLOCK of the changes that you want to make.
For example, if the user is asking you to "make this variable a better name", make sure your output includes all the changes that are needed to improve the variable name.
- Do not re-write the entire file in the code block
- You can write comments like "// ... existing code" to indicate existing code
- Make sure you give enough context in the code block to apply the changes to the correct location in the code`
export const aiRegex_computeReplacementsForFile_userMessage = async ({ searchClause, replaceClause, fileURI, voidFileService }: { searchClause: string, replaceClause: string, fileURI: URI, modelService: IModelService, voidFileService: IVoidFileService }) => {
// we may want to do this in batches
const fileSelection: FileSelection = { type: 'File', fileURI, selectionStr: null, range: null, state: { isOpened: false } }
const file = await stringifyFileSelections([fileSelection], voidFileService)
return `\
## FILE
${file}
## SEARCH_CLAUSE
Here is what the user is searching for:
${searchClause}
## REPLACE_CLAUSE
Here is what the user wants to replace it with:
${replaceClause}
## INSTRUCTIONS
Please return the changes you want to make to the file in a codeblock, or return "no" if you do not want to make changes.`
}
// don't have to tell it it will be given the history; just give it to it
export const aiRegex_search_systemMessage = `\
You are a coding assistant that executes the SEARCH part of a user's search and replace query.
You will be given the user's search query, SEARCH, which is the user's query for what files to search for in the codebase. You may also be given the user's REPLACE query for additional context.
Output
- Regex query
- Files to Include (optional)
- Files to Exclude? (optional)
`
export const ORIGINAL = `<<<<<<< ORIGINAL`
export const DIVIDER = `=======`
export const FINAL = `>>>>>>> UPDATED`
export const searchReplace_systemMessage = `\
You are a coding assistant that generates SEARCH/REPLACE code blocks that will be used to edit a file.
A SEARCH/REPLACE block describes the code before and after a change. Here is the format:
${tripleTick[0]}
${ORIGINAL}
// ... original code goes here
${DIVIDER}
// ... final code goes here
${FINAL}
${tripleTick[1]}
You will be given the original file \`ORIGINAL_FILE\` and a description of a change \`CHANGE\` to make.
Output SEARCH/REPLACE blocks to edit the file according to the desired change. You may output multiple SEARCH/REPLACE blocks.
Directions:
1. Your OUTPUT should consist ONLY of SEARCH/REPLACE blocks. Do NOT output any text or explanations before or after this.
2. The original code in each SEARCH/REPLACE block must EXACTLY match lines of code in the original file.
3. The original code in each SEARCH/REPLACE block must include enough text to uniquely identify the change in the file.
4. The original code in each SEARCH/REPLACE block must be disjoint from all other blocks.
The SEARCH/REPLACE blocks you generate will be applied immediately, and so they **MUST** produce a file that the user can run IMMEDIATELY.
- Make sure you add all necessary imports.
- Make sure the "final" code is complete and will not result in syntax/lint errors.
Follow coding conventions of the user (spaces, semilcolons, comments, etc). If the user spaces or formats things a certain way, CONTINUE formatting it that way, even if you prefer otherwise.
## EXAMPLE 1
ORIGINAL_FILE
${tripleTick[0]}
let w = 5
let x = 6
let y = 7
let z = 8
${tripleTick[1]}
CHANGE
Make x equal to 6.5, not 6.
${tripleTick[0]}
// ... existing code
let x = 6.5
// ... existing code
${tripleTick[1]}
## ACCEPTED OUTPUT
${tripleTick[0]}
${ORIGINAL}
let x = 6
${DIVIDER}
let x = 6.5
${FINAL}
${tripleTick[1]}
`
export const searchReplace_userMessage = ({ originalCode, applyStr }: { originalCode: string, applyStr: string }) => `\
ORIGINAL_FILE
${originalCode}
CHANGE
${applyStr}
INSTRUCTIONS
Please output SEARCH/REPLACE blocks to make the change. Return ONLY your suggested SEARCH/REPLACE blocks, without any explanation.
`
export const voidPrefixAndSuffix = ({ fullFileStr, startLine, endLine }: { fullFileStr: string, startLine: number, endLine: number }) => {
const fullFileLines = fullFileStr.split('\n')
// we can optimize this later
const MAX_PREFIX_SUFFIX_CHARS = 20_000
/*
a
a
a <-- final i (prefix = a\na\n)
a
|b <-- startLine-1 (middle = b\nc\nd\n) <-- initial i (moves up)
c
d| <-- endLine-1 <-- initial j (moves down)
e
e <-- final j (suffix = e\ne\n)
e
e
*/
let prefix = ''
let i = startLine - 1 // 0-indexed exclusive
// we'll include fullFileLines[i...(startLine-1)-1].join('\n') in the prefix.
while (i !== 0) {
const newLine = fullFileLines[i - 1]
if (newLine.length + 1 + prefix.length <= MAX_PREFIX_SUFFIX_CHARS) { // +1 to include the \n
prefix = `${newLine}\n${prefix}`
i -= 1
}
else break
}
let suffix = ''
let j = endLine - 1
while (j !== fullFileLines.length - 1) {
const newLine = fullFileLines[j + 1]
if (newLine.length + 1 + suffix.length <= MAX_PREFIX_SUFFIX_CHARS) { // +1 to include the \n
suffix = `${suffix}\n${newLine}`
j += 1
}
else break
}
return { prefix, suffix }
}
export type QuickEditFimTagsType = {
preTag: string,
sufTag: string,
midTag: string
}
export const defaultQuickEditFimTags: QuickEditFimTagsType = {
preTag: 'ABOVE',
sufTag: 'BELOW',
midTag: 'SELECTION',
}
// this should probably be longer
export const ctrlKStream_systemMessage = ({ quickEditFIMTags: { preTag, midTag, sufTag } }: { quickEditFIMTags: QuickEditFimTagsType }) => {
return `\
You are a FIM (fill-in-the-middle) coding assistant. Your task is to fill in the middle SELECTION marked by <${midTag}> tags.
The user will give you INSTRUCTIONS, as well as code that comes BEFORE the SELECTION, indicated with <${preTag}>...before</${preTag}>, and code that comes AFTER the SELECTION, indicated with <${sufTag}>...after</${sufTag}>.
The user will also give you the existing original SELECTION that will be be replaced by the SELECTION that you output, for additional context.
Instructions:
1. Your OUTPUT should be a SINGLE PIECE OF CODE of the form <${midTag}>...new_code</${midTag}>. Do NOT output any text or explanations before or after this.
2. You may ONLY CHANGE the original SELECTION, and NOT the content in the <${preTag}>...</${preTag}> or <${sufTag}>...</${sufTag}> tags.
3. Make sure all brackets in the new selection are balanced the same as in the original selection.
4. Be careful not to duplicate or remove variables, comments, or other syntax by mistake.
`
}
export const ctrlKStream_userMessage = ({ selection, prefix, suffix, instructions, fimTags, isOllamaFIM, language }: {
selection: string, prefix: string, suffix: string, instructions: string, fimTags: QuickEditFimTagsType, language: string,
isOllamaFIM: false, // we require this be false for clarity
}) => {
const { preTag, sufTag, midTag } = fimTags
// prompt the model artifically on how to do FIM
// const preTag = 'BEFORE'
// const sufTag = 'AFTER'
// const midTag = 'SELECTION'
return `\
CURRENT SELECTION
${tripleTick[0]}${language}
<${midTag}>${selection}</${midTag}>
${tripleTick[1]}
INSTRUCTIONS
${instructions}
<${preTag}>${prefix}</${preTag}>
<${sufTag}>${suffix}</${sufTag}>
Return only the completion block of code (of the form ${tripleTick[0]}${language}
<${midTag}>...new code</${midTag}>
${tripleTick[1]}).`
};
@@ -8,7 +8,7 @@ import { Action2, registerAction2 } from '../../../../platform/actions/common/ac
import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
import { IEditCodeService } from './editCodeService.js';
import { IEditCodeService } from './editCodeServiceInterface.js';
import { roundRangeToLines } from './sidebarActions.js';
import { VOID_CTRL_K_ACTION_ID } from './actionIDs.js';
import { localize2 } from '../../../../nls.js';
@@ -1,77 +0,0 @@
/*--------------------------------------------------------------------------------------
* 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';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { QuickEdit } from './quickEditActions.js';
// service that manages state
export type VoidQuickEditState = {
quickEditsOfDocument: { [uri: string]: QuickEdit }
}
export interface IQuickEditStateService {
readonly _serviceBrand: undefined;
readonly state: VoidQuickEditState; // readonly to the user
setState(newState: Partial<VoidQuickEditState>): void;
onDidChangeState: Event<void>;
onDidFocusChat: Event<void>;
onDidBlurChat: Event<void>;
fireFocusChat(): void;
fireBlurChat(): void;
}
export const IQuickEditStateService = createDecorator<IQuickEditStateService>('voidQuickEditStateService');
class VoidQuickEditStateService extends Disposable implements IQuickEditStateService {
_serviceBrand: undefined;
static readonly ID = 'voidQuickEditStateService';
private readonly _onDidChangeState = new Emitter<void>();
readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
private readonly _onFocusChat = new Emitter<void>();
readonly onDidFocusChat: Event<void> = this._onFocusChat.event;
private readonly _onBlurChat = new Emitter<void>();
readonly onDidBlurChat: Event<void> = this._onBlurChat.event;
// state
state: VoidQuickEditState
constructor(
) {
super()
// initial state
this.state = { quickEditsOfDocument: {} }
}
setState(newState: Partial<VoidQuickEditState>) {
this.state = { ...this.state, ...newState }
this._onDidChangeState.fire()
}
fireFocusChat() {
this._onFocusChat.fire()
}
fireBlurChat() {
this._onBlurChat.fire()
}
}
registerSingleton(IQuickEditStateService, VoidQuickEditStateService, InstantiationType.Eager);
@@ -74,7 +74,7 @@ function saveStylesFile() {
} catch (err) {
console.error('[scope-tailwind] Error saving styles.css:', err);
}
}, 3000);
}, 6000);
}
const args = process.argv.slice(2);
@@ -1,9 +1,17 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { useState, useEffect, useCallback } from 'react'
import { useAccessor, useURIStreamState, useSettingsState } from '../util/services.js'
import { useRefState } from '../util/helpers.js'
import { useAccessor, useCommandBarState, useCommandBarURIListener, useSettingsState } from '../util/services.js'
import { usePromise, useRefState } from '../util/helpers.js'
import { isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js'
import { URI } from '../../../../../../../base/common/uri.js'
import { IEditCodeService, URIStreamState } from '../../../editCodeService.js'
import { FileSymlink, LucideIcon, RotateCw, Terminal } from 'lucide-react'
import { Check, X, Square, Copy, Play, } from 'lucide-react'
import { getBasename, ListableToolItem, ToolChildrenWrapper } from '../sidebar-tsx/SidebarChat.js'
import { PlacesType, VariantType } from 'react-tooltip'
enum CopyButtonText {
Idle = 'Copy',
@@ -11,9 +19,57 @@ enum CopyButtonText {
Error = 'Could not copy',
}
const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
const CopyButton = ({ codeStr }: { codeStr: string }) => {
type IconButtonProps = {
Icon: LucideIcon
}
export const IconShell1 = ({ onClick, Icon, disabled, className, ...props }: IconButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>) => (
<button
disabled={disabled}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onClick?.(e);
}}
// border border-void-border-1 rounded
className={`
size-[18px]
p-[2px]
flex items-center justify-center
text-sm bg-void-bg-3 text-void-fg-3
hover:brightness-110
disabled:opacity-50 disabled:cursor-not-allowed
${className}
`}
{...props}
>
<Icon />
</button>
)
// export const IconShell2 = ({ onClick, title, Icon, disabled, className }: IconButtonProps) => (
// <button
// title={title}
// disabled={disabled}
// onClick={onClick}
// className={`
// size-[24px]
// flex items-center justify-center
// text-sm
// hover:opacity-80
// disabled:opacity-50 disabled:cursor-not-allowed
// ${className}
// `}
// >
// <Icon size={16} />
// </button>
// )
const COPY_FEEDBACK_TIMEOUT = 1500 // amount of time to say 'Copied!'
export const CopyButton = ({ codeStr }: { codeStr: string }) => {
const accessor = useAccessor()
const metricsService = accessor.get('IMetricsService')
@@ -27,7 +83,6 @@ const CopyButton = ({ codeStr }: { codeStr: string }) => {
}, COPY_FEEDBACK_TIMEOUT)
}, [copyButtonText])
const onCopy = useCallback(() => {
clipboardService.writeText(codeStr)
.then(() => { setCopyButtonText(CopyButtonText.Copied) })
@@ -35,123 +90,299 @@ const CopyButton = ({ codeStr }: { codeStr: string }) => {
metricsService.capture('Copy Code', { length: codeStr.length }) // capture the length only
}, [metricsService, clipboardService, codeStr, setCopyButtonText])
const isSingleLine = !codeStr.includes('\n')
return <button
className={`${isSingleLine ? '' : 'px-1 py-0.5'} text-sm bg-void-bg-1 text-void-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
return <IconShell1
Icon={copyButtonText === CopyButtonText.Copied ? Check : copyButtonText === CopyButtonText.Error ? X : Copy}
onClick={onCopy}
>
{copyButtonText}
</button>
{...tooltipPropsForApplyBlock({ tooltipName: 'Copy' })}
/>
}
export const JumpToFileButton = ({ uri, ...props }: { uri: URI | 'current' } & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
const accessor = useAccessor()
const commandService = accessor.get('ICommandService')
const jumpToFileButton = uri !== 'current' && (
<IconShell1
Icon={FileSymlink}
onClick={() => {
commandService.executeCommand('vscode.open', uri, { preview: true })
}}
{...tooltipPropsForApplyBlock({ tooltipName: 'Go to file' })}
{...props}
/>
)
return jumpToFileButton
}
export const JumpToTerminalButton = ({ onClick }: { onClick: () => void }) => {
return (
<IconShell1
Icon={Terminal}
onClick={onClick}
/>
)
}
// state persisted for duration of react only
// TODO change this to use type `ChatThreads.applyBoxState[applyBoxId]`
const applyingURIOfApplyBoxIdRef: { current: { [applyBoxId: string]: URI | undefined } } = { current: {} }
const getUriBeingApplied = (applyBoxId: string) => {
return applyingURIOfApplyBoxIdRef.current[applyBoxId] ?? null
}
export const ApplyBlockHoverButtons = ({ codeStr, applyBoxId }: { codeStr: string, applyBoxId: string }) => {
console.log('applyboxid', applyBoxId, applyingURIOfApplyBoxIdRef)
export const useApplyButtonState = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI | 'current' }) => {
const settingsState = useSettingsState()
const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId
const accessor = useAccessor()
const editCodeService = accessor.get('IEditCodeService')
const metricsService = accessor.get('IMetricsService')
const voidCommandBarService = accessor.get('IVoidCommandBarService')
const [_, rerender] = useState(0)
const applyingUri = useCallback(() => applyingURIOfApplyBoxIdRef.current[applyBoxId] ?? null, [applyBoxId])
const streamState = useCallback(() => editCodeService.getURIStreamState({ uri: applyingUri() }), [editCodeService, applyingUri])
const getStreamState = useCallback(() => {
const uri = getUriBeingApplied(applyBoxId)
if (!uri) return 'idle-no-changes'
return voidCommandBarService.getStreamState(uri)
}, [voidCommandBarService, applyBoxId])
// listen for stream updates
useURIStreamState(
useCallback((uri, newStreamState) => {
const shouldUpdate = applyingUri()?.fsPath !== uri.fsPath
if (shouldUpdate) return
// listen for stream updates on this box
useCommandBarURIListener(useCallback((uri_) => {
const shouldUpdate = (
getUriBeingApplied(applyBoxId)?.fsPath === uri_.fsPath
|| (uri !== 'current' && uri.fsPath === uri_.fsPath)
)
if (shouldUpdate) {
rerender(c => c + 1)
}, [applyBoxId, editCodeService, applyingUri])
}
}, [applyBoxId, uri]))
const currStreamState = getStreamState()
return {
getStreamState,
isDisabled,
currStreamState,
}
}
type IndicatorColor = 'green' | 'orange' | 'dark' | 'yellow' | null
export const StatusIndicator = ({ indicatorColor, title, className, ...props }: { indicatorColor: IndicatorColor, title?: React.ReactNode, className?: string } & React.HTMLAttributes<HTMLDivElement>) => {
return (
<div className={`flex flex-row text-void-fg-3 text-xs items-center gap-1.5 ${className}`} {...props}>
{title && <span className='opacity-80'>{title}</span>}
<div
className={` size-1.5 rounded-full border
${indicatorColor === 'dark' ? 'bg-void-bg-3 border-void-border-1' :
indicatorColor === 'orange' ? 'bg-orange-500 border-orange-500 shadow-[0_0_4px_0px_rgba(234,88,12,0.6)]' :
indicatorColor === 'green' ? 'bg-green-500 border-green-500 shadow-[0_0_4px_0px_rgba(22,163,74,0.6)]' :
indicatorColor === 'yellow' ? 'bg-yellow-500 border-yellow-500 shadow-[0_0_4px_0px_rgba(22,163,74,0.6)]' :
'bg-void-border-1 border-void-border-1'
}
`}
/>
</div>
);
};
const tooltipPropsForApplyBlock = ({ tooltipName, color = undefined, position = 'top', offset = undefined }: { tooltipName: string, color?: IndicatorColor, position?: PlacesType, offset?: number }) => ({
'data-tooltip-id': color === 'orange' ? `void-tooltip-orange` : color === 'green' ? 'void-tooltip-green' : 'void-tooltip',
'data-tooltip-place': position as PlacesType,
'data-tooltip-content': `${tooltipName}`,
'data-tooltip-offset': offset,
})
export const StatusIndicatorForApplyButton = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI | 'current' } & React.HTMLAttributes<HTMLDivElement>) => {
const { currStreamState } = useApplyButtonState({ applyBoxId, uri })
const color = (
currStreamState === 'idle-no-changes' ? 'dark' :
currStreamState === 'streaming' ? 'orange' :
currStreamState === 'idle-has-changes' ? 'green' :
null
)
const onSubmit = useCallback(() => {
const tooltipName = (
currStreamState === 'idle-no-changes' ? 'Done' :
currStreamState === 'streaming' ? 'Applying' :
currStreamState === 'idle-has-changes' ? 'Done' : // also 'Done'? 'Applied' looked bad
''
)
const statusIndicatorHTML = <StatusIndicator
key={currStreamState}
className='mx-2'
indicatorColor={color}
{...tooltipPropsForApplyBlock({ tooltipName, color, position: 'top', offset: 12 })}
/>
return statusIndicatorHTML
}
export const ApplyButtonsHTML = ({ codeStr, applyBoxId, reapplyIcon, uri }: { codeStr: string, applyBoxId: string, reapplyIcon: boolean, uri: URI | 'current' }) => {
const accessor = useAccessor()
const editCodeService = accessor.get('IEditCodeService')
const metricsService = accessor.get('IMetricsService')
const {
currStreamState,
isDisabled,
getStreamState,
} = useApplyButtonState({ applyBoxId, uri })
const onClickSubmit = useCallback(async () => {
if (isDisabled) return
if (streamState() === 'streaming') return
const newApplyingUri = editCodeService.startApplying({
if (getStreamState() === 'streaming') return
const opts = {
from: 'ClickApply',
type: 'searchReplace',
applyStr: codeStr,
uri: 'current',
uri: uri,
startBehavior: 'reject-conflicts',
} as const
await editCodeService.callBeforeStartApplying(opts)
const [newApplyingUri, applyDonePromise] = editCodeService.startApplying(opts) ?? []
// catch any errors by interrupting the stream
applyDonePromise?.catch(e => {
const uri = getUriBeingApplied(applyBoxId)
if (uri) editCodeService.interruptURIStreaming({ uri: uri })
})
applyingURIOfApplyBoxIdRef.current[applyBoxId] = newApplyingUri ?? undefined
rerender(c => c + 1)
// rerender(c => c + 1)
metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only
}, [isDisabled, streamState, editCodeService, codeStr, applyBoxId, metricsService])
}, [isDisabled, getStreamState, editCodeService, codeStr, uri, applyBoxId, metricsService])
const onInterrupt = useCallback(() => {
if (streamState() !== 'streaming') return
const uri = applyingUri()
if (getStreamState() !== 'streaming') return
const uri = getUriBeingApplied(applyBoxId)
if (!uri) return
editCodeService.interruptURIStreaming({ uri })
metricsService.capture('Stop Apply', {})
}, [streamState, applyingUri, editCodeService, metricsService])
}, [getStreamState, applyBoxId, editCodeService, metricsService])
const onAccept = useCallback(() => {
const uri = getUriBeingApplied(applyBoxId)
if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri, behavior: 'accept', removeCtrlKs: false })
}, [applyBoxId, editCodeService])
const onReject = useCallback(() => {
const uri = getUriBeingApplied(applyBoxId)
if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri, behavior: 'reject', removeCtrlKs: false })
}, [applyBoxId, editCodeService])
// const onReapply = useCallback(() => {
// onReject()
// onClickSubmit()
// }, [onReject, onClickSubmit])
const isSingleLine = !codeStr.includes('\n')
if (currStreamState === 'streaming') {
return <IconShell1
const applyButton = <button
// btn btn-secondary btn-sm border text-sm border-vscode-input-border rounded
className={`${isSingleLine ? '' : 'px-1 py-0.5'} text-sm bg-void-bg-1 text-void-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
onClick={onSubmit}
>
Apply
</button>
Icon={Square}
onClick={onInterrupt}
const stopButton = <button
// btn btn-secondary btn-sm border text-sm border-vscode-input-border rounded
className={`${isSingleLine ? '' : 'px-1 py-0.5'} text-sm bg-void-bg-1 text-void-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
onClick={onInterrupt}
>
Stop
</button>
{...tooltipPropsForApplyBlock({ tooltipName: 'Stop' })}
/>
}
const acceptRejectButtons = <>
<button
// btn btn-secondary btn-sm border text-sm border-vscode-input-border rounded
className={`${isSingleLine ? '' : 'px-1 py-0.5'} text-sm bg-void-bg-1 text-void-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
onClick={() => {
const uri = applyingUri()
if (uri) editCodeService.removeDiffAreas({ uri, behavior: 'accept', removeCtrlKs: false })
}}
>
Accept
</button>
<button
// btn btn-secondary btn-sm border text-sm border-vscode-input-border rounded
className={`${isSingleLine ? '' : 'px-1 py-0.5'} text-sm bg-void-bg-1 text-void-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
onClick={() => {
const uri = applyingUri()
if (uri) editCodeService.removeDiffAreas({ uri, behavior: 'reject', removeCtrlKs: false })
}}
>
Reject
</button>
</>
if (currStreamState === 'idle-no-changes') {
console.log('streamStateRef.current', streamState())
return <IconShell1
Icon={reapplyIcon ? RotateCw : Play}
onClick={onClickSubmit}
{...tooltipPropsForApplyBlock({ tooltipName: reapplyIcon ? 'Reapply' : 'Apply' })}
/>
}
if (currStreamState === 'idle-has-changes') {
return <>
{/* <IconShell1
Icon={RotateCw}
onClick={onReapply}
/> */}
<IconShell1
Icon={X}
onClick={onReject}
{...tooltipPropsForApplyBlock({ tooltipName: 'Remove' })}
/>
<IconShell1
Icon={Check}
onClick={onAccept}
{...tooltipPropsForApplyBlock({ tooltipName: 'Keep' })}
/>
</>
}
}
export const BlockCodeApplyWrapper = ({
children,
initValue,
applyBoxId,
language,
canApply,
uri,
}: {
initValue: string;
children: React.ReactNode;
applyBoxId: string;
canApply: boolean;
language: string;
uri: URI | 'current',
}) => {
const accessor = useAccessor()
const commandService = accessor.get('ICommandService')
const { currStreamState } = useApplyButtonState({ applyBoxId, uri })
const name = uri !== 'current' ?
<ListableToolItem
name={<span className='not-italic'>{getBasename(uri.fsPath)}</span>}
isSmall={true}
showDot={false}
onClick={() => { commandService.executeCommand('vscode.open', uri, { preview: true }) }}
/>
: <span>{language}</span>
return <div className='border border-void-border-3 rounded overflow-hidden bg-void-bg-3 my-1'>
{/* header */}
<div className=" select-none flex justify-between items-center py-1 px-2 border-b border-void-border-3 cursor-default">
<div className="flex items-center">
<StatusIndicatorForApplyButton uri={uri} applyBoxId={applyBoxId} />
<span className="text-[13px] font-light text-void-fg-3">
{name}
</span>
</div>
<div className={`${canApply ? '' : 'hidden'} flex items-center gap-1`}>
<JumpToFileButton uri={uri} />
{currStreamState === 'idle-no-changes' && <CopyButton codeStr={initValue} />}
<ApplyButtonsHTML uri={uri} applyBoxId={applyBoxId} codeStr={initValue} reapplyIcon={false} />
</div>
</div>
{/* contents */}
<ToolChildrenWrapper>
{children}
</ToolChildrenWrapper>
</div>
const currStreamState = streamState()
return <>
{currStreamState !== 'streaming' && <CopyButton codeStr={codeStr} />}
{currStreamState === 'idle' && !isDisabled && applyButton}
{currStreamState === 'streaming' && stopButton}
{currStreamState === 'acceptRejectAll' && acceptRejectButtons}
</>
}
@@ -1,29 +0,0 @@
/*--------------------------------------------------------------------------------------
* 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 { VoidCodeEditor, VoidCodeEditorProps } from '../util/inputs.js';
export const BlockCode = ({ buttonsOnHover, ...codeEditorProps }: { buttonsOnHover?: React.ReactNode } & VoidCodeEditorProps) => {
const isSingleLine = !codeEditorProps.initValue.includes('\n')
return (
<>
<div className="relative group w-full overflow-hidden">
{buttonsOnHover === null ? null : (
<div className={`z-[1] absolute top-0 right-0 opacity-0 group-hover:opacity-100 duration-200 ${isSingleLine ? 'h-full flex items-center' : ''}`}>
<div className={`flex space-x-1 ${isSingleLine ? 'pr-2' : 'p-2'}`}>
{buttonsOnHover}
</div>
</div>
)}
<VoidCodeEditor {...codeEditorProps} />
</div>
</>
)
}
@@ -3,92 +3,199 @@
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import React, { JSX } from 'react'
import React, { JSX, useMemo, useState } from 'react'
import { marked, MarkedToken, Token } from 'marked'
import { BlockCode } from './BlockCode.js'
import { nameToVscodeLanguage } from '../../../helpers/detectLanguage.js'
import { ApplyBlockHoverButtons } from './ApplyBlockHoverButtons.js'
import { convertToVscodeLang, detectLanguage } from '../../../../common/helpers/languageHelpers.js'
import { BlockCodeApplyWrapper } from './ApplyBlockHoverButtons.js'
import { useAccessor } from '../util/services.js'
import { ScrollType } from '../../../../../../../editor/common/editorCommon.js'
import { URI } from '../../../../../../../base/common/uri.js'
import { isAbsolute } from '../../../../../../../base/common/path.js'
import { separateOutFirstLine } from '../../../../common/helpers/util.js'
import { BlockCode } from '../util/inputs.js'
export type ChatMessageLocation = {
threadId: string;
messageIdx: number;
}
type ApplyBoxLocation = ChatMessageLocation & { tokenIdx: string }
const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocation) => {
export const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocation) => {
return `${threadId}-${messageIdx}-${tokenIdx}`
}
export const CodeSpan = ({ children, className }: { children: React.ReactNode, className?: string }) => {
return <code className={`
bg-void-bg-1
px-1
rounded-sm
font-mono font-medium
break-all
${className}
`}
>
{children}
</code>
function isValidUri(s: string): boolean {
return s.length > 5 && isAbsolute(s) && !s.includes('//') && !s.includes('/*') // common case that is a false positive is comments like //
}
const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, tokenIdx }: { token: Token | string, nested?: boolean, noSpace?: boolean, chatMessageLocationForApply?: ChatMessageLocation, tokenIdx: string }): JSX.Element => {
const Codespan = ({ text, className, onClick }: { text: string, className?: string, onClick?: () => void }) => {
// TODO compute this once for efficiency. we should use `labels.ts/shorten` to display duplicates properly
return <code
className={`font-mono font-medium rounded-sm bg-void-bg-1 px-1 ${className}`}
onClick={onClick}
>
{text}
</code>
}
const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string, rawText: string, chatMessageLocation: ChatMessageLocation }) => {
const accessor = useAccessor()
const chatThreadService = accessor.get('IChatThreadService')
const commandService = accessor.get('ICommandService')
const editorService = accessor.get('ICodeEditorService')
const { messageIdx, threadId } = chatMessageLocation
const [didComputeCodespanLink, setDidComputeCodespanLink] = useState<boolean>(false)
let link = undefined
if (rawText.endsWith('`')) { // if codespan was completed
// get link from cache
link = chatThreadService.getCodespanLink({ codespanStr: text, messageIdx, threadId })
if (link === undefined) {
// if no link, generate link and add to cache
(chatThreadService.generateCodespanLink({ codespanStr: text, threadId })
.then(link => {
chatThreadService.addCodespanLink({ newLinkText: text, newLinkLocation: link, messageIdx, threadId })
setDidComputeCodespanLink(true) // rerender
})
)
}
}
const onClick = () => {
if (!link) return;
const selection = link.selection
// open the file
commandService.executeCommand('vscode.open', link.uri).then(() => {
// select the text
setTimeout(() => {
if (!selection) return;
const editor = editorService.getActiveCodeEditor()
if (!editor) return;
editor.setSelection(selection)
editor.revealRange(selection, ScrollType.Immediate)
}, 50) // needed when document was just opened and needs to initialize
})
}
return <Codespan
// text={link?.displayText || text}
text={link?.displayText || text}
onClick={onClick}
className={link ? 'underline hover:brightness-90 transition-all duration-200 cursor-pointer' : ''}
/>
}
export type RenderTokenOptions = { isApplyEnabled?: boolean, isLinkDetectionEnabled?: boolean }
const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, ...options }: { token: Token | string, inPTag?: boolean, codeURI?: URI, chatMessageLocation?: ChatMessageLocation, tokenIdx: string, } & RenderTokenOptions): React.ReactNode => {
const accessor = useAccessor()
const languageService = accessor.get('ILanguageService')
// deal with built-in tokens first (assume marked token)
const t = token as MarkedToken
if (t.type === "space") {
if (t.raw.trim() === '') {
return null;
}
if (t.type === 'space') {
return <span>{t.raw}</span>
}
if (t.type === "code") {
if (t.type === 'code') {
const [firstLine, remainingContents] = separateOutFirstLine(t.text)
const firstLineIsURI = isValidUri(firstLine) && !codeURI
const contents = firstLineIsURI ? (remainingContents?.trimStart() || '') : t.text // exclude first-line URI from contents
const applyBoxId = chatMessageLocationForApply ? getApplyBoxId({
threadId: chatMessageLocationForApply.threadId,
messageIdx: chatMessageLocationForApply.messageIdx,
tokenIdx: tokenIdx,
}) : null
if (!contents) return null
return <div className='my-4'>
<BlockCode
initValue={t.text}
language={t.lang === undefined ? undefined : nameToVscodeLanguage[t.lang]}
buttonsOnHover={applyBoxId && <ApplyBlockHoverButtons applyBoxId={applyBoxId} codeStr={t.text} />}
/>
</div>
}
if (t.type === "heading") {
const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements
const headingClasses: { [h: string]: string } = {
h1: "text-4xl font-semibold mt-6 mb-4 pb-2 border-b border-void-bg-2",
h2: "text-3xl font-semibold mt-6 mb-4 pb-2 border-b border-void-bg-2",
h3: "text-2xl font-semibold mt-6 mb-4",
h4: "text-xl font-semibold mt-6 mb-4",
h5: "text-lg font-semibold mt-6 mb-4",
h6: "text-base font-semibold mt-6 mb-4 text-gray-600"
// figure out langauge and URI
let uri: URI | null
let language: string
if (codeURI) {
uri = codeURI
}
return <HeadingTag className={headingClasses[HeadingTag]}>{t.text}</HeadingTag>
else if (firstLineIsURI) { // get lang from the uri in the first line of the markdown
uri = URI.file(firstLine)
}
else {
uri = null
}
if (t.lang) { // a language was provided. empty string is common so check truthy, not just undefined
language = convertToVscodeLang(languageService, t.lang) // convert markdown language to language that vscode recognizes (eg markdown doesn't know bash but it does know shell)
}
else { // no language provided - fallback - get lang from the uri and contents
language = detectLanguage(languageService, { uri, fileContents: contents })
}
if (options.isApplyEnabled && chatMessageLocation) {
const isCodeblockClosed = t.raw.trimEnd().endsWith('```') // user should only be able to Apply when the code has been closed (t.raw ends with '```')
const applyBoxId = getApplyBoxId({
threadId: chatMessageLocation.threadId,
messageIdx: chatMessageLocation.messageIdx,
tokenIdx: tokenIdx,
})
return <BlockCodeApplyWrapper
canApply={isCodeblockClosed}
applyBoxId={applyBoxId}
initValue={contents}
language={language}
uri={uri || 'current'}
>
<BlockCode
initValue={contents}
language={language}
/>
</BlockCodeApplyWrapper>
}
return <BlockCode
initValue={contents}
language={language}
/>
}
if (t.type === "table") {
if (t.type === 'heading') {
const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements
return <HeadingTag>
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={t.text} inPTag={true} codeURI={codeURI} {...options} />
</HeadingTag>
}
if (t.type === 'table') {
return (
<div className={`${noSpace ? '' : 'my-4'} overflow-x-auto`}>
<table className="min-w-full border border-void-bg-2">
<div>
<table>
<thead>
<tr className="bg-void-bg-1">
<tr>
{t.header.map((cell: any, index: number) => (
<th
key={index}
className="px-4 py-2 border border-void-bg-2 font-semibold"
style={{ textAlign: t.align[index] || "left" }}
>
<th key={index}>
{cell.raw}
</th>
))}
@@ -96,13 +203,9 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
</thead>
<tbody>
{t.rows.map((row: any[], rowIndex: number) => (
<tr key={rowIndex} className={rowIndex % 2 === 0 ? 'bg-white' : 'bg-void-bg-1'}>
<tr key={rowIndex}>
{row.map((cell: any, cellIndex: number) => (
<td
key={cellIndex}
className="px-4 py-2 border border-void-bg-2"
style={{ textAlign: t.align[cellIndex] || "left" }}
>
<td key={cellIndex} >
{cell.raw}
</td>
))}
@@ -112,147 +215,171 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocationForApply, toke
</table>
</div>
)
// return (
// <div>
// <table className={'min-w-full border border-void-bg-2'}>
// <thead>
// <tr className='bg-void-bg-1'>
// {t.header.map((cell: any, index: number) => (
// <th
// key={index}
// className='px-4 py-2 border border-void-bg-2 font-semibold'
// style={{ textAlign: t.align[index] || 'left' }}
// >
// {cell.raw}
// </th>
// ))}
// </tr>
// </thead>
// <tbody>
// {t.rows.map((row: any[], rowIndex: number) => (
// <tr key={rowIndex} className={rowIndex % 2 === 0 ? 'bg-white' : 'bg-void-bg-1'}>
// {row.map((cell: any, cellIndex: number) => (
// <td
// key={cellIndex}
// className={'px-4 py-2 border border-void-bg-2'}
// style={{ textAlign: t.align[cellIndex] || 'left' }}
// >
// {cell.raw}
// </td>
// ))}
// </tr>
// ))}
// </tbody>
// </table>
// </div>
// )
}
if (t.type === "hr") {
return <hr className="my-6 border-t border-void-bg-2" />
if (t.type === 'hr') {
return <hr />
}
if (t.type === "blockquote") {
return <blockquote className={`pl-4 border-l-4 border-void-bg-2 italic ${noSpace ? '' : 'my-4'}`}>{t.text}</blockquote>
if (t.type === 'blockquote') {
return <blockquote>{t.text}</blockquote>
}
if (t.type === "list") {
const ListTag = t.ordered ? "ol" : "ul"
if (t.type === 'list_item') {
return <li>
<input type='checkbox' checked={t.checked} readOnly />
<span>
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={t.text} inPTag={true} codeURI={codeURI} {...options} />
</span>
</li>
}
if (t.type === 'list') {
const ListTag = t.ordered ? 'ol' : 'ul'
return (
<ListTag
start={t.start ? t.start : undefined}
className={`list-inside pl-2 ${noSpace ? '' : 'my-4'} ${t.ordered ? "list-decimal" : "list-disc"}`}
>
<ListTag start={t.start ? t.start : undefined}>
{t.items.map((item, index) => (
<li key={index} className={`${noSpace ? '' : 'mb-4'}`}>
<li key={index}>
{item.task && (
<input type="checkbox" checked={item.checked} readOnly className="mr-2 form-checkbox" />
<input type='checkbox' checked={item.checked} readOnly />
)}
<span className="ml-1">
<ChatMarkdownRender chatMessageLocationForApply={chatMessageLocationForApply} string={item.text} nested={true} />
<span>
<ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={item.text} inPTag={true} {...options} />
</span>
</li>
))}
</ListTag>
)
// attempt at indentation
// return (
// <ListTag
// start={t.start ? t.start : undefined}
// className={`pl-2 ${noSpace ? '' : 'my-4'} ${t.ordered ? "list-decimal" : "list-disc"}`}
// >
// {t.items.map((item, index) => (
// <li key={index} className={`${noSpace ? '' : 'mb-2'} ml-4`}>
// {item.task && (
// <input type="checkbox" className='mr-2 form-checkbox' checked={item.checked} readOnly />
// )}
// <span className-='inline-block pr-2'>
// <ChatMarkdownRender chatMessageLocation={chatMessageLocation} string={item.text} nested={true} />
// </span>
// </li>
// ))}
// </ListTag>
// )
}
if (t.type === "paragraph") {
if (t.type === 'paragraph') {
const contents = <>
{t.tokens.map((token, index) => (
<RenderToken key={index} token={token} tokenIdx={`${tokenIdx ? `${tokenIdx}-` : ''}${index}`} /> // assign a unique tokenId to nested components
<RenderToken key={index}
token={token}
tokenIdx={`${tokenIdx ? `${tokenIdx}-` : ''}${index}`} // assign a unique tokenId to inPTag components
chatMessageLocation={chatMessageLocation}
inPTag={true}
{...options}
/>
))}
</>
if (nested) return contents
return <p className={`${noSpace ? '' : 'my-4'}`}>
{contents}
</p>
if (inPTag) return <span className='block'>{contents}</span>
return <p>{contents}</p>
}
if (t.type === "html") {
return (
<p className={`${noSpace ? '' : 'my-4'}`}>
{t.raw}
</p>
)
}
if (t.type === "text" || t.type === "escape") {
if (t.type === 'text' || t.type === 'escape' || t.type === 'html') {
return <span>{t.raw}</span>
}
if (t.type === "def") {
if (t.type === 'def') {
return <></> // Definitions are typically not rendered
}
if (t.type === "link") {
if (t.type === 'link') {
return (
<a
className='underline'
onClick={() => { window.open(t.href) }}
href={t.href}
title={t.title ?? undefined}
className='underline cursor-pointer hover:brightness-90 transition-all duration-200 text-void-fg-2'
>
{t.text}
</a>
)
}
if (t.type === "image") {
if (t.type === 'image') {
return <img
src={t.href}
alt={t.text}
title={t.title ?? undefined}
className={`max4w-full h-auto rounded ${noSpace ? '' : 'my-4'}`}
/>
}
if (t.type === "strong") {
return <strong className="font-semibold">{t.text}</strong>
if (t.type === 'strong') {
return <strong>{t.text}</strong>
}
if (t.type === "em") {
return <em className="italic">{t.text}</em>
if (t.type === 'em') {
return <em>{t.text}</em>
}
// inline code
if (t.type === "codespan") {
return (
<CodeSpan>
{t.text}
</CodeSpan>
)
if (t.type === 'codespan') {
if (options.isLinkDetectionEnabled && chatMessageLocation) {
return <CodespanWithLink
text={t.text}
rawText={t.raw}
chatMessageLocation={chatMessageLocation}
/>
}
return <Codespan text={t.text} />
}
if (t.type === "br") {
if (t.type === 'br') {
return <br />
}
// strikethrough
if (t.type === "del") {
return <del className="line-through">{t.text}</del>
if (t.type === 'del') {
return <del>{t.text}</del>
}
// default
return (
<div className="bg-orange-50 rounded-sm overflow-hidden p-2">
<span className="text-sm text-orange-500">Unknown type:</span>
{t.raw}
<div className='bg-orange-50 rounded-sm overflow-hidden p-2'>
<span className='text-sm text-orange-500'>Unknown token rendered...</span>
</div>
)
}
export const ChatMarkdownRender = ({ string, nested = false, noSpace, chatMessageLocationForApply }: { string: string, nested?: boolean, noSpace?: boolean, chatMessageLocationForApply?: ChatMessageLocation }) => {
export const ChatMarkdownRender = ({ string, inPTag = false, chatMessageLocation, ...options }: { string: string, inPTag?: boolean, codeURI?: URI, chatMessageLocation: ChatMessageLocation | undefined } & RenderTokenOptions) => {
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
return (
<>
{tokens.map((token, index) => (
<RenderToken key={index} token={token} nested={nested} noSpace={noSpace} chatMessageLocationForApply={chatMessageLocationForApply} tokenIdx={index + ''} />
<RenderToken key={index} token={token} inPTag={inPTag} chatMessageLocation={chatMessageLocation} tokenIdx={index + ''} {...options} />
))}
</>
)
@@ -3,15 +3,13 @@
* 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, useChatThreadsState, useQuickEditState, useAccessor, useCtrlKZoneStreamingState } from '../util/services.js';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useSettingsState, useAccessor, useCtrlKZoneStreamingState } from '../util/services.js';
import { TextAreaFns, VoidInputBox2 } from '../util/inputs.js';
import { QuickEditPropsType } from '../../../quickEditActions.js';
import { ButtonStop, ButtonSubmit, IconX, VoidChatArea } from '../sidebar-tsx/SidebarChat.js';
import { ModelDropdown } from '../void-settings-tsx/ModelDropdown.js';
import { VOID_CTRL_K_ACTION_ID } from '../../../actionIDs.js';
import { useRefState } from '../util/helpers.js';
import { useScrollbarStyles } from '../util/useScrollbarStyles.js';
import { isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
export const QuickEditChat = ({
@@ -55,17 +53,27 @@ export const QuickEditChat = ({
setIsStreamingRef(isStreaming)
}, [diffareaid, setIsStreamingRef]))
const loadingIcon = <div
className="@@codicon @@codicon-loading @@codicon-modifier-spin @@codicon-no-default-spin text-void-fg-3"
/>
const onSubmit = useCallback(() => {
const onSubmit = useCallback(async () => {
if (isDisabled) return
if (isStreamingRef.current) return
textAreaFnsRef.current?.disable()
editCodeService.startApplying({
const opts = {
from: 'QuickEdit',
type: 'rewrite',
diffareaid,
})
startBehavior: 'keep-conflicts',
} as const
await editCodeService.callBeforeStartApplying(opts)
const [newApplyingUri, applyDonePromise] = editCodeService.startApplying(opts) ?? []
// catch any errors by interrupting the stream
applyDonePromise?.catch(e => { if (newApplyingUri) editCodeService.interruptCtrlKStreaming({ diffareaid }) })
}, [isStreamingRef, isDisabled, editCodeService, diffareaid])
const onInterrupt = useCallback(() => {
@@ -80,21 +88,19 @@ export const QuickEditChat = ({
editCodeService.removeCtrlKZone({ diffareaid })
}, [editCodeService, diffareaid])
useScrollbarStyles(sizerRef)
const keybindingString = accessor.get('IKeybindingService').lookupKeybinding(VOID_CTRL_K_ACTION_ID)?.getLabel()
const chatAreaRef = useRef<HTMLDivElement | null>(null)
return <div ref={sizerRef} style={{ maxWidth: 450 }} className={`py-2 w-full`}>
<VoidChatArea
featureName='Ctrl+K'
divRef={chatAreaRef}
onSubmit={onSubmit}
onAbort={onInterrupt}
onClose={onX}
isStreaming={isStreamingRef.current}
loadingIcon={loadingIcon}
isDisabled={isDisabled}
featureName="Ctrl+K"
className="py-2 w-full"
onClickAnywhere={() => { textAreaRef.current?.focus() }}
>
<VoidInputBox2
@@ -5,8 +5,8 @@
import React, { useEffect, useState } from 'react';
import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react';
import { errorDetails } from '../../../../../../../workbench/contrib/void/common/llmMessageTypes.js';
import { useSettingsState } from '../util/services.js';
import { errorDetails } from '../../../../common/sendLLMMessageTypes.js';
export const ErrorDisplay = ({

Some files were not shown because too many files have changed in this diff Show More