mirror of
https://github.com/hanzoai/code.git
synced 2026-09-30 20:53:35 +00:00
one contribution, one directory — finish the upstream merge
The rebrand renamed platform/void and contrib/void/browser to code; upstream then
moved platform/void into contrib/void/{common,electron-main}. Git could not see
those as the same move, so the merge kept both sides and the contribution came out
split across three directories — browser/ under contrib/code, common/ and
electron-main/ under contrib/void, a pre-move copy under platform/code. Every
../common/x.js import out of browser/ dangled. Shared-file conflicts were settled
by replacing upstream's lines instead of keeping both, so extHost.protocol lost
MainThreadChatStatus and ExtHostMcp and telemetryService lost the body of _log.
The fork's only change to src/ is that rename, so the tree it should have is
upstream's with the rename applied. This is that tree. What upstream deleted goes
with it: inlineDiffsService and the InlineDiff extension host API.
buildreact joins the gate because react/out is generated rather than committed and
the workbench imports its mount functions from there — the order HOW_TO_CONTRIBUTE
already gives. compile alone reported 281 errors; ci, buildreact and compile
together finish with 0.
Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
+3
-3
@@ -22,9 +22,9 @@ product.overrides.json
|
||||
*.snap.actual
|
||||
.vscode-test
|
||||
|
||||
# Void added these:
|
||||
# Code added these:
|
||||
.tmp/
|
||||
.tmp2/
|
||||
.tool-versions
|
||||
src/vs/workbench/contrib/void/browser/react/out/**
|
||||
src/vs/workbench/contrib/void/browser/react/src2/**
|
||||
src/vs/workbench/contrib/code/browser/react/out/**
|
||||
src/vs/workbench/contrib/code/browser/react/src2/**
|
||||
|
||||
+2
-2
@@ -149,8 +149,8 @@ Edit: far too many changes to track... this is old
|
||||
|
||||
- src/vs/workbench/api/common/{extHost.api.impl.ts | extHostApiCommands.ts}
|
||||
- src/vs/workbench/workbench.common.main.ts
|
||||
- src/vs/workbench/contrib/void/\*
|
||||
- extensions/void/\*
|
||||
- src/vs/workbench/contrib/code/\*
|
||||
- extensions/code/\*
|
||||
|
||||
- .github/\*
|
||||
- .vscode/settings/\*
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
This folder is no longer relevant, since Code is no longer just an extension.
|
||||
|
||||
See the new Code code in `src/vs/workbench/contrib/void` (and a few other locations - you can just search "Code").
|
||||
See the new Code code in `src/vs/workbench/contrib/code` (and a few other locations - you can just search "Code").
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# This fork has had no build gate at all — .github/workflows holds only
|
||||
# triage.yml. `npm ci && npm run compile` is the repo's real build (gulp over
|
||||
# the whole workbench plus every bundled extension), so it is what runs.
|
||||
# triage.yml. The repo's real build is `npm ci && npm run buildreact && npm run
|
||||
# compile`, the order HOW_TO_CONTRIBUTE.md gives, so it is what runs.
|
||||
#
|
||||
# buildreact is load-bearing, not a convenience: it bundles the sidebar, settings
|
||||
# and quick-edit React roots into src/vs/workbench/contrib/code/browser/react/out,
|
||||
# which is generated rather than committed. The workbench imports its mount
|
||||
# functions from there, so without it compile cannot resolve them.
|
||||
#
|
||||
# The apt line is not scaffolding: on Linux the postinstall step compiles
|
||||
# native-keymap, node-pty and keytar, which need the X11/kerberos/libsecret
|
||||
@@ -9,4 +14,4 @@
|
||||
# reusable runs gates under `bash -c`, with no set -e of its own.
|
||||
test:
|
||||
- name: build
|
||||
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends build-essential g++ python3 libx11-dev libxkbfile-dev libsecret-1-dev libkrb5-dev && npm ci && npm run compile
|
||||
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends build-essential g++ python3 libx11-dev libxkbfile-dev libsecret-1-dev libkrb5-dev && npm ci && npm run buildreact && npm run compile
|
||||
|
||||
+2
-7
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
// "jsx": "react", // <-- Code added this
|
||||
"esModuleInterop": true,
|
||||
"removeComments": false,
|
||||
"preserveConstEnums": true,
|
||||
@@ -30,11 +29,7 @@
|
||||
"./*.ts",
|
||||
"./typings",
|
||||
"./vs/**/*.ts",
|
||||
"vscode-dts/vscode.proposed.*.d.ts",
|
||||
"vscode-dts/vscode.d.ts",
|
||||
|
||||
// Code added these:
|
||||
// "./vs/workbench/contrib/void/browser/**.tsx",
|
||||
// "./vs/**/*.d.mts",
|
||||
"./vscode-dts/vscode.proposed.*.d.ts",
|
||||
"./vscode-dts/vscode.d.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
/* custom speed & easing for loading icon */
|
||||
.codicon-loading:not(.codicon-no-default-spin), /* Void changed this as it is literally broken to the !important */
|
||||
.codicon-loading:not(.codicon-no-default-spin), /* Code 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;
|
||||
|
||||
@@ -57,8 +57,8 @@ export type ExtensionVirtualWorkspaceSupport = {
|
||||
|
||||
export interface IProductConfiguration {
|
||||
readonly version: string;
|
||||
readonly voidVersion?: string;
|
||||
readonly release?: string; // Void - VSCodium added this, we add it for TS
|
||||
readonly codeVersion?: string;
|
||||
readonly release?: string; // Code - VSCodium added this, we add it for TS
|
||||
readonly date?: string;
|
||||
readonly quality?: string;
|
||||
readonly commit?: string;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25959,7 +25959,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/lines-around-comment.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-find.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-non-null-assertion.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-invalid-code-type.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-invalid-void-type.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/class-methods-use-this.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/space-infix-ops.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/ban-tslint-comment.js
|
||||
@@ -25980,7 +25980,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-return-this-type.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-promises.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unused-expressions.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-invalid-code-type.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-invalid-void-type.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-type-alias.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-boolean-expressions.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/type-annotation-spacing.js
|
||||
@@ -26057,7 +26057,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unsafe-unary-minus.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-for-of.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-empty-interface.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-meaningless-code-operator.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-meaningless-void-operator.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/explicit-function-return-type.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/comma-dangle.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/index.js
|
||||
@@ -26068,7 +26068,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unsafe-argument.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-extra-parens.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-import-type-side-effects.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-meaningless-code-operator.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-meaningless-void-operator.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/method-signature-style.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unused-vars.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-array-delete.js
|
||||
@@ -26167,7 +26167,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-inferrable-types.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-useless-template-literals.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unused-expressions.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-code-expression.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-void-expression.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-ts-expect-error.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unsafe-member-access.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-namespace-keyword.js
|
||||
@@ -26242,7 +26242,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/ban-ts-comment.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-regexp-exec.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-non-null-asserted-optional-chain.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-code-expression.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-void-expression.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-for-in-array.js.map
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/require-await.js
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-dupe-class-members.js
|
||||
@@ -26277,7 +26277,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-unnecessary-type-arguments.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/ban-ts-comment.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-non-null-asserted-optional-chain.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-confusing-code-expression.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-confusing-void-expression.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/require-await.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-dupe-class-members.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/ban-types.md
|
||||
@@ -26341,7 +26341,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-unused-vars.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-array-delete.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/README.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-meaningless-code-operator.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-meaningless-void-operator.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-non-null-asserted-nullish-coalescing.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/prefer-for-of.md
|
||||
@@ -26376,7 +26376,7 @@
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/strict-boolean-expressions.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/type-annotation-spacing.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/typedef.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-invalid-code-type.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-invalid-void-type.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-type-alias.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-unnecessary-type-constraint.md
|
||||
/Users/example/node_modules/@typescript-eslint/eslint-plugin/docs/rules/no-implied-eval.md
|
||||
@@ -64786,7 +64786,7 @@
|
||||
/Users/example/extensions/notebook-renderers/node_modules/whatwg-url/lib/utils.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/whatwg-url/lib/url-state-machine.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/whatwg-url/lib/URL.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/whatwg-url/lib/CodeFunction.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/whatwg-url/lib/VoidFunction.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/whatwg-url/LICENSE.txt
|
||||
/Users/example/extensions/notebook-renderers/node_modules/domexception/index.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/domexception/webidl2js-wrapper.js
|
||||
@@ -64797,7 +64797,7 @@
|
||||
/Users/example/extensions/notebook-renderers/node_modules/domexception/lib/utils.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/domexception/lib/DOMException-impl.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/domexception/lib/DOMException.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/domexception/lib/CodeFunction.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/domexception/lib/VoidFunction.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/domexception/LICENSE.txt
|
||||
/Users/example/extensions/notebook-renderers/node_modules/combined-stream/License
|
||||
/Users/example/extensions/notebook-renderers/node_modules/combined-stream/Readme.md
|
||||
@@ -65079,7 +65079,7 @@
|
||||
/Users/example/extensions/notebook-renderers/node_modules/jsdom/lib/jsdom/living/generated/Text.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/jsdom/lib/jsdom/living/generated/HTMLTableElement.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/jsdom/lib/jsdom/living/generated/HTMLMenuElement.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/jsdom/lib/jsdom/living/generated/CodeFunction.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/jsdom/lib/jsdom/living/generated/VoidFunction.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/jsdom/lib/jsdom/living/generated/PopStateEvent.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/jsdom/lib/jsdom/living/generated/Document.js
|
||||
/Users/example/extensions/notebook-renderers/node_modules/jsdom/lib/jsdom/living/generated/FormData.js
|
||||
@@ -73339,7 +73339,7 @@
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/vscode-languageserver/node.cmd
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/LICENSE
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/CHANGELOG.md
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/code-tag.js
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/void-tag.js
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/parse.d.ts
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/nodes/html.js
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/nodes/type.js
|
||||
@@ -73351,7 +73351,7 @@
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/nodes/text.d.ts
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/nodes/node.d.ts
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/nodes/text.js
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/code-tag.d.ts
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/void-tag.d.ts
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/index.js
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/valid.d.ts
|
||||
/Users/example/extensions/markdown-language-features/server/node_modules/node-html-parser/dist/valid.js
|
||||
|
||||
@@ -123,11 +123,16 @@ import { IWebContentExtractorService } from '../../platform/webContentExtractor/
|
||||
import { NativeWebContentExtractorService } from '../../platform/webContentExtractor/electron-main/webContentExtractorService.js';
|
||||
import ErrorTelemetry from '../../platform/telemetry/electron-main/errorTelemetry.js';
|
||||
|
||||
import { LLMMessageChannel } from '../../platform/void/electron-main/llmMessageChannel.js';
|
||||
import { IMetricsService } from '../../platform/void/common/metricsService.js';
|
||||
import { MetricsMainService } from '../../platform/void/electron-main/metricsMainService.js';
|
||||
import { CodeMainUpdateService } from '../../platform/void/electron-main/codeUpdateMainService.js';
|
||||
import { ICodeUpdateService } from '../../platform/void/common/codeUpdateService.js';
|
||||
// in theory this is not allowed
|
||||
// ignore the eslint errors below
|
||||
import { IMetricsService } from '../../workbench/contrib/code/common/metricsService.js';
|
||||
import { ICodeUpdateService } from '../../workbench/contrib/code/common/codeUpdateService.js';
|
||||
import { MetricsMainService } from '../../workbench/contrib/code/electron-main/metricsMainService.js';
|
||||
import { CodeMainUpdateService } from '../../workbench/contrib/code/electron-main/codeUpdateMainService.js';
|
||||
import { LLMMessageChannel } from '../../workbench/contrib/code/electron-main/sendLLMMessageChannel.js';
|
||||
import { CodeSCMService } from '../../workbench/contrib/code/electron-main/codeSCMMainService.js';
|
||||
import { ICodeSCMService } from '../../workbench/contrib/code/common/codeSCMTypes.js';
|
||||
import { MCPChannel } from '../../workbench/contrib/code/electron-main/mcpChannel.js';
|
||||
/**
|
||||
* The main VS Code application. There will only ever be one instance,
|
||||
* even if the user starts many instances (e.g. from the command line).
|
||||
@@ -522,35 +527,6 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
|
||||
//#endregion
|
||||
|
||||
// //#region Code IPC
|
||||
// validatedIpcMain.handle('vscode:sendLLMMessage', async (event, data) => {
|
||||
// try {
|
||||
// await this.sendLLMMessage(data);
|
||||
// } catch (error) {
|
||||
// console.error('Error sending LLM message:', error);
|
||||
// }
|
||||
// });
|
||||
// //#endregion
|
||||
}
|
||||
|
||||
private onUnexpectedError(error: Error): void {
|
||||
if (error) {
|
||||
|
||||
// take only the message and stack property
|
||||
const friendlyError = {
|
||||
message: `[uncaught exception in main]: ${error.message}`,
|
||||
stack: error.stack
|
||||
};
|
||||
|
||||
// handle on client side
|
||||
this.windowsMainService?.sendToFocused('vscode:reportError', JSON.stringify(friendlyError));
|
||||
}
|
||||
|
||||
this.logService.error(`[uncaught exception in main]: ${error}`);
|
||||
if (error.stack) {
|
||||
this.logService.error(error.stack);
|
||||
}
|
||||
}
|
||||
|
||||
async startup(): Promise<void> {
|
||||
@@ -1128,6 +1104,7 @@ export class CodeApplication extends Disposable {
|
||||
// Code main process services (required for services with a channel for comm between browser and electron-main (node))
|
||||
services.set(IMetricsService, new SyncDescriptor(MetricsMainService, undefined, false));
|
||||
services.set(ICodeUpdateService, new SyncDescriptor(CodeMainUpdateService, undefined, false));
|
||||
services.set(ICodeSCMService, new SyncDescriptor(CodeSCMService, undefined, false));
|
||||
|
||||
// Default Extensions Profile Init
|
||||
services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService, undefined, true));
|
||||
@@ -1266,8 +1243,16 @@ export class CodeApplication extends Disposable {
|
||||
const codeUpdatesChannel = ProxyChannel.fromService(accessor.get(ICodeUpdateService), disposables);
|
||||
mainProcessElectronServer.registerChannel('code-channel-update', codeUpdatesChannel);
|
||||
|
||||
const llmMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService));
|
||||
mainProcessElectronServer.registerChannel('code-channel-llmMessageService', llmMessageChannel);
|
||||
const sendLLMMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService));
|
||||
mainProcessElectronServer.registerChannel('code-channel-llmMessage', sendLLMMessageChannel);
|
||||
|
||||
// Code added this
|
||||
const codeSCMChannel = ProxyChannel.fromService(accessor.get(ICodeSCMService), disposables);
|
||||
mainProcessElectronServer.registerChannel('code-channel-scm', codeSCMChannel);
|
||||
|
||||
// Code added this
|
||||
const mcpChannel = new MCPChannel();
|
||||
mainProcessElectronServer.registerChannel('code-channel-mcp', mcpChannel);
|
||||
|
||||
// Extension Host Debug Broadcasting
|
||||
const electronExtensionHostDebugBroadcastChannel = new ElectronExtensionHostDebugBroadcastChannel(accessor.get(IWindowsMainService));
|
||||
|
||||
@@ -3570,7 +3570,7 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
|
||||
const defaults: InternalQuickSuggestionsOptions = {
|
||||
other: 'on',
|
||||
comments: 'off',
|
||||
strings: 'on' // Void changed this setting
|
||||
strings: 'on' // Code changed this setting
|
||||
};
|
||||
const types: IJSONSchema[] = [
|
||||
{ type: 'boolean' },
|
||||
|
||||
@@ -209,11 +209,6 @@ export class EditorWorker implements IDisposable, IWorkerTextModelSyncChannelSer
|
||||
private static readonly _diffLimit = 100000;
|
||||
|
||||
public async $computeMoreMinimalEdits(modelUrl: string, edits: TextEdit[], pretty: boolean): Promise<TextEdit[]> {
|
||||
return this.$Code_computeMoreMinimalEdits(modelUrl, edits, pretty)
|
||||
}
|
||||
|
||||
// Code added this as non async
|
||||
public $Code_computeMoreMinimalEdits(modelUrl: string, edits: TextEdit[], pretty: boolean): TextEdit[] {
|
||||
const model = this._getModel(modelUrl);
|
||||
if (!model) {
|
||||
return edits;
|
||||
|
||||
@@ -122,7 +122,7 @@ 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
|
||||
// Code changed this to skip over added whitespace when using smartSelect
|
||||
// // Store the original selections for comparison
|
||||
// const originalSelections = selections;
|
||||
|
||||
|
||||
@@ -418,7 +418,7 @@ suite('SnippetController2', function () {
|
||||
model.setValue('');
|
||||
editor.setSelection(new Selection(1, 1, 1, 1));
|
||||
|
||||
ctrl.insert('.REGION ${2:FUNCTION_NAME}\nCREATE.FUNCTION ${1:CODE} ${2:FUNCTION_NAME}(${3:})\n\t${4:}\nEND\n.ENDREGION$0');
|
||||
ctrl.insert('.REGION ${2:FUNCTION_NAME}\nCREATE.FUNCTION ${1:VOID} ${2:FUNCTION_NAME}(${3:})\n\t${4:}\nEND\n.ENDREGION$0');
|
||||
assertSelections(editor, new Selection(2, 17, 2, 21));
|
||||
|
||||
ctrl.next();
|
||||
@@ -438,7 +438,7 @@ suite('SnippetController2', function () {
|
||||
model.setValue('');
|
||||
editor.setSelection(new Selection(1, 1, 1, 1));
|
||||
|
||||
ctrl.insert('.REGION ${2:FUNCTION_NAME}\nCREATE.FUNCTION ${1:CODE} ${2:FUNCTION_NAME}(${3:})\n\t${4:}\nEND\n.ENDREGION$0');
|
||||
ctrl.insert('.REGION ${2:FUNCTION_NAME}\nCREATE.FUNCTION ${1:VOID} ${2:FUNCTION_NAME}(${3:})\n\t${4:}\nEND\n.ENDREGION$0');
|
||||
assertSelections(editor, new Selection(2, 17, 2, 21));
|
||||
|
||||
ctrl.next();
|
||||
|
||||
@@ -1,326 +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 { deepClone } from '../../../base/common/objects.js';
|
||||
import { IEncryptionService } from '../../encryption/common/encryptionService.js';
|
||||
import { registerSingleton, InstantiationType } from '../../instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js';
|
||||
import { IMetricsService } from './metricsService.js';
|
||||
import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, modelInfoOfDefaultNames, CodeModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings } from './codeSettingsTypes.js';
|
||||
|
||||
|
||||
const STORAGE_KEY = 'void.settingsServiceStorage'
|
||||
|
||||
type SetSettingOfProviderFn = <S extends SettingName>(
|
||||
providerName: ProviderName,
|
||||
settingName: S,
|
||||
newVal: SettingsOfProvider[ProviderName][S extends keyof SettingsOfProvider[ProviderName] ? S : never],
|
||||
) => Promise<void>;
|
||||
|
||||
type SetModelSelectionOfFeatureFn = <K extends FeatureName>(
|
||||
featureName: K,
|
||||
newVal: ModelSelectionOfFeature[K],
|
||||
options?: { doNotApplyEffects?: true }
|
||||
) => Promise<void>;
|
||||
|
||||
type SetGlobalSettingFn = <T extends GlobalSettingName, >(settingName: T, newVal: GlobalSettings[T]) => void;
|
||||
|
||||
export type ModelOption = { name: string, selection: ModelSelection }
|
||||
|
||||
|
||||
|
||||
export type CodeSettingsState = {
|
||||
readonly settingsOfProvider: SettingsOfProvider; // optionsOfProvider
|
||||
readonly modelSelectionOfFeature: ModelSelectionOfFeature; // stateOfFeature
|
||||
readonly globalSettings: GlobalSettings;
|
||||
|
||||
readonly _modelOptions: ModelOption[] // computed based on the two above items
|
||||
}
|
||||
|
||||
type RealCodeSettings = Exclude<keyof CodeSettingsState, '_modelOptions'>
|
||||
type EventProp<T extends RealCodeSettings = RealCodeSettings> = T extends 'globalSettings' ? [T, keyof CodeSettingsState[T]] : T | 'all'
|
||||
|
||||
|
||||
export interface ICodeSettingsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
readonly state: CodeSettingsState; // in order to play nicely with react, you should immutably change state
|
||||
readonly waitForInitState: Promise<void>;
|
||||
|
||||
onDidChangeState: Event<EventProp>;
|
||||
|
||||
setSettingOfProvider: SetSettingOfProviderFn;
|
||||
setModelSelectionOfFeature: SetModelSelectionOfFeatureFn;
|
||||
setGlobalSetting: SetGlobalSettingFn;
|
||||
|
||||
setAutodetectedModels(providerName: ProviderName, modelNames: string[], logging: object): void;
|
||||
toggleModelHidden(providerName: ProviderName, modelName: string): void;
|
||||
addModel(providerName: ProviderName, modelName: string): void;
|
||||
deleteModel(providerName: ProviderName, modelName: string): boolean;
|
||||
}
|
||||
|
||||
|
||||
let _computeModelOptions = (settingsOfProvider: SettingsOfProvider) => {
|
||||
let modelOptions: ModelOption[] = []
|
||||
for (const providerName of providerNames) {
|
||||
const providerConfig = settingsOfProvider[providerName]
|
||||
if (!providerConfig._enabled) continue // if disabled, don't display model options
|
||||
for (const { modelName, isHidden } of providerConfig.models) {
|
||||
if (isHidden) continue
|
||||
modelOptions.push({ name: `${modelName} (${providerName})`, selection: { providerName, modelName } })
|
||||
}
|
||||
}
|
||||
return modelOptions
|
||||
}
|
||||
|
||||
|
||||
const defaultState = () => {
|
||||
const d: CodeSettingsState = {
|
||||
settingsOfProvider: deepClone(defaultSettingsOfProvider),
|
||||
modelSelectionOfFeature: { 'Ctrl+L': null, 'Ctrl+K': null, 'Autocomplete': null, 'FastApply': null },
|
||||
globalSettings: deepClone(defaultGlobalSettings),
|
||||
_modelOptions: _computeModelOptions(defaultSettingsOfProvider), // computed
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
|
||||
export const ICodeSettingsService = createDecorator<ICodeSettingsService>('CodeSettingsService');
|
||||
class CodeSettingsService extends Disposable implements ICodeSettingsService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<EventProp>();
|
||||
readonly onDidChangeState: Event<EventProp> = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes
|
||||
|
||||
state: CodeSettingsState;
|
||||
waitForInitState: Promise<void> // await this if you need a valid state initially
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IEncryptionService private readonly _encryptionService: IEncryptionService,
|
||||
@IMetricsService private readonly _metricsService: IMetricsService,
|
||||
// could have used this, but it's clearer the way it is (+ slightly different eg StorageTarget.USER)
|
||||
// @ISecretStorageService private readonly _secretStorageService: ISecretStorageService,
|
||||
) {
|
||||
super()
|
||||
|
||||
// at the start, we haven't read the partial config yet, but we need to set state to something
|
||||
this.state = defaultState()
|
||||
|
||||
let resolver: () => void = () => { }
|
||||
this.waitForInitState = new Promise((res, rej) => resolver = res)
|
||||
|
||||
// read and update the actual state immediately
|
||||
this._readState().then(readS => {
|
||||
|
||||
// the stored data structure might be outdated, so we need to update it here (can do a more general solution later when we need to)
|
||||
readS = {
|
||||
...readS,
|
||||
settingsOfProvider: {
|
||||
// A HACK BECAUSE WE ADDED DEEPSEEK (did not exist before, comes before readS)
|
||||
...{ deepseek: defaultSettingsOfProvider.deepseek },
|
||||
|
||||
// A HACK BECAUSE WE ADDED MISTRAL (did not exist before, comes before readS)
|
||||
...{ mistral: defaultSettingsOfProvider.mistral },
|
||||
|
||||
...readS.settingsOfProvider,
|
||||
|
||||
// A HACK BECAUSE WE ADDED NEW GEMINI MODELS (existed before, comes after readS)
|
||||
gemini: {
|
||||
...readS.settingsOfProvider.gemini,
|
||||
models: [
|
||||
...readS.settingsOfProvider.gemini.models,
|
||||
...defaultSettingsOfProvider.gemini.models.filter(m => /* if cant find the model in readS (yes this is O(n^2), very small) */ !readS.settingsOfProvider.gemini.models.find(m2 => m2.modelName === m.modelName))
|
||||
]
|
||||
}
|
||||
},
|
||||
modelSelectionOfFeature: {
|
||||
// A HACK BECAUSE WE ADDED FastApply
|
||||
...{ 'FastApply': null },
|
||||
...readS.modelSelectionOfFeature,
|
||||
}
|
||||
}
|
||||
|
||||
this.state = readS
|
||||
resolver()
|
||||
this._onDidChangeState.fire('all')
|
||||
})
|
||||
}
|
||||
|
||||
private async _readState(): Promise<CodeSettingsState> {
|
||||
const encryptedState = this._storageService.get(STORAGE_KEY, StorageScope.APPLICATION)
|
||||
|
||||
if (!encryptedState)
|
||||
return defaultState()
|
||||
|
||||
const stateStr = await this._encryptionService.decrypt(encryptedState)
|
||||
return JSON.parse(stateStr)
|
||||
}
|
||||
|
||||
|
||||
private async _storeState() {
|
||||
const state = this.state
|
||||
const encryptedState = await this._encryptionService.encrypt(JSON.stringify(state))
|
||||
this._storageService.store(STORAGE_KEY, encryptedState, StorageScope.APPLICATION, StorageTarget.USER);
|
||||
}
|
||||
|
||||
setSettingOfProvider: SetSettingOfProviderFn = async (providerName, settingName, newVal) => {
|
||||
|
||||
const newModelSelectionOfFeature = this.state.modelSelectionOfFeature
|
||||
|
||||
const newSettingsOfProvider = {
|
||||
...this.state.settingsOfProvider,
|
||||
[providerName]: {
|
||||
...this.state.settingsOfProvider[providerName],
|
||||
[settingName]: newVal,
|
||||
}
|
||||
}
|
||||
|
||||
const newGlobalSettings = this.state.globalSettings
|
||||
|
||||
// if changed models or enabled a provider, recompute models list
|
||||
const modelsListChanged = settingName === 'models' || settingName === '_enabled'
|
||||
const newModelsList = modelsListChanged ? _computeModelOptions(newSettingsOfProvider) : this.state._modelOptions
|
||||
|
||||
const newState: CodeSettingsState = {
|
||||
modelSelectionOfFeature: newModelSelectionOfFeature,
|
||||
settingsOfProvider: newSettingsOfProvider,
|
||||
globalSettings: newGlobalSettings,
|
||||
_modelOptions: newModelsList,
|
||||
}
|
||||
|
||||
// this must go above this.setanythingelse()
|
||||
this.state = newState
|
||||
|
||||
// if the user-selected model is no longer in the list, update the selection for each feature that needs it to something relevant (the 0th model available, or null)
|
||||
if (modelsListChanged) {
|
||||
for (const featureName of featureNames) {
|
||||
|
||||
const currentSelection = newModelSelectionOfFeature[featureName]
|
||||
const selnIdx = currentSelection === null ? -1 : newModelsList.findIndex(m => modelSelectionsEqual(m.selection, currentSelection))
|
||||
|
||||
if (selnIdx === -1) {
|
||||
if (newModelsList.length !== 0)
|
||||
this.setModelSelectionOfFeature(featureName, newModelsList[0].selection, { doNotApplyEffects: true })
|
||||
else
|
||||
this.setModelSelectionOfFeature(featureName, null, { doNotApplyEffects: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this._storeState()
|
||||
this._onDidChangeState.fire('settingsOfProvider')
|
||||
}
|
||||
|
||||
|
||||
setGlobalSetting: SetGlobalSettingFn = async (settingName, newVal) => {
|
||||
const newState: CodeSettingsState = {
|
||||
...this.state,
|
||||
globalSettings: {
|
||||
...this.state.globalSettings,
|
||||
[settingName]: newVal
|
||||
}
|
||||
}
|
||||
this.state = newState
|
||||
await this._storeState()
|
||||
this._onDidChangeState.fire(['globalSettings', settingName])
|
||||
|
||||
}
|
||||
|
||||
|
||||
setModelSelectionOfFeature: SetModelSelectionOfFeatureFn = async (featureName, newVal, options) => {
|
||||
const newState: CodeSettingsState = {
|
||||
...this.state,
|
||||
modelSelectionOfFeature: {
|
||||
...this.state.modelSelectionOfFeature,
|
||||
[featureName]: newVal
|
||||
}
|
||||
}
|
||||
|
||||
this.state = newState
|
||||
|
||||
if (options?.doNotApplyEffects)
|
||||
return
|
||||
|
||||
await this._storeState()
|
||||
this._onDidChangeState.fire('modelSelectionOfFeature')
|
||||
}
|
||||
|
||||
|
||||
|
||||
setAutodetectedModels(providerName: ProviderName, newDefaultModelNames: string[], logging: object) {
|
||||
|
||||
const { models } = this.state.settingsOfProvider[providerName]
|
||||
|
||||
const old_names = models.map(m => m.modelName)
|
||||
|
||||
const newDefaultModels = modelInfoOfDefaultNames(newDefaultModelNames, { isAutodetected: true, existingModels: models })
|
||||
const newModels = [
|
||||
...newDefaultModels,
|
||||
...models.filter(m => !m.isDefault), // keep any non-default models
|
||||
]
|
||||
|
||||
|
||||
this.setSettingOfProvider(providerName, 'models', newModels)
|
||||
|
||||
// if the models changed, log it
|
||||
const new_names = newModels.map(m => m.modelName)
|
||||
if (!(old_names.length === new_names.length
|
||||
&& old_names.every((_, i) => old_names[i] === new_names[i])
|
||||
)) {
|
||||
this._metricsService.capture('Autodetect Models', { providerName, newModels, ...logging })
|
||||
}
|
||||
}
|
||||
toggleModelHidden(providerName: ProviderName, modelName: string) {
|
||||
|
||||
|
||||
const { models } = this.state.settingsOfProvider[providerName]
|
||||
const modelIdx = models.findIndex(m => m.modelName === modelName)
|
||||
if (modelIdx === -1) return
|
||||
const newIsHidden = !models[modelIdx].isHidden
|
||||
const newModels: CodeModelInfo[] = [
|
||||
...models.slice(0, modelIdx),
|
||||
{ ...models[modelIdx], isHidden: newIsHidden },
|
||||
...models.slice(modelIdx + 1, Infinity)
|
||||
]
|
||||
this.setSettingOfProvider(providerName, 'models', newModels)
|
||||
|
||||
this._metricsService.capture('Toggle Model Hidden', { providerName, modelName, newIsHidden })
|
||||
|
||||
}
|
||||
addModel(providerName: ProviderName, modelName: string) {
|
||||
const { models } = this.state.settingsOfProvider[providerName]
|
||||
const existingIdx = models.findIndex(m => m.modelName === modelName)
|
||||
if (existingIdx !== -1) return // if exists, do nothing
|
||||
const newModels = [
|
||||
...models,
|
||||
{ modelName, isDefault: false, isHidden: false }
|
||||
]
|
||||
this.setSettingOfProvider(providerName, 'models', newModels)
|
||||
|
||||
this._metricsService.capture('Add Model', { providerName, modelName })
|
||||
|
||||
}
|
||||
deleteModel(providerName: ProviderName, modelName: string): boolean {
|
||||
const { models } = this.state.settingsOfProvider[providerName]
|
||||
const delIdx = models.findIndex(m => m.modelName === modelName)
|
||||
if (delIdx === -1) return false
|
||||
const newModels = [
|
||||
...models.slice(0, delIdx), // delete the idx
|
||||
...models.slice(delIdx + 1, Infinity)
|
||||
]
|
||||
this.setSettingOfProvider(providerName, 'models', newModels)
|
||||
|
||||
this._metricsService.capture('Delete Model', { providerName, modelName })
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
registerSingleton(ICodeSettingsService, CodeSettingsService, InstantiationType.Eager);
|
||||
@@ -1,481 +0,0 @@
|
||||
|
||||
/*--------------------------------------------------------------------------------------
|
||||
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
|
||||
export type CodeModelInfo = {
|
||||
modelName: string,
|
||||
isDefault: boolean, // whether or not it's a default for its provider
|
||||
isHidden: boolean, // whether or not the user is hiding it
|
||||
isAutodetected?: boolean, // whether the model was autodetected by polling
|
||||
}
|
||||
|
||||
// creates `modelInfo` from `modelNames`
|
||||
export const modelInfoOfDefaultNames = (modelNames: string[], options?: { isAutodetected: true, existingModels: CodeModelInfo[] }): CodeModelInfo[] => {
|
||||
|
||||
const { isAutodetected, existingModels } = options ?? {}
|
||||
|
||||
if (!existingModels) { // default settings
|
||||
|
||||
return modelNames.map((modelName, i) => ({
|
||||
modelName,
|
||||
isDefault: true,
|
||||
isAutodetected: isAutodetected,
|
||||
isHidden: modelNames.length >= 10 // hide all models if there are a ton of them, and make user enable them individually
|
||||
}))
|
||||
|
||||
} else { // settings if there are existing models (keep existing `isHidden` property)
|
||||
|
||||
const existingModelsMap: Record<string, CodeModelInfo> = {}
|
||||
for (const existingModel of existingModels) {
|
||||
existingModelsMap[existingModel.modelName] = existingModel
|
||||
}
|
||||
|
||||
return modelNames.map((modelName, i) => ({
|
||||
modelName,
|
||||
isDefault: true,
|
||||
isAutodetected: isAutodetected,
|
||||
isHidden: !!existingModelsMap[modelName]?.isHidden,
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// https://docs.anthropic.com/en/docs/about-claude/models
|
||||
export const defaultAnthropicModels = modelInfoOfDefaultNames([
|
||||
'claude-3-5-sonnet-20241022',
|
||||
'claude-3-5-haiku-20241022',
|
||||
'claude-3-opus-20240229',
|
||||
'claude-3-sonnet-20240229',
|
||||
// 'claude-3-haiku-20240307',
|
||||
])
|
||||
|
||||
|
||||
// https://platform.openai.com/docs/models/gp
|
||||
export const defaultOpenAIModels = modelInfoOfDefaultNames([
|
||||
'o1-preview',
|
||||
'o1-mini',
|
||||
'gpt-4o',
|
||||
'gpt-4o-mini',
|
||||
// 'gpt-4o-2024-05-13',
|
||||
// 'gpt-4o-2024-08-06',
|
||||
// 'gpt-4o-mini-2024-07-18',
|
||||
// 'gpt-4-turbo',
|
||||
// 'gpt-4-turbo-2024-04-09',
|
||||
// 'gpt-4-turbo-preview',
|
||||
// 'gpt-4-0125-preview',
|
||||
// 'gpt-4-1106-preview',
|
||||
// 'gpt-4',
|
||||
// 'gpt-4-0613',
|
||||
// 'gpt-3.5-turbo-0125',
|
||||
// 'gpt-3.5-turbo',
|
||||
// 'gpt-3.5-turbo-1106',
|
||||
])
|
||||
|
||||
// https://platform.openai.com/docs/models/gp
|
||||
export const defaultDeepseekModels = modelInfoOfDefaultNames([
|
||||
'deepseek-chat',
|
||||
'deepseek-reasoner',
|
||||
])
|
||||
|
||||
|
||||
// https://console.groq.com/docs/models
|
||||
export const defaultGroqModels = modelInfoOfDefaultNames([
|
||||
"llama3-70b-8192",
|
||||
"llama-3.3-70b-versatile",
|
||||
"llama-3.1-8b-instant",
|
||||
"gemma2-9b-it",
|
||||
"mixtral-8x7b-32768"
|
||||
])
|
||||
|
||||
|
||||
export const defaultGeminiModels = modelInfoOfDefaultNames([
|
||||
'gemini-1.5-flash',
|
||||
'gemini-1.5-pro',
|
||||
'gemini-1.5-flash-8b',
|
||||
'gemini-2.0-flash-exp',
|
||||
'gemini-2.0-flash-thinking-exp-1219',
|
||||
'learnlm-1.5-pro-experimental'
|
||||
])
|
||||
|
||||
export const defaultMistralModels = modelInfoOfDefaultNames([
|
||||
"codestral-latest",
|
||||
"open-codestral-mamba",
|
||||
"open-mistral-nemo",
|
||||
"mistral-large-latest",
|
||||
"pixtral-large-latest",
|
||||
"ministral-3b-latest",
|
||||
"ministral-8b-latest",
|
||||
"mistral-small-latest",
|
||||
])
|
||||
|
||||
// export const parseMaxTokensStr = (maxTokensStr: string) => {
|
||||
// // parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN
|
||||
// const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr)
|
||||
// if (Number.isNaN(int))
|
||||
// return undefined
|
||||
// return int
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
export const anthropicMaxPossibleTokens = (modelName: string) => {
|
||||
if (modelName === 'claude-3-5-sonnet-20241022'
|
||||
|| modelName === 'claude-3-5-haiku-20241022')
|
||||
return 8192
|
||||
if (modelName === 'claude-3-opus-20240229'
|
||||
|| modelName === 'claude-3-sonnet-20240229'
|
||||
|| modelName === 'claude-3-haiku-20240307')
|
||||
return 4096
|
||||
return 1024 // return a reasonably small number if they're using a different model
|
||||
}
|
||||
|
||||
|
||||
type UnionOfKeys<T> = T extends T ? keyof T : never;
|
||||
|
||||
|
||||
|
||||
export const defaultProviderSettings = {
|
||||
anthropic: {
|
||||
apiKey: '',
|
||||
},
|
||||
openAI: {
|
||||
apiKey: '',
|
||||
},
|
||||
deepseek: {
|
||||
apiKey: '',
|
||||
},
|
||||
ollama: {
|
||||
endpoint: 'http://127.0.0.1:11434',
|
||||
},
|
||||
openRouter: {
|
||||
apiKey: '',
|
||||
},
|
||||
openAICompatible: {
|
||||
endpoint: '',
|
||||
apiKey: '',
|
||||
},
|
||||
gemini: {
|
||||
apiKey: '',
|
||||
},
|
||||
groq: {
|
||||
apiKey: '',
|
||||
},
|
||||
mistral: {
|
||||
apiKey: ''
|
||||
}
|
||||
} as const
|
||||
|
||||
export type ProviderName = keyof typeof defaultProviderSettings
|
||||
export const providerNames = Object.keys(defaultProviderSettings) as ProviderName[]
|
||||
|
||||
export const localProviderNames = ['ollama'] satisfies ProviderName[] // all local names
|
||||
export const nonlocalProviderNames = providerNames.filter((name) => !(localProviderNames as string[]).includes(name)) // all non-local names
|
||||
|
||||
type CustomSettingName = UnionOfKeys<typeof defaultProviderSettings[ProviderName]>
|
||||
type CustomProviderSettings<providerName extends ProviderName> = {
|
||||
[k in CustomSettingName]: k extends keyof typeof defaultProviderSettings[providerName] ? string : undefined
|
||||
}
|
||||
export const customSettingNamesOfProvider = (providerName: ProviderName) => {
|
||||
return Object.keys(defaultProviderSettings[providerName]) as CustomSettingName[]
|
||||
}
|
||||
|
||||
|
||||
type CommonProviderSettings = {
|
||||
_enabled: boolean | undefined, // undefined initially, computed when user types in all fields
|
||||
models: CodeModelInfo[],
|
||||
}
|
||||
|
||||
export type SettingsForProvider<providerName extends ProviderName> = CustomProviderSettings<providerName> & CommonProviderSettings
|
||||
|
||||
// part of state
|
||||
export type SettingsOfProvider = {
|
||||
[providerName in ProviderName]: SettingsForProvider<providerName>
|
||||
}
|
||||
|
||||
|
||||
export type SettingName = keyof SettingsForProvider<ProviderName>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
type DisplayInfoForProviderName = {
|
||||
title: string,
|
||||
desc?: string,
|
||||
}
|
||||
|
||||
export const displayInfoOfProviderName = (providerName: ProviderName): DisplayInfoForProviderName => {
|
||||
if (providerName === 'anthropic') {
|
||||
return {
|
||||
title: 'Anthropic',
|
||||
}
|
||||
}
|
||||
else if (providerName === 'openAI') {
|
||||
return {
|
||||
title: 'OpenAI',
|
||||
}
|
||||
}
|
||||
else if (providerName === 'deepseek') {
|
||||
return {
|
||||
title: 'DeepSeek',
|
||||
}
|
||||
}
|
||||
else if (providerName === 'openRouter') {
|
||||
return {
|
||||
title: 'OpenRouter',
|
||||
}
|
||||
}
|
||||
else if (providerName === 'ollama') {
|
||||
return {
|
||||
title: 'Ollama',
|
||||
|
||||
}
|
||||
}
|
||||
else if (providerName === 'openAICompatible') {
|
||||
return {
|
||||
title: 'OpenAI-Compatible',
|
||||
}
|
||||
}
|
||||
else if (providerName === 'gemini') {
|
||||
return {
|
||||
title: 'Gemini',
|
||||
}
|
||||
}
|
||||
else if (providerName === 'groq') {
|
||||
return {
|
||||
title: 'Groq',
|
||||
}
|
||||
}
|
||||
else if (providerName === 'mistral') {
|
||||
return {
|
||||
title: 'Mistral',
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`descOfProviderName: Unknown provider name: "${providerName}"`)
|
||||
}
|
||||
|
||||
type DisplayInfo = {
|
||||
title: string,
|
||||
placeholder: string,
|
||||
subTextMd?: string,
|
||||
}
|
||||
export const displayInfoOfSettingName = (providerName: ProviderName, settingName: SettingName): DisplayInfo => {
|
||||
if (settingName === 'apiKey') {
|
||||
return {
|
||||
title: 'API Key',
|
||||
|
||||
// **Please follow this convention**:
|
||||
// The word "key..." here is a placeholder for the hash. For example, sk-ant-key... means the key will look like sk-ant-abcdefg123...
|
||||
placeholder: providerName === 'anthropic' ? 'sk-ant-key...' : // sk-ant-api03-key
|
||||
providerName === 'openAI' ? 'sk-proj-key...' :
|
||||
providerName === 'deepseek' ? 'sk-key...' :
|
||||
providerName === 'openRouter' ? 'sk-or-key...' : // sk-or-v1-key
|
||||
providerName === 'gemini' ? 'key...' :
|
||||
providerName === 'groq' ? 'gsk_key...' :
|
||||
providerName === 'mistral' ? 'key...' :
|
||||
providerName === 'openAICompatible' ? 'sk-key...' :
|
||||
'',
|
||||
|
||||
subTextMd: providerName === 'anthropic' ? 'Get your [API Key here](https://console.anthropic.com/settings/keys).' :
|
||||
providerName === 'openAI' ? 'Get your [API Key here](https://platform.openai.com/api-keys).' :
|
||||
providerName === 'deepseek' ? 'Get your [API Key here](https://platform.deepseek.com/api_keys).' :
|
||||
providerName === 'openRouter' ? 'Get your [API Key here](https://openrouter.ai/settings/keys).' :
|
||||
providerName === 'gemini' ? 'Get your [API Key here](https://aistudio.google.com/apikey).' :
|
||||
providerName === 'groq' ? 'Get your [API Key here](https://console.groq.com/keys).' :
|
||||
providerName === 'mistral' ? 'Get your [API Key here](https://console.mistral.ai/api-keys/).' :
|
||||
providerName === 'openAICompatible' ? undefined :
|
||||
'',
|
||||
}
|
||||
}
|
||||
else if (settingName === 'endpoint') {
|
||||
return {
|
||||
title: providerName === 'ollama' ? 'Endpoint' :
|
||||
providerName === 'openAICompatible' ? 'baseURL' // (do not include /chat/completions)
|
||||
: '(never)',
|
||||
|
||||
placeholder: providerName === 'ollama' ? defaultProviderSettings.ollama.endpoint
|
||||
: providerName === 'openAICompatible' ? 'https://my-website.com/v1'
|
||||
: '(never)',
|
||||
|
||||
subTextMd: providerName === 'ollama' ? 'If you would like to change this endpoint, please read more about [Endpoints here](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-expose-ollama-on-my-network).' :
|
||||
undefined,
|
||||
}
|
||||
}
|
||||
else if (settingName === '_enabled') {
|
||||
return {
|
||||
title: '(never)',
|
||||
placeholder: '(never)',
|
||||
}
|
||||
}
|
||||
else if (settingName === 'models') {
|
||||
return {
|
||||
title: '(never)',
|
||||
placeholder: '(never)',
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`displayInfo: Unknown setting name: "${settingName}"`)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const defaultCustomSettings: Record<CustomSettingName, undefined> = {
|
||||
apiKey: undefined,
|
||||
endpoint: undefined,
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const voidInitModelOptions = {
|
||||
anthropic: {
|
||||
models: defaultAnthropicModels,
|
||||
},
|
||||
openAI: {
|
||||
models: defaultOpenAIModels,
|
||||
},
|
||||
deepseek: {
|
||||
models: defaultDeepseekModels,
|
||||
},
|
||||
ollama: {
|
||||
models: [],
|
||||
},
|
||||
openRouter: {
|
||||
models: [], // any string
|
||||
},
|
||||
openAICompatible: {
|
||||
models: [],
|
||||
},
|
||||
gemini: {
|
||||
models: defaultGeminiModels,
|
||||
},
|
||||
groq: {
|
||||
models: defaultGroqModels,
|
||||
},
|
||||
mistral: {
|
||||
models: defaultMistralModels,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// used when waiting and for a type reference
|
||||
export const defaultSettingsOfProvider: SettingsOfProvider = {
|
||||
anthropic: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.anthropic,
|
||||
...voidInitModelOptions.anthropic,
|
||||
_enabled: undefined,
|
||||
},
|
||||
openAI: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.openAI,
|
||||
...voidInitModelOptions.openAI,
|
||||
_enabled: undefined,
|
||||
},
|
||||
deepseek: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.deepseek,
|
||||
...voidInitModelOptions.deepseek,
|
||||
_enabled: undefined,
|
||||
},
|
||||
gemini: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.gemini,
|
||||
...voidInitModelOptions.gemini,
|
||||
_enabled: undefined,
|
||||
},
|
||||
groq: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.groq,
|
||||
...voidInitModelOptions.groq,
|
||||
_enabled: undefined,
|
||||
},
|
||||
ollama: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.ollama,
|
||||
...voidInitModelOptions.ollama,
|
||||
_enabled: undefined,
|
||||
},
|
||||
openRouter: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.openRouter,
|
||||
...voidInitModelOptions.openRouter,
|
||||
_enabled: undefined,
|
||||
},
|
||||
openAICompatible: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.openAICompatible,
|
||||
...voidInitModelOptions.openAICompatible,
|
||||
_enabled: undefined,
|
||||
},
|
||||
mistral: {
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.mistral,
|
||||
...voidInitModelOptions.mistral,
|
||||
_enabled: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export type ModelSelection = { providerName: ProviderName, modelName: string }
|
||||
|
||||
export const modelSelectionsEqual = (m1: ModelSelection, m2: ModelSelection) => {
|
||||
return m1.modelName === m2.modelName && m1.providerName === m2.providerName
|
||||
}
|
||||
|
||||
// this is a state
|
||||
export const featureNames = ['Ctrl+L', 'Ctrl+K', 'Autocomplete', 'FastApply'] as const
|
||||
export type ModelSelectionOfFeature = Record<(typeof featureNames)[number], ModelSelection | null>
|
||||
export type FeatureName = keyof ModelSelectionOfFeature
|
||||
|
||||
export const displayInfoOfFeatureName = (featureName: FeatureName) => {
|
||||
if (featureName === 'Autocomplete')
|
||||
return 'Autocomplete'
|
||||
else if (featureName === 'Ctrl+K')
|
||||
return 'Quick Edit'
|
||||
else if (featureName === 'Ctrl+L')
|
||||
return 'Sidebar Chat'
|
||||
else if (featureName === 'FastApply')
|
||||
return 'Fast Apply'
|
||||
else
|
||||
throw new Error(`Feature Name ${featureName} not allowed`)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// the models of these can be refreshed (in theory all can, but not all should)
|
||||
export const refreshableProviderNames = localProviderNames
|
||||
export type RefreshableProviderName = typeof refreshableProviderNames[number]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export type GlobalSettings = {
|
||||
autoRefreshModels: boolean;
|
||||
aiInstructions: string;
|
||||
}
|
||||
export const defaultGlobalSettings: GlobalSettings = {
|
||||
autoRefreshModels: true,
|
||||
aiInstructions: '',
|
||||
}
|
||||
|
||||
export type GlobalSettingName = keyof GlobalSettings
|
||||
export const globalSettingNames = Object.keys(defaultGlobalSettings) as GlobalSettingName[]
|
||||
|
||||
|
||||
@@ -1,179 +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 { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, ServiceSendLLMMessageParams, MainSendLLMMessageParams, MainLLMMessageAbortParams, ServiceModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, MainModelListParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from './llmMessageTypes.js';
|
||||
import { IChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||
import { IMainProcessService } from '../../ipc/common/mainProcessService.js';
|
||||
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
|
||||
import { generateUuid } from '../../../base/common/uuid.js';
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
import { Event } from '../../../base/common/event.js';
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import { ICodeSettingsService } from './codeSettingsService.js';
|
||||
// import { INotificationService } from '../../notification/common/notification.js';
|
||||
|
||||
// calls channel to implement features
|
||||
export const ILLMMessageService = createDecorator<ILLMMessageService>('llmMessageService');
|
||||
|
||||
export interface ILLMMessageService {
|
||||
readonly _serviceBrand: undefined;
|
||||
sendLLMMessage: (params: ServiceSendLLMMessageParams) => string | null;
|
||||
abort: (requestId: string) => void;
|
||||
ollamaList: (params: ServiceModelListParams<OllamaModelResponse>) => void;
|
||||
openAICompatibleList: (params: ServiceModelListParams<OpenaiCompatibleModelResponse>) => void;
|
||||
}
|
||||
|
||||
export class LLMMessageService extends Disposable implements ILLMMessageService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly channel: IChannel // LLMMessageChannel
|
||||
|
||||
// llmMessage
|
||||
private readonly onTextHooks_llm: { [eventId: string]: ((params: EventLLMMessageOnTextParams) => void) } = {}
|
||||
private readonly onFinalMessageHooks_llm: { [eventId: string]: ((params: EventLLMMessageOnFinalMessageParams) => void) } = {}
|
||||
private readonly onErrorHooks_llm: { [eventId: string]: ((params: EventLLMMessageOnErrorParams) => void) } = {}
|
||||
|
||||
|
||||
// ollamaList
|
||||
private readonly onSuccess_ollama: { [eventId: string]: ((params: EventModelListOnSuccessParams<OllamaModelResponse>) => void) } = {}
|
||||
private readonly onError_ollama: { [eventId: string]: ((params: EventModelListOnErrorParams<OllamaModelResponse>) => void) } = {}
|
||||
|
||||
// openAICompatibleList
|
||||
private readonly onSuccess_openAICompatible: { [eventId: string]: ((params: EventModelListOnSuccessParams<OpenaiCompatibleModelResponse>) => void) } = {}
|
||||
private readonly onError_openAICompatible: { [eventId: string]: ((params: EventModelListOnErrorParams<OpenaiCompatibleModelResponse>) => void) } = {}
|
||||
|
||||
constructor(
|
||||
@IMainProcessService private readonly mainProcessService: IMainProcessService, // used as a renderer (only usable on client side)
|
||||
@ICodeSettingsService private readonly codeSettingsService: ICodeSettingsService,
|
||||
// @INotificationService private readonly notificationService: INotificationService,
|
||||
) {
|
||||
super()
|
||||
|
||||
// const service = ProxyChannel.toService<LLMMessageChannel>(mainProcessService.getChannel('code-channel-sendLLMMessage')); // lets you call it like a service
|
||||
// see llmMessageChannel.ts
|
||||
this.channel = this.mainProcessService.getChannel('code-channel-llmMessageService')
|
||||
|
||||
// .listen sets up an IPC channel and takes a few ms, so we set up listeners immediately and add hooks to them instead
|
||||
// llm
|
||||
this._register((this.channel.listen('onText_llm') satisfies Event<EventLLMMessageOnTextParams>)(e => {
|
||||
this.onTextHooks_llm[e.requestId]?.(e)
|
||||
}))
|
||||
this._register((this.channel.listen('onFinalMessage_llm') satisfies Event<EventLLMMessageOnFinalMessageParams>)(e => {
|
||||
this.onFinalMessageHooks_llm[e.requestId]?.(e)
|
||||
this._onRequestIdDone(e.requestId)
|
||||
}))
|
||||
this._register((this.channel.listen('onError_llm') satisfies Event<EventLLMMessageOnErrorParams>)(e => {
|
||||
console.error('Error in LLMMessageService:', JSON.stringify(e))
|
||||
this.onErrorHooks_llm[e.requestId]?.(e)
|
||||
this._onRequestIdDone(e.requestId)
|
||||
}))
|
||||
// ollama .list()
|
||||
this._register((this.channel.listen('onSuccess_ollama') satisfies Event<EventModelListOnSuccessParams<OllamaModelResponse>>)(e => {
|
||||
this.onSuccess_ollama[e.requestId]?.(e)
|
||||
}))
|
||||
this._register((this.channel.listen('onError_ollama') satisfies Event<EventModelListOnErrorParams<OllamaModelResponse>>)(e => {
|
||||
this.onError_ollama[e.requestId]?.(e)
|
||||
}))
|
||||
// openaiCompatible .list()
|
||||
this._register((this.channel.listen('onSuccess_openAICompatible') satisfies Event<EventModelListOnSuccessParams<OpenaiCompatibleModelResponse>>)(e => {
|
||||
this.onSuccess_openAICompatible[e.requestId]?.(e)
|
||||
}))
|
||||
this._register((this.channel.listen('onError_openAICompatible') satisfies Event<EventModelListOnErrorParams<OpenaiCompatibleModelResponse>>)(e => {
|
||||
this.onError_openAICompatible[e.requestId]?.(e)
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
sendLLMMessage(params: ServiceSendLLMMessageParams) {
|
||||
const { onText, onFinalMessage, onError, ...proxyParams } = params;
|
||||
const { useProviderFor: featureName } = proxyParams
|
||||
|
||||
// end early if no provider
|
||||
const modelSelection = this.codeSettingsService.state.modelSelectionOfFeature[featureName]
|
||||
if (modelSelection === null) {
|
||||
onError({ message: 'Please add a Provider in Settings!', fullError: null })
|
||||
return null
|
||||
}
|
||||
|
||||
const { providerName, modelName } = modelSelection
|
||||
|
||||
// add state for request id
|
||||
const requestId = generateUuid();
|
||||
this.onTextHooks_llm[requestId] = onText
|
||||
this.onFinalMessageHooks_llm[requestId] = onFinalMessage
|
||||
this.onErrorHooks_llm[requestId] = onError
|
||||
|
||||
const { aiInstructions } = this.codeSettingsService.state.globalSettings
|
||||
const { settingsOfProvider } = this.codeSettingsService.state
|
||||
|
||||
// params will be stripped of all its functions over the IPC channel
|
||||
this.channel.call('sendLLMMessage', {
|
||||
...proxyParams,
|
||||
aiInstructions,
|
||||
requestId,
|
||||
providerName,
|
||||
modelName,
|
||||
settingsOfProvider,
|
||||
} satisfies MainSendLLMMessageParams);
|
||||
|
||||
return requestId
|
||||
}
|
||||
|
||||
|
||||
abort(requestId: string) {
|
||||
this.channel.call('abort', { requestId } satisfies MainLLMMessageAbortParams);
|
||||
this._onRequestIdDone(requestId)
|
||||
}
|
||||
|
||||
|
||||
ollamaList = (params: ServiceModelListParams<OllamaModelResponse>) => {
|
||||
const { onSuccess, onError, ...proxyParams } = params
|
||||
|
||||
const { settingsOfProvider } = this.codeSettingsService.state
|
||||
|
||||
// add state for request id
|
||||
const requestId_ = generateUuid();
|
||||
this.onSuccess_ollama[requestId_] = onSuccess
|
||||
this.onError_ollama[requestId_] = onError
|
||||
|
||||
this.channel.call('ollamaList', {
|
||||
...proxyParams,
|
||||
settingsOfProvider,
|
||||
requestId: requestId_,
|
||||
} satisfies MainModelListParams<OllamaModelResponse>)
|
||||
}
|
||||
|
||||
|
||||
openAICompatibleList = (params: ServiceModelListParams<OpenaiCompatibleModelResponse>) => {
|
||||
const { onSuccess, onError, ...proxyParams } = params
|
||||
|
||||
const { settingsOfProvider } = this.codeSettingsService.state
|
||||
|
||||
// add state for request id
|
||||
const requestId_ = generateUuid();
|
||||
this.onSuccess_openAICompatible[requestId_] = onSuccess
|
||||
this.onError_openAICompatible[requestId_] = onError
|
||||
|
||||
this.channel.call('openAICompatibleList', {
|
||||
...proxyParams,
|
||||
settingsOfProvider,
|
||||
requestId: requestId_,
|
||||
} satisfies MainModelListParams<OpenaiCompatibleModelResponse>)
|
||||
}
|
||||
|
||||
|
||||
|
||||
_onRequestIdDone(requestId: string) {
|
||||
delete this.onTextHooks_llm[requestId]
|
||||
delete this.onFinalMessageHooks_llm[requestId]
|
||||
delete this.onErrorHooks_llm[requestId]
|
||||
|
||||
delete this.onSuccess_ollama[requestId]
|
||||
delete this.onError_ollama[requestId]
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ILLMMessageService, LLMMessageService, InstantiationType.Eager);
|
||||
|
||||
@@ -1,177 +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 { ProviderName, SettingsOfProvider } from './codeSettingsTypes.js'
|
||||
|
||||
|
||||
export const errorDetails = (fullError: Error | null): string | null => {
|
||||
if (fullError === null) {
|
||||
return null
|
||||
}
|
||||
else if (typeof fullError === 'object') {
|
||||
return JSON.stringify(fullError, null, 2)
|
||||
}
|
||||
else if (typeof fullError === 'string') {
|
||||
return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export type OnText = (p: { newText: string, fullText: string }) => void
|
||||
export type OnFinalMessage = (p: { fullText: string }) => void
|
||||
export type OnError = (p: { message: string, fullError: Error | null }) => void
|
||||
export type AbortRef = { current: (() => void) | null }
|
||||
|
||||
export type LLMMessage = {
|
||||
role: 'system' | 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type _InternalLLMMessage = {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
type _InternalOllamaFIMMessages = {
|
||||
prefix: string;
|
||||
suffix: string;
|
||||
stopTokens: string[];
|
||||
}
|
||||
|
||||
type SendLLMType = {
|
||||
type: 'sendLLMMessage';
|
||||
messages: LLMMessage[];
|
||||
} | {
|
||||
type: 'ollamaFIM';
|
||||
messages: _InternalOllamaFIMMessages;
|
||||
}
|
||||
|
||||
// service types
|
||||
export type ServiceSendLLMMessageParams = {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
logging: { loggingName: string, };
|
||||
useProviderFor: 'Ctrl+K' | 'Ctrl+L' | 'Autocomplete';
|
||||
} & SendLLMType
|
||||
|
||||
// params to the true sendLLMMessage function
|
||||
export type SendLLMMessageParams = {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
logging: { loggingName: string, };
|
||||
abortRef: AbortRef;
|
||||
|
||||
aiInstructions: string;
|
||||
|
||||
providerName: ProviderName;
|
||||
modelName: string;
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
} & SendLLMType
|
||||
|
||||
|
||||
|
||||
// can't send functions across a proxy, use listeners instead
|
||||
export type BlockedMainLLMMessageParams = 'onText' | 'onFinalMessage' | 'onError' | 'abortRef'
|
||||
export type MainSendLLMMessageParams = Omit<SendLLMMessageParams, BlockedMainLLMMessageParams> & { requestId: string } & SendLLMType
|
||||
|
||||
export type MainLLMMessageAbortParams = { requestId: string }
|
||||
|
||||
export type EventLLMMessageOnTextParams = Parameters<OnText>[0] & { requestId: string }
|
||||
export type EventLLMMessageOnFinalMessageParams = Parameters<OnFinalMessage>[0] & { requestId: string }
|
||||
export type EventLLMMessageOnErrorParams = Parameters<OnError>[0] & { requestId: string }
|
||||
|
||||
|
||||
export type _InternalSendLLMMessageFnType = (
|
||||
params: {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
providerName: ProviderName;
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
modelName: string;
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
|
||||
messages: _InternalLLMMessage[];
|
||||
}
|
||||
) => void
|
||||
|
||||
export type _InternalOllamaFIMMessageFnType = (
|
||||
params: {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
providerName: ProviderName;
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
modelName: string;
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
|
||||
messages: _InternalOllamaFIMMessages;
|
||||
}
|
||||
) => void
|
||||
|
||||
// service -> main -> internal -> event (back to main)
|
||||
// (browser)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// These are from 'ollama' SDK
|
||||
interface OllamaModelDetails {
|
||||
parent_model: string;
|
||||
format: string;
|
||||
family: string;
|
||||
families: string[];
|
||||
parameter_size: string;
|
||||
quantization_level: string;
|
||||
}
|
||||
|
||||
export type OllamaModelResponse = {
|
||||
name: string;
|
||||
modified_at: Date;
|
||||
size: number;
|
||||
digest: string;
|
||||
details: OllamaModelDetails;
|
||||
expires_at: Date;
|
||||
size_vram: number;
|
||||
}
|
||||
|
||||
export type OpenaiCompatibleModelResponse = {
|
||||
id: string;
|
||||
created: number;
|
||||
object: 'model';
|
||||
owned_by: string;
|
||||
}
|
||||
|
||||
|
||||
// params to the true list fn
|
||||
export type ModelListParams<modelResponse> = {
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
onSuccess: (param: { models: modelResponse[] }) => void;
|
||||
onError: (param: { error: string }) => void;
|
||||
}
|
||||
|
||||
// params to the service
|
||||
export type ServiceModelListParams<modelResponse> = {
|
||||
onSuccess: (param: { models: modelResponse[] }) => void;
|
||||
onError: (param: { error: any }) => void;
|
||||
}
|
||||
|
||||
type BlockedMainModelListParams = 'onSuccess' | 'onError'
|
||||
export type MainModelListParams<modelResponse> = Omit<ModelListParams<modelResponse>, BlockedMainModelListParams> & { requestId: string }
|
||||
|
||||
export type EventModelListOnSuccessParams<modelResponse> = Parameters<ModelListParams<modelResponse>['onSuccess']>[0] & { requestId: string }
|
||||
export type EventModelListOnErrorParams<modelResponse> = Parameters<ModelListParams<modelResponse>['onError']>[0] & { requestId: string }
|
||||
|
||||
|
||||
|
||||
|
||||
export type _InternalModelListFnType<modelResponse> = (params: ModelListParams<modelResponse>) => void
|
||||
@@ -1,73 +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 { createDecorator, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js';
|
||||
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js';
|
||||
import { localize2 } from '../../../../nls.js';
|
||||
import { registerAction2, Action2 } from '../../../../platform/actions/common/actions.js';
|
||||
import { INotificationService } from '../../../../platform/notification/common/notification.js';
|
||||
|
||||
export interface IMetricsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
capture(event: string, params: Record<string, any>): void;
|
||||
setOptOut(val: boolean): void;
|
||||
getDebuggingProperties(): Promise<object>;
|
||||
}
|
||||
|
||||
export const IMetricsService = createDecorator<IMetricsService>('metricsService');
|
||||
|
||||
|
||||
// implemented by calling channel
|
||||
export class MetricsService implements IMetricsService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly metricsService: IMetricsService;
|
||||
|
||||
constructor(
|
||||
@IMainProcessService mainProcessService: IMainProcessService // (only usable on client side)
|
||||
) {
|
||||
// creates an IPC proxy to use metricsMainService.ts
|
||||
this.metricsService = ProxyChannel.toService<IMetricsService>(mainProcessService.getChannel('code-channel-metrics'));
|
||||
}
|
||||
|
||||
// call capture on the channel
|
||||
capture(...params: Parameters<IMetricsService['capture']>) {
|
||||
this.metricsService.capture(...params);
|
||||
}
|
||||
|
||||
setOptOut(...params: Parameters<IMetricsService['setOptOut']>) {
|
||||
this.metricsService.setOptOut(...params);
|
||||
}
|
||||
|
||||
|
||||
// anything transmitted over a channel must be async even if it looks like it doesn't have to be
|
||||
async getDebuggingProperties(): Promise<object> {
|
||||
return this.metricsService.getDebuggingProperties()
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IMetricsService, MetricsService, InstantiationType.Eager);
|
||||
|
||||
|
||||
// debugging action
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'codeDebugInfo',
|
||||
f1: true,
|
||||
title: localize2('voidMetricsDebug', 'Code: Log Debug Info'),
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const metricsService = accessor.get(IMetricsService)
|
||||
const notifService = accessor.get(INotificationService)
|
||||
|
||||
const debugProperties = await metricsService.getDebuggingProperties()
|
||||
console.log('Metrics:', debugProperties)
|
||||
notifService.info(`Code Debug info:\n${JSON.stringify(debugProperties, null, 2)}`)
|
||||
}
|
||||
})
|
||||
@@ -1,222 +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 { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
|
||||
import { ICodeSettingsService } from './codeSettingsService.js';
|
||||
import { ILLMMessageService } from './llmMessageService.js';
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { Disposable, IDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { RefreshableProviderName, refreshableProviderNames, SettingsOfProvider } from './codeSettingsTypes.js';
|
||||
import { OllamaModelResponse, OpenaiCompatibleModelResponse } from './llmMessageTypes.js';
|
||||
|
||||
|
||||
|
||||
|
||||
type RefreshableState = ({
|
||||
state: 'init',
|
||||
timeoutId: null,
|
||||
} | {
|
||||
state: 'refreshing',
|
||||
timeoutId: NodeJS.Timeout | null, // the timeoutId of the most recent call to refreshModels
|
||||
} | {
|
||||
state: 'finished',
|
||||
timeoutId: null,
|
||||
} | {
|
||||
state: 'error',
|
||||
timeoutId: null,
|
||||
})
|
||||
|
||||
|
||||
/*
|
||||
|
||||
user click -> error -> fire(error)
|
||||
\> success -> fire(success)
|
||||
finally: keep polling
|
||||
|
||||
poll -> do not fire
|
||||
|
||||
*/
|
||||
export type RefreshModelStateOfProvider = Record<RefreshableProviderName, RefreshableState>
|
||||
|
||||
|
||||
|
||||
const refreshBasedOn: { [k in RefreshableProviderName]: (keyof SettingsOfProvider[k])[] } = {
|
||||
ollama: ['_didFillInProviderSettings', 'endpoint'],
|
||||
vLLM: ['_didFillInProviderSettings', 'endpoint'],
|
||||
lmStudio: ['_didFillInProviderSettings', 'endpoint'],
|
||||
// openAICompatible: ['_didFillInProviderSettings', 'endpoint', 'apiKey'],
|
||||
}
|
||||
const REFRESH_INTERVAL = 5_000
|
||||
// const COOLDOWN_TIMEOUT = 300
|
||||
|
||||
const autoOptions = { enableProviderOnSuccess: true, doNotFire: true }
|
||||
|
||||
// element-wise equals
|
||||
function eq<T>(a: T[], b: T[]): boolean {
|
||||
if (a.length !== b.length) return false
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
export interface IRefreshModelService {
|
||||
readonly _serviceBrand: undefined;
|
||||
startRefreshingModels: (providerName: RefreshableProviderName, options: { enableProviderOnSuccess: boolean, doNotFire: boolean }) => void;
|
||||
onDidChangeState: Event<RefreshableProviderName>;
|
||||
state: RefreshModelStateOfProvider;
|
||||
}
|
||||
|
||||
export const IRefreshModelService = createDecorator<IRefreshModelService>('RefreshModelService');
|
||||
|
||||
export class RefreshModelService extends Disposable implements IRefreshModelService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<RefreshableProviderName>();
|
||||
readonly onDidChangeState: Event<RefreshableProviderName> = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes
|
||||
|
||||
|
||||
constructor(
|
||||
@ICodeSettingsService private readonly codeSettingsService: ICodeSettingsService,
|
||||
@ILLMMessageService private readonly llmMessageService: ILLMMessageService,
|
||||
) {
|
||||
super()
|
||||
|
||||
|
||||
const disposables: Set<IDisposable> = new Set()
|
||||
|
||||
const initializeAutoPollingAndOnChange = () => {
|
||||
this._clearAllTimeouts()
|
||||
disposables.forEach(d => d.dispose())
|
||||
disposables.clear()
|
||||
|
||||
if (!codeSettingsService.state.globalSettings.autoRefreshModels) return
|
||||
|
||||
for (const providerName of refreshableProviderNames) {
|
||||
|
||||
// const { _enabled: enabled } = this.codeSettingsService.state.settingsOfProvider[providerName]
|
||||
this.startRefreshingModels(providerName, autoOptions)
|
||||
|
||||
// every time providerName.enabled changes, refresh models too, like a useEffect
|
||||
let relevantVals = () => refreshBasedOn[providerName].map(settingName => codeSettingsService.state.settingsOfProvider[providerName][settingName])
|
||||
let prevVals = relevantVals() // each iteration of a for loop has its own context and vars, so this is ok
|
||||
disposables.add(
|
||||
codeSettingsService.onDidChangeState(() => { // we might want to debounce this
|
||||
const newVals = relevantVals()
|
||||
if (!eq(prevVals, newVals)) {
|
||||
|
||||
const prevEnabled = prevVals[0] as boolean
|
||||
const enabled = newVals[0] as boolean
|
||||
|
||||
// if it was just enabled, or there was a change and it wasn't to the enabled state, refresh
|
||||
if ((enabled && !prevEnabled) || (!enabled && !prevEnabled)) {
|
||||
// if user just clicked enable, refresh
|
||||
this.startRefreshingModels(providerName, autoOptions)
|
||||
}
|
||||
else {
|
||||
// else if user just clicked disable, don't refresh
|
||||
|
||||
// //give cooldown before re-enabling (or at least re-fetching)
|
||||
// const timeoutId = setTimeout(() => this.refreshModels(providerName, !enabled), COOLDOWN_TIMEOUT)
|
||||
// this._setTimeoutId(providerName, timeoutId)
|
||||
}
|
||||
prevVals = newVals
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// on mount (when get init settings state), and if a relevant feature flag changes, start refreshing models
|
||||
codeSettingsService.waitForInitState.then(() => {
|
||||
initializeAutoPollingAndOnChange()
|
||||
this._register(
|
||||
codeSettingsService.onDidChangeState((type) => { if (typeof type === 'object' && type[1] === 'autoRefreshModels') initializeAutoPollingAndOnChange() })
|
||||
)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
state: RefreshModelStateOfProvider = {
|
||||
ollama: { state: 'init', timeoutId: null },
|
||||
vLLM: { state: 'init', timeoutId: null },
|
||||
lmStudio: { state: 'init', timeoutId: null },
|
||||
}
|
||||
|
||||
|
||||
// start listening for models (and don't stop)
|
||||
startRefreshingModels: IRefreshModelService['startRefreshingModels'] = (providerName, options) => {
|
||||
|
||||
this._clearProviderTimeout(providerName)
|
||||
|
||||
this._setRefreshState(providerName, 'refreshing', options)
|
||||
|
||||
const autoPoll = () => {
|
||||
if (this.codeSettingsService.state.globalSettings.autoRefreshModels) {
|
||||
// resume auto-polling
|
||||
const timeoutId = setTimeout(() => this.startRefreshingModels(providerName, autoOptions), REFRESH_INTERVAL)
|
||||
this._setTimeoutId(providerName, timeoutId)
|
||||
}
|
||||
}
|
||||
const listFn = providerName === 'ollama' ? this.llmMessageService.ollamaList
|
||||
: this.llmMessageService.openAICompatibleList
|
||||
|
||||
listFn({
|
||||
providerName,
|
||||
onSuccess: ({ models }) => {
|
||||
// set the models to the detected models
|
||||
this.codeSettingsService.setAutodetectedModels(
|
||||
providerName,
|
||||
models.map(model => {
|
||||
if (providerName === 'ollama') return (model as OllamaModelResponse).name;
|
||||
else if (providerName === 'vLLM') return (model as OpenaiCompatibleModelResponse).id;
|
||||
else if (providerName === 'lmStudio') return (model as OpenaiCompatibleModelResponse).id;
|
||||
else throw new Error('refreshMode fn: unknown provider', providerName);
|
||||
}),
|
||||
{ enableProviderOnSuccess: options.enableProviderOnSuccess, hideRefresh: options.doNotFire }
|
||||
)
|
||||
|
||||
if (options.enableProviderOnSuccess) this.codeSettingsService.setSettingOfProvider(providerName, '_enabled', true)
|
||||
|
||||
this._setRefreshState(providerName, 'finished', options)
|
||||
autoPoll()
|
||||
},
|
||||
onError: ({ error }) => {
|
||||
this._setRefreshState(providerName, 'error', options)
|
||||
autoPoll()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
_clearAllTimeouts() {
|
||||
for (const providerName of refreshableProviderNames) {
|
||||
this._clearProviderTimeout(providerName)
|
||||
}
|
||||
}
|
||||
|
||||
_clearProviderTimeout(providerName: RefreshableProviderName) {
|
||||
// cancel any existing poll
|
||||
if (this.state[providerName].timeoutId) {
|
||||
clearTimeout(this.state[providerName].timeoutId)
|
||||
this._setTimeoutId(providerName, null)
|
||||
}
|
||||
}
|
||||
|
||||
private _setTimeoutId(providerName: RefreshableProviderName, timeoutId: NodeJS.Timeout | null) {
|
||||
this.state[providerName].timeoutId = timeoutId
|
||||
}
|
||||
|
||||
private _setRefreshState(providerName: RefreshableProviderName, state: RefreshableState['state'], options?: { doNotFire: boolean }) {
|
||||
if (options?.doNotFire) return
|
||||
this.state[providerName].state = state
|
||||
this._onDidChangeState.fire(providerName)
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IRefreshModelService, RefreshModelService, InstantiationType.Eager);
|
||||
|
||||
@@ -1,50 +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 { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js';
|
||||
|
||||
import { IProductService } from '../../product/common/productService.js';
|
||||
|
||||
import { ICodeUpdateService } from '../common/codeUpdateService.js';
|
||||
|
||||
|
||||
|
||||
export class CodeMainUpdateService extends Disposable implements ICodeUpdateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
@IEnvironmentMainService private readonly _envMainService: IEnvironmentMainService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
async check() {
|
||||
const isDevMode = !this._envMainService.isBuilt // found in abstractUpdateService.ts
|
||||
|
||||
if (isDevMode) {
|
||||
return { hasUpdate: false } as const
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`https://updates.code.hanzo.ai/api/v0/${this._productService.commit}`)
|
||||
const resJSON = await res.json()
|
||||
|
||||
if (!resJSON) return null
|
||||
|
||||
const { hasUpdate, downloadMessage } = resJSON ?? {}
|
||||
if (hasUpdate === undefined)
|
||||
return null
|
||||
|
||||
const after = (downloadMessage || '') + ''
|
||||
return { hasUpdate: !!hasUpdate, message: after }
|
||||
}
|
||||
catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +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 Anthropic from '@anthropic-ai/sdk';
|
||||
import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
|
||||
import { anthropicMaxPossibleTokens } from '../../common/codeSettingsTypes.js';
|
||||
|
||||
export const sendAnthropicMsg: _InternalSendLLMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
||||
const thisConfig = settingsOfProvider.anthropic
|
||||
|
||||
const maxTokens = anthropicMaxPossibleTokens(modelName)
|
||||
if (maxTokens === undefined) {
|
||||
onError({ message: `Please set a value for Max Tokens.`, fullError: null })
|
||||
return
|
||||
}
|
||||
|
||||
const anthropic = new Anthropic({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
|
||||
|
||||
const stream = anthropic.messages.stream({
|
||||
// system: systemMessage,
|
||||
messages: messages,
|
||||
model: modelName,
|
||||
max_tokens: maxTokens,
|
||||
});
|
||||
|
||||
|
||||
// when receive text
|
||||
stream.on('text', (newText, fullText) => {
|
||||
onText({ newText, fullText })
|
||||
})
|
||||
|
||||
// when we get the final message on this stream (or when error/fail)
|
||||
stream.on('finalMessage', (claude_response) => {
|
||||
// stringify the response's content
|
||||
const content = claude_response.content.map(c => c.type === 'text' ? c.text : c.type).join('\n');
|
||||
onFinalMessage({ fullText: content })
|
||||
})
|
||||
|
||||
stream.on('error', (error) => {
|
||||
// the most common error will be invalid API key (401), so we handle this with a nice message
|
||||
if (error instanceof Anthropic.APIError && error.status === 401) {
|
||||
onError({ message: 'Invalid API key.', fullError: error })
|
||||
}
|
||||
else {
|
||||
onError({ message: error + '', fullError: error }) // anthropic errors can be stringified nicely like this
|
||||
}
|
||||
})
|
||||
|
||||
// TODO need to test this to make sure it works, it might throw an error
|
||||
_setAborter(() => stream.controller.abort())
|
||||
|
||||
};
|
||||
@@ -1,43 +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 { Content, GoogleGenerativeAI } from '@google/generative-ai';
|
||||
import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
|
||||
|
||||
// Gemini
|
||||
export const sendGeminiMsg: _InternalSendLLMMessageFnType = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
const thisConfig = settingsOfProvider.gemini
|
||||
|
||||
const genAI = new GoogleGenerativeAI(thisConfig.apiKey);
|
||||
const model = genAI.getGenerativeModel({ model: modelName });
|
||||
|
||||
// Convert messages to Gemini format
|
||||
const geminiMessages: Content[] = messages
|
||||
.map((msg, i) => ({
|
||||
parts: [{ text: msg.content }],
|
||||
role: msg.role === 'assistant' ? 'model' : 'user'
|
||||
}))
|
||||
|
||||
model.generateContentStream({
|
||||
// systemInstruction: systemMessage,
|
||||
contents: geminiMessages,
|
||||
})
|
||||
.then(async response => {
|
||||
_setAborter(() => response.stream.return(fullText))
|
||||
|
||||
for await (const chunk of response.stream) {
|
||||
const newText = chunk.text();
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage({ fullText });
|
||||
})
|
||||
.catch((error) => {
|
||||
onError({ message: error + '', fullError: error })
|
||||
})
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*--------------------------------------------------------------------------------------
|
||||
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
// // Greptile
|
||||
// // https://docs.greptile.com/api-reference/query
|
||||
// // https://docs.greptile.com/quickstart#sample-response-streamed
|
||||
|
||||
// import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js';
|
||||
|
||||
// export const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, _setAborter }) => {
|
||||
|
||||
// let fullText = ''
|
||||
|
||||
// const thisConfig = settingsOfProvider.greptile
|
||||
|
||||
// fetch('https://api.greptile.com/v2/query', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Authorization': `Bearer ${thisConfig.apikey}`,
|
||||
// 'X-Github-Token': `${thisConfig.githubPAT}`,
|
||||
// 'Content-Type': `application/json`,
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// messages,
|
||||
// stream: true,
|
||||
// repositories: [thisConfig.repoinfo],
|
||||
// }),
|
||||
// })
|
||||
// // this is {message}\n{message}\n{message}...\n
|
||||
// .then(async response => {
|
||||
// const text = await response.text()
|
||||
// console.log('got greptile', text)
|
||||
// return JSON.parse(`[${text.trim().split('\n').join(',')}]`)
|
||||
// })
|
||||
// // TODO make this actually stream, right now it just sends one message at the end
|
||||
// // TODO add _setAborter() when add streaming
|
||||
// .then(async responseArr => {
|
||||
|
||||
// for (const response of responseArr) {
|
||||
// const type: string = response['type']
|
||||
// const message = response['message']
|
||||
|
||||
// // when receive text
|
||||
// if (type === 'message') {
|
||||
// fullText += message
|
||||
// onText({ newText: message, fullText })
|
||||
// }
|
||||
// else if (type === 'sources') {
|
||||
// const { filepath, linestart: _, lineend: _2 } = message as { filepath: string; linestart: number | null; lineend: number | null }
|
||||
// fullText += filepath
|
||||
// onText({ newText: filepath, fullText })
|
||||
// }
|
||||
// // type: 'status' with an empty 'message' means last message
|
||||
// else if (type === 'status') {
|
||||
// if (!message) {
|
||||
// onFinalMessage({ fullText })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// })
|
||||
// .catch(error => {
|
||||
// onError({ error })
|
||||
// });
|
||||
|
||||
// }
|
||||
@@ -1,44 +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 Groq from 'groq-sdk';
|
||||
import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
|
||||
|
||||
// Groq
|
||||
export const sendGroqMsg: _InternalSendLLMMessageFnType = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
let fullText = '';
|
||||
|
||||
const thisConfig = settingsOfProvider.groq
|
||||
|
||||
const groq = new Groq({
|
||||
apiKey: thisConfig.apiKey,
|
||||
dangerouslyAllowBrowser: true
|
||||
});
|
||||
|
||||
await groq.chat.completions
|
||||
.create({
|
||||
messages: messages,
|
||||
model: modelName,
|
||||
stream: true,
|
||||
// temperature: 0.7,
|
||||
// max_tokens: parseMaxTokensStr(thisConfig.maxTokens),
|
||||
})
|
||||
.then(async response => {
|
||||
_setAborter(() => response.controller.abort())
|
||||
// when receive text
|
||||
for await (const chunk of response) {
|
||||
const newText = chunk.choices[0]?.delta?.content || '';
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
|
||||
onFinalMessage({ fullText });
|
||||
})
|
||||
.catch(error => {
|
||||
onError({ message: error + '', fullError: error });
|
||||
})
|
||||
|
||||
|
||||
};
|
||||
@@ -1,46 +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 { Mistral } from '@mistralai/mistralai';
|
||||
import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
|
||||
|
||||
// Mistral
|
||||
export const sendMistralMsg: _InternalSendLLMMessageFnType = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
let fullText = '';
|
||||
|
||||
const thisConfig = settingsOfProvider.mistral;
|
||||
|
||||
const mistral = new Mistral({
|
||||
apiKey: thisConfig.apiKey,
|
||||
})
|
||||
|
||||
await mistral.chat
|
||||
.stream({
|
||||
messages: messages,
|
||||
model: modelName,
|
||||
stream: true,
|
||||
// temperature: 0.7,
|
||||
// maxTokens: 2048,
|
||||
})
|
||||
.then(async response => {
|
||||
// Mistral has a really nonstandard API - no interrupt and weird stream types
|
||||
_setAborter(() => { console.log('Mistral does not support interrupts! Further messages will just be ignored.') });
|
||||
// when receive text
|
||||
for await (const chunk of response) {
|
||||
const c = chunk.data.choices[0].delta.content || ''
|
||||
const newText = (
|
||||
typeof c === 'string' ? c
|
||||
: c?.map(c => c.type === 'text' ? c.text : c.type).join('\n')
|
||||
)
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
|
||||
onFinalMessage({ fullText });
|
||||
})
|
||||
.catch(error => {
|
||||
onError({ message: error + '', fullError: error });
|
||||
})
|
||||
}
|
||||
@@ -1,116 +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 { Ollama } from 'ollama';
|
||||
import { _InternalModelListFnType, _InternalOllamaFIMMessageFnType, _InternalSendLLMMessageFnType, OllamaModelResponse } from '../../common/llmMessageTypes.js';
|
||||
import { defaultProviderSettings } from '../../common/codeSettingsTypes.js';
|
||||
|
||||
export const ollamaList: _InternalModelListFnType<OllamaModelResponse> = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider }) => {
|
||||
|
||||
const onSuccess = ({ models }: { models: OllamaModelResponse[] }) => {
|
||||
onSuccess_({ models })
|
||||
}
|
||||
|
||||
const onError = ({ error }: { error: string }) => {
|
||||
onError_({ error })
|
||||
}
|
||||
|
||||
try {
|
||||
const thisConfig = settingsOfProvider.ollama
|
||||
// if endpoint is empty, normally ollama will send to 11434, but we want it to fail - the user should type it in
|
||||
if (!thisConfig.endpoint) throw new Error(`Ollama Endpoint was empty (please enter ${defaultProviderSettings.ollama.endpoint} in Code if you want the default url).`)
|
||||
|
||||
const ollama = new Ollama({ host: thisConfig.endpoint })
|
||||
ollama.list()
|
||||
.then((response) => {
|
||||
const { models } = response
|
||||
onSuccess({ models })
|
||||
})
|
||||
.catch((error) => {
|
||||
onError({ error: error + '' })
|
||||
})
|
||||
}
|
||||
catch (error) {
|
||||
onError({ error: error + '' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const sendOllamaFIM: _InternalOllamaFIMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
||||
const thisConfig = settingsOfProvider.ollama
|
||||
// if endpoint is empty, normally ollama will send to 11434, but we want it to fail - the user should type it in
|
||||
if (!thisConfig.endpoint) throw new Error(`Ollama Endpoint was empty (please enter ${defaultProviderSettings.ollama.endpoint} if you want the default).`)
|
||||
|
||||
let fullText = ''
|
||||
|
||||
const ollama = new Ollama({ host: thisConfig.endpoint })
|
||||
|
||||
ollama.generate({
|
||||
model: modelName,
|
||||
prompt: messages.prefix,
|
||||
suffix: messages.suffix,
|
||||
options: {
|
||||
stop: messages.stopTokens,
|
||||
},
|
||||
raw: true,
|
||||
stream: true,
|
||||
// options: { num_predict: parseMaxTokensStr(thisConfig.maxTokens) } // this is max_tokens
|
||||
})
|
||||
.then(async stream => {
|
||||
_setAborter(() => stream.abort())
|
||||
// iterate through the stream
|
||||
for await (const chunk of stream) {
|
||||
const newText = chunk.response;
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage({ fullText });
|
||||
})
|
||||
// when error/fail
|
||||
.catch((error) => {
|
||||
onError({ message: error + '', fullError: error })
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
// Ollama
|
||||
export const sendOllamaMsg: _InternalSendLLMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
||||
const thisConfig = settingsOfProvider.ollama
|
||||
// if endpoint is empty, normally ollama will send to 11434, but we want it to fail - the user should type it in
|
||||
if (!thisConfig.endpoint) throw new Error(`Ollama Endpoint was empty (please enter ${defaultProviderSettings.ollama.endpoint} if you want the default).`)
|
||||
|
||||
let fullText = ''
|
||||
|
||||
const ollama = new Ollama({ host: thisConfig.endpoint })
|
||||
|
||||
ollama.chat({
|
||||
model: modelName,
|
||||
messages: messages,
|
||||
stream: true,
|
||||
// options: { num_predict: parseMaxTokensStr(thisConfig.maxTokens) } // this is max_tokens
|
||||
})
|
||||
.then(async stream => {
|
||||
_setAborter(() => stream.abort())
|
||||
// iterate through the stream
|
||||
for await (const chunk of stream) {
|
||||
const newText = chunk.message.content;
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage({ fullText });
|
||||
|
||||
})
|
||||
// when error/fail
|
||||
.catch((error) => {
|
||||
onError({ message: error + '', fullError: error })
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ['codestral', 'qwen2.5-coder', 'qwen2.5-coder:0.5b', 'qwen2.5-coder:1.5b', 'qwen2.5-coder:3b', 'qwen2.5-coder:7b', 'qwen2.5-coder:14b', 'qwen2.5-coder:32b', 'codegemma', 'codegemma:2b', 'codegemma:7b', 'codellama', 'codellama:7b', 'codellama:13b', 'codellama:34b', 'codellama:70b', 'codellama:code', 'codellama:python', 'command-r', 'command-r:35b', 'command-r-plus', 'command-r-plus:104b', 'deepseek-coder-v2', 'deepseek-coder-v2:16b', 'deepseek-coder-v2:236b', 'falcon2', 'falcon2:11b', 'firefunction-v2', 'firefunction-v2:70b', 'gemma', 'gemma:2b', 'gemma:7b', 'gemma2', 'gemma2:2b', 'gemma2:9b', 'gemma2:27b', 'llama2', 'llama2:7b', 'llama2:13b', 'llama2:70b', 'llama3', 'llama3:8b', 'llama3:70b', 'llama3-chatqa', 'llama3-chatqa:8b', 'llama3-chatqa:70b', 'llama3-gradient', 'llama3-gradient:8b', 'llama3-gradient:70b', 'llama3.1', 'llama3.1:8b', 'llama3.1:70b', 'llama3.1:405b', 'llava', 'llava:7b', 'llava:13b', 'llava:34b', 'llava-llama3', 'llava-llama3:8b', 'llava-phi3', 'llava-phi3:3.8b', 'mistral', 'mistral:7b', 'mistral-large', 'mistral-large:123b', 'mistral-nemo', 'mistral-nemo:12b', 'mixtral', 'mixtral:8x7b', 'mixtral:8x22b', 'moondream', 'moondream:1.8b', 'openhermes', 'openhermes:v2.5', 'phi3', 'phi3:3.8b', 'phi3:14b', 'phi3.5', 'phi3.5:3.8b', 'qwen', 'qwen:7b', 'qwen:14b', 'qwen:32b', 'qwen:72b', 'qwen:110b', 'qwen2', 'qwen2:0.5b', 'qwen2:1.5b', 'qwen2:7b', 'qwen2:72b', 'smollm', 'smollm:135m', 'smollm:360m', 'smollm:1.7b',]
|
||||
@@ -1,112 +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 OpenAI from 'openai';
|
||||
import { _InternalModelListFnType, _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
|
||||
import { Model } from 'openai/resources/models.js';
|
||||
// import { parseMaxTokensStr } from './util.js';
|
||||
|
||||
|
||||
|
||||
export const openaiCompatibleList: _InternalModelListFnType<Model> = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider }) => {
|
||||
const onSuccess = ({ models }: { models: Model[] }) => {
|
||||
onSuccess_({ models })
|
||||
}
|
||||
|
||||
const onError = ({ error }: { error: string }) => {
|
||||
onError_({ error })
|
||||
}
|
||||
|
||||
try {
|
||||
const thisConfig = settingsOfProvider.openAICompatible
|
||||
const openai = new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true })
|
||||
|
||||
openai.models.list()
|
||||
.then(async (response) => {
|
||||
const models: Model[] = []
|
||||
models.push(...response.data)
|
||||
while (response.hasNextPage()) {
|
||||
models.push(...(await response.getNextPage()).data)
|
||||
}
|
||||
onSuccess({ models })
|
||||
})
|
||||
.catch((error) => {
|
||||
onError({ error: error + '' })
|
||||
})
|
||||
}
|
||||
catch (error) {
|
||||
onError({ error: error + '' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// OpenAI, OpenRouter, OpenAICompatible
|
||||
export const sendOpenAIMsg: _InternalSendLLMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
let openai: OpenAI
|
||||
let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
||||
|
||||
|
||||
if (providerName === 'openAI') {
|
||||
const thisConfig = settingsOfProvider.openAI
|
||||
openai = new OpenAI({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
|
||||
options = { model: modelName, messages: messages, stream: true, /*max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens)*/ }
|
||||
}
|
||||
else if (providerName === 'openRouter') {
|
||||
const thisConfig = settingsOfProvider.openRouter
|
||||
openai = new OpenAI({
|
||||
baseURL: 'https://openrouter.ai/api/v1', apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true,
|
||||
defaultHeaders: {
|
||||
'HTTP-Referer': 'https://code.hanzo.ai', // Optional, for including your app on openrouter.ai rankings.
|
||||
'X-Title': 'Code Editor', // Optional. Shows in rankings on openrouter.ai.
|
||||
},
|
||||
});
|
||||
options = { model: modelName, messages: messages, stream: true, /*max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens)*/ }
|
||||
}
|
||||
else if (providerName === 'deepseek') {
|
||||
const thisConfig = settingsOfProvider.deepseek
|
||||
openai = new OpenAI({
|
||||
baseURL: 'https://api.deepseek.com/v1', apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true,
|
||||
});
|
||||
options = { model: modelName, messages: messages, stream: true, /*max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens)*/ }
|
||||
|
||||
}
|
||||
else if (providerName === 'openAICompatible') {
|
||||
const thisConfig = settingsOfProvider.openAICompatible
|
||||
openai = new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true })
|
||||
options = { model: modelName, messages: messages, stream: true, /*max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens)*/ }
|
||||
}
|
||||
else {
|
||||
console.error(`sendOpenAIMsg: invalid providerName: ${providerName}`)
|
||||
throw new Error(`providerName was invalid: ${providerName}`)
|
||||
}
|
||||
|
||||
openai.chat.completions
|
||||
.create(options)
|
||||
.then(async response => {
|
||||
_setAborter(() => response.controller.abort())
|
||||
// when receive text
|
||||
for await (const chunk of response) {
|
||||
const newText = chunk.choices[0]?.delta?.content || '';
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage({ fullText });
|
||||
})
|
||||
// when error/fail - this catches errors of both .create() and .then(for await)
|
||||
.catch(error => {
|
||||
if (error instanceof OpenAI.APIError && error.status === 401) {
|
||||
onError({ message: 'Invalid API key.', fullError: error });
|
||||
}
|
||||
else {
|
||||
onError({ message: error + '', fullError: error });
|
||||
}
|
||||
})
|
||||
|
||||
};
|
||||
@@ -1,167 +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 { SendLLMMessageParams, OnText, OnFinalMessage, OnError, LLMMessage, _InternalLLMMessage } from '../../common/llmMessageTypes.js';
|
||||
import { IMetricsService } from '../../common/metricsService.js';
|
||||
|
||||
import { sendAnthropicMsg } from './anthropic.js';
|
||||
import { sendOllamaFIM, sendOllamaMsg } from './ollama.js';
|
||||
import { sendOpenAIMsg } from './openai.js';
|
||||
import { sendGeminiMsg } from './gemini.js';
|
||||
import { sendGroqMsg } from './groq.js';
|
||||
import { sendMistralMsg } from './mistral.js';
|
||||
|
||||
|
||||
const cleanMessages = (messages: LLMMessage[]): _InternalLLMMessage[] => {
|
||||
// trim message content (Anthropic and other providers give an error if there is trailing whitespace)
|
||||
messages = messages.map(m => ({ ...m, content: m.content.trim() }))
|
||||
|
||||
// find system messages and concatenate them
|
||||
const systemMessage = messages
|
||||
.filter(msg => msg.role === 'system')
|
||||
.map(msg => msg.content)
|
||||
.join('\n') || undefined;
|
||||
|
||||
// remove all system messages
|
||||
const noSystemMessages = messages
|
||||
.filter(msg => msg.role !== 'system') as _InternalLLMMessage[]
|
||||
|
||||
// add system mesasges to first message (should be a user message)
|
||||
if (systemMessage && (noSystemMessages.length !== 0)) {
|
||||
const newFirstMessage = {
|
||||
role: noSystemMessages[0].role,
|
||||
content: (''
|
||||
+ '<SYSTEM_MESSAGE>\n'
|
||||
+ systemMessage
|
||||
+ '\n'
|
||||
+ '</SYSTEM_MESSAGE>\n'
|
||||
+ noSystemMessages[0].content
|
||||
)
|
||||
}
|
||||
noSystemMessages.splice(0, 1) // delete first message
|
||||
noSystemMessages.unshift(newFirstMessage) // add new first message
|
||||
}
|
||||
|
||||
return noSystemMessages
|
||||
}
|
||||
|
||||
|
||||
export const sendLLMMessage = ({
|
||||
type,
|
||||
aiInstructions,
|
||||
messages: messages_,
|
||||
onText: onText_,
|
||||
onFinalMessage: onFinalMessage_,
|
||||
onError: onError_,
|
||||
abortRef: abortRef_,
|
||||
logging: { loggingName },
|
||||
settingsOfProvider,
|
||||
providerName,
|
||||
modelName,
|
||||
}: SendLLMMessageParams,
|
||||
|
||||
metricsService: IMetricsService
|
||||
) => {
|
||||
// messages.unshift({ role: 'system', content: aiInstructions })
|
||||
|
||||
const messagesArr = type === 'sendLLMMessage' ? cleanMessages(messages_) : []
|
||||
|
||||
// only captures number of messages and message "shape", no actual code, instructions, prompts, etc
|
||||
const captureLLMEvent = (eventId: string, extras?: object) => {
|
||||
metricsService.capture(eventId, {
|
||||
providerName,
|
||||
modelName,
|
||||
...type === 'sendLLMMessage' ? {
|
||||
numMessages: messagesArr?.length,
|
||||
messagesShape: messagesArr?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
||||
origNumMessages: messages_?.length,
|
||||
origMessagesShape: messages_?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
||||
|
||||
} : type === 'ollamaFIM' ? {
|
||||
|
||||
} : {},
|
||||
|
||||
...extras,
|
||||
})
|
||||
}
|
||||
const submit_time = new Date()
|
||||
|
||||
let _fullTextSoFar = ''
|
||||
let _aborter: (() => void) | null = null
|
||||
let _setAborter = (fn: () => void) => { _aborter = fn }
|
||||
let _didAbort = false
|
||||
|
||||
const onText: OnText = ({ newText, fullText }) => {
|
||||
if (_didAbort) return
|
||||
onText_({ newText, fullText })
|
||||
_fullTextSoFar = fullText
|
||||
}
|
||||
|
||||
const onFinalMessage: OnFinalMessage = ({ fullText }) => {
|
||||
if (_didAbort) return
|
||||
captureLLMEvent(`${loggingName} - Received Full Message`, { messageLength: fullText.length, duration: new Date().getMilliseconds() - submit_time.getMilliseconds() })
|
||||
onFinalMessage_({ fullText })
|
||||
}
|
||||
|
||||
const onError: OnError = ({ message: error, fullError }) => {
|
||||
if (_didAbort) return
|
||||
console.error('sendLLMMessage onError:', error)
|
||||
captureLLMEvent(`${loggingName} - Error`, { error })
|
||||
onError_({ message: error, fullError })
|
||||
}
|
||||
|
||||
const onAbort = () => {
|
||||
captureLLMEvent(`${loggingName} - Abort`, { messageLengthSoFar: _fullTextSoFar.length })
|
||||
try { _aborter?.() } // aborter sometimes automatically throws an error
|
||||
catch (e) { }
|
||||
_didAbort = true
|
||||
}
|
||||
abortRef_.current = onAbort
|
||||
|
||||
captureLLMEvent(`${loggingName} - Sending Message`, { messageLength: messagesArr[messagesArr.length - 1]?.content.length })
|
||||
|
||||
try {
|
||||
switch (providerName) {
|
||||
case 'anthropic':
|
||||
sendAnthropicMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'openAI':
|
||||
case 'openRouter':
|
||||
case 'deepseek':
|
||||
case 'openAICompatible':
|
||||
sendOpenAIMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'gemini':
|
||||
sendGeminiMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'ollama':
|
||||
if (type === 'ollamaFIM')
|
||||
sendOllamaFIM({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName })
|
||||
else
|
||||
sendOllamaMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'groq':
|
||||
sendGroqMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'mistral':
|
||||
sendMistralMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
default:
|
||||
onError({ message: `Error: Code provider was "${providerName}", which is not recognized.`, fullError: null })
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
if (error instanceof Error) { onError({ message: error + '', fullError: error }) }
|
||||
else { onError({ message: `Unexpected Error in sendLLMMessage: ${error}`, fullError: error }); }
|
||||
// ; (_aborter as any)?.()
|
||||
// _didAbort = true
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
/*--------------------------------------------------------------------------------------
|
||||
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
// registered in app.ts
|
||||
// code convention is to make a service responsible for this stuff, and not a channel, but having fewer files is simpler...
|
||||
|
||||
import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainSendLLMMessageParams, AbortRef, SendLLMMessageParams, MainLLMMessageAbortParams, MainModelListParams, ModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from '../common/llmMessageTypes.js';
|
||||
import { sendLLMMessage } from './llmMessage/sendLLMMessage.js'
|
||||
import { IMetricsService } from '../common/metricsService.js';
|
||||
import { ollamaList } from './llmMessage/ollama.js';
|
||||
import { openaiCompatibleList } from './llmMessage/openai.js';
|
||||
|
||||
// NODE IMPLEMENTATION - calls actual sendLLMMessage() and returns listeners to it
|
||||
|
||||
export class LLMMessageChannel implements IServerChannel {
|
||||
// sendLLMMessage
|
||||
private readonly _onText_llm = new Emitter<EventLLMMessageOnTextParams>();
|
||||
private readonly _onFinalMessage_llm = new Emitter<EventLLMMessageOnFinalMessageParams>();
|
||||
private readonly _onError_llm = new Emitter<EventLLMMessageOnErrorParams>();
|
||||
|
||||
// abort
|
||||
private readonly _abortRefOfRequestId_llm: Record<string, AbortRef> = {}
|
||||
|
||||
// ollamaList
|
||||
private readonly _onSuccess_ollama = new Emitter<EventModelListOnSuccessParams<OllamaModelResponse>>();
|
||||
private readonly _onError_ollama = new Emitter<EventModelListOnErrorParams<OllamaModelResponse>>();
|
||||
|
||||
// openaiCompatibleList
|
||||
private readonly _onSuccess_openAICompatible = new Emitter<EventModelListOnSuccessParams<OpenaiCompatibleModelResponse>>();
|
||||
private readonly _onError_openAICompatible = new Emitter<EventModelListOnErrorParams<OpenaiCompatibleModelResponse>>();
|
||||
|
||||
// stupidly, channels can't take in @IService
|
||||
constructor(
|
||||
private readonly metricsService: IMetricsService,
|
||||
) { }
|
||||
|
||||
// browser uses this to listen for changes
|
||||
listen(_: unknown, event: string): Event<any> {
|
||||
if (event === 'onText_llm') {
|
||||
return this._onText_llm.event;
|
||||
}
|
||||
else if (event === 'onFinalMessage_llm') {
|
||||
return this._onFinalMessage_llm.event;
|
||||
}
|
||||
else if (event === 'onError_llm') {
|
||||
return this._onError_llm.event;
|
||||
}
|
||||
else if (event === 'onSuccess_ollama') {
|
||||
return this._onSuccess_ollama.event;
|
||||
}
|
||||
else if (event === 'onError_ollama') {
|
||||
return this._onError_ollama.event;
|
||||
}
|
||||
else if (event === 'onSuccess_openAICompatible') {
|
||||
return this._onSuccess_openAICompatible.event;
|
||||
}
|
||||
else if (event === 'onError_openAICompatible') {
|
||||
return this._onError_openAICompatible.event;
|
||||
}
|
||||
else {
|
||||
throw new Error(`Event not found: ${event}`);
|
||||
}
|
||||
}
|
||||
|
||||
// browser uses this to call
|
||||
async call(_: unknown, command: string, params: any): Promise<any> {
|
||||
try {
|
||||
if (command === 'sendLLMMessage') {
|
||||
this._callSendLLMMessage(params)
|
||||
}
|
||||
else if (command === 'abort') {
|
||||
this._callAbort(params)
|
||||
}
|
||||
else if (command === 'ollamaList') {
|
||||
this._callOllamaList(params)
|
||||
}
|
||||
else if (command === 'openAICompatibleList') {
|
||||
this._callOpenAICompatibleList(params)
|
||||
}
|
||||
else {
|
||||
throw new Error(`Code sendLLM: command "${command}" not recognized.`)
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log('llmMessageChannel: Call Error:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// the only place sendLLMMessage is actually called
|
||||
private async _callSendLLMMessage(params: MainSendLLMMessageParams) {
|
||||
const { requestId } = params;
|
||||
|
||||
if (!(requestId in this._abortRefOfRequestId_llm))
|
||||
this._abortRefOfRequestId_llm[requestId] = { current: null }
|
||||
|
||||
const mainThreadParams: SendLLMMessageParams = {
|
||||
...params,
|
||||
onText: ({ newText, fullText }) => { this._onText_llm.fire({ requestId, newText, fullText }); },
|
||||
onFinalMessage: ({ fullText }) => { this._onFinalMessage_llm.fire({ requestId, fullText }); },
|
||||
onError: ({ message: error, fullError }) => { console.log('sendLLM: firing err'); this._onError_llm.fire({ requestId, message: error, fullError }); },
|
||||
abortRef: this._abortRefOfRequestId_llm[requestId],
|
||||
}
|
||||
sendLLMMessage(mainThreadParams, this.metricsService);
|
||||
}
|
||||
|
||||
private _callAbort(params: MainLLMMessageAbortParams) {
|
||||
const { requestId } = params;
|
||||
if (!(requestId in this._abortRefOfRequestId_llm)) return
|
||||
this._abortRefOfRequestId_llm[requestId].current?.()
|
||||
delete this._abortRefOfRequestId_llm[requestId]
|
||||
}
|
||||
|
||||
private _callOllamaList(params: MainModelListParams<OllamaModelResponse>) {
|
||||
const { requestId } = params;
|
||||
|
||||
const mainThreadParams: ModelListParams<OllamaModelResponse> = {
|
||||
...params,
|
||||
onSuccess: ({ models }) => { this._onSuccess_ollama.fire({ requestId, models }); },
|
||||
onError: ({ error }) => { this._onError_ollama.fire({ requestId, error }); },
|
||||
}
|
||||
ollamaList(mainThreadParams)
|
||||
}
|
||||
|
||||
private _callOpenAICompatibleList(params: MainModelListParams<OpenaiCompatibleModelResponse>) {
|
||||
const { requestId } = params;
|
||||
|
||||
const mainThreadParams: ModelListParams<OpenaiCompatibleModelResponse> = {
|
||||
...params,
|
||||
onSuccess: ({ models }) => { this._onSuccess_openAICompatible.fire({ requestId, models }); },
|
||||
onError: ({ error }) => { this._onError_openAICompatible.fire({ requestId, error }); },
|
||||
}
|
||||
openaiCompatibleList(mainThreadParams)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export class EncryptionMainService implements IEncryptionMainService {
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
|
||||
// Void added this as a nice default for linux so you don't need to specify encryption provider
|
||||
// Code added this as a nice default for linux so you don't need to specify encryption provider
|
||||
if (isLinux && !app.commandLine.getSwitchValue('password-store')) {
|
||||
this.logService.trace('[EncryptionMainService] No password-store switch, defaulting to basic...');
|
||||
app.commandLine.appendSwitch('password-store', PasswordStoreCLIOption.basic);
|
||||
|
||||
@@ -410,7 +410,6 @@ function isVersionValid(currentVersion: string, date: ProductDate, requestedVers
|
||||
}
|
||||
|
||||
if (!isValidVersion(currentVersion, date, desiredVersion)) {
|
||||
// Code - ignore not compatible
|
||||
notices.push(nls.localize('versionMismatch', "Extension is not compatible with Code {0}. Extension requires: {1}.", currentVersion, requestedVersion));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -31,29 +31,25 @@ export class ServerTelemetryService extends TelemetryService implements IServerT
|
||||
}
|
||||
|
||||
override publicLog(eventName: string, data?: ITelemetryData) {
|
||||
// Code commented this out
|
||||
// if (this._injectedTelemetryLevel < TelemetryLevel.USAGE) {
|
||||
// return;
|
||||
// }
|
||||
// return super.publicLog(eventName, data);
|
||||
if (this._injectedTelemetryLevel < TelemetryLevel.USAGE) {
|
||||
return;
|
||||
}
|
||||
return super.publicLog(eventName, data);
|
||||
}
|
||||
|
||||
override publicLog2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>) {
|
||||
// Code commented this out
|
||||
// return this.publicLog(eventName, data as ITelemetryData | undefined);
|
||||
return this.publicLog(eventName, data as ITelemetryData | undefined);
|
||||
}
|
||||
|
||||
override publicLogError(errorEventName: string, data?: ITelemetryData) {
|
||||
// Code commented this out
|
||||
// if (this._injectedTelemetryLevel < TelemetryLevel.ERROR) {
|
||||
// return Promise.resolve(undefined);
|
||||
// }
|
||||
// return super.publicLogError(errorEventName, data);
|
||||
if (this._injectedTelemetryLevel < TelemetryLevel.ERROR) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
return super.publicLogError(errorEventName, data);
|
||||
}
|
||||
|
||||
override publicLogError2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>) {
|
||||
// Code commented this out
|
||||
// return this.publicLogError(eventName, data as ITelemetryData | undefined);
|
||||
return this.publicLogError(eventName, data as ITelemetryData | undefined);
|
||||
}
|
||||
|
||||
async updateInjectedTelemetryLevel(telemetryLevel: TelemetryLevel): Promise<void> {
|
||||
|
||||
@@ -121,12 +121,11 @@ export class TelemetryService implements ITelemetryService {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
// Code commented this out
|
||||
private _log(eventName: string, eventLevel: TelemetryLevel, data?: ITelemetryData) {
|
||||
// // don't send events when the user is optout
|
||||
// if (this._telemetryLevel < eventLevel) {
|
||||
// return;
|
||||
// }
|
||||
// don't send events when the user is optout
|
||||
if (this._telemetryLevel < eventLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add experiment properties
|
||||
data = mixin(data, this._experimentProperties);
|
||||
@@ -150,18 +149,16 @@ export class TelemetryService implements ITelemetryService {
|
||||
}
|
||||
|
||||
publicLogError(errorEventName: string, data?: ITelemetryData) {
|
||||
// Code commented this out
|
||||
// if (!this._sendErrorTelemetry) {
|
||||
// return;
|
||||
// }
|
||||
if (!this._sendErrorTelemetry) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send error event and anonymize paths
|
||||
this._log(errorEventName, TelemetryLevel.ERROR, data);
|
||||
}
|
||||
|
||||
publicLogError2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>) {
|
||||
// Code commented this out
|
||||
// this.publicLogError(eventName, data as ITelemetryData);
|
||||
this.publicLogError(eventName, data as ITelemetryData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +170,7 @@ function getTelemetryLevelSettingDescription(): string {
|
||||
const restartString = !isWeb ? localize('telemetry.restart', 'Microsoft says \"Some third party extensions might not respect this setting. Consult the specific extension\'s documentation to be sure. A full restart of the application is necessary for crash reporting changes to take effect.\"') : '';
|
||||
|
||||
|
||||
// Void removed these
|
||||
// Code removed these
|
||||
// const crashReportsHeader = localize('telemetry.crashReports', "Crash Reports");
|
||||
// const errorsHeader = localize('telemetry.errors', "Error Telemetry");
|
||||
// const usageHeader = localize('telemetry.usage', "Usage Data");
|
||||
@@ -194,7 +191,7 @@ ${telemetryText}
|
||||
|
||||
${restartString}
|
||||
|
||||
Void separately records basic usage like the number of messages people are sending. If you'd like to disable Void metrics, you may do so in Void's Settings.
|
||||
Code separately records basic usage like the number of messages people are sending. If you'd like to disable Code metrics, you may do so in Code's Settings.
|
||||
`;
|
||||
|
||||
return telemetryDescription;
|
||||
@@ -251,4 +248,3 @@ configurationRegistry.registerConfiguration({
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -15,10 +15,7 @@ import { IRequestService } from '../../request/common/request.js';
|
||||
import { AvailableForDownload, DisablementReason, IUpdateService, State, StateType, UpdateType } from '../common/update.js';
|
||||
|
||||
export function createUpdateURL(platform: string, quality: string, productService: IProductService): string {
|
||||
// return `https://code.hanzo.ai/api/update/${platform}/stable`;
|
||||
// return `${productService.updateUrl}/api/update/${platform}/${quality}/${productService.commit}`;
|
||||
// https://github.com/VSCodium/update-api
|
||||
return `https://updates.code.hanzo.ai/api/update/${platform}/${quality}/${productService.commit}`;
|
||||
return `${productService.updateUrl}/api/update/${platform}/${quality}/${productService.commit}`;
|
||||
}
|
||||
|
||||
export type UpdateErrorClassification = {
|
||||
@@ -108,7 +105,10 @@ export abstract class AbstractUpdateService implements IUpdateService {
|
||||
|
||||
this.setState(State.Idle(this.getUpdateType()));
|
||||
|
||||
// Code - temporarily disabled while we figure out how to do this the right way
|
||||
if (updateMode === 'manual') {
|
||||
this.logService.info('update#ctor - manual checks only; automatic updates are disabled by user preference');
|
||||
return;
|
||||
}
|
||||
|
||||
if (updateMode === 'start') {
|
||||
this.logService.info('update#ctor - startup checks only; automatic updates are disabled by user preference');
|
||||
|
||||
@@ -88,9 +88,8 @@ import './mainThreadShare.js';
|
||||
import './mainThreadProfileContentHandlers.js';
|
||||
import './mainThreadAiRelatedInformation.js';
|
||||
import './mainThreadAiEmbeddingVector.js';
|
||||
|
||||
// Code added this:
|
||||
import './mainThreadInlineDiff.js';
|
||||
import './mainThreadMcp.js';
|
||||
import './mainThreadChatStatus.js';
|
||||
|
||||
export class ExtensionPoints implements IWorkbenchContribution {
|
||||
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
// Code created this file
|
||||
// it comes from mainThreadCodeInsets.ts
|
||||
|
||||
import { Disposable } from '../../../base/common/lifecycle.js';
|
||||
import { ICodeEditorService } from '../../../editor/browser/services/codeEditorService.js';
|
||||
import { MainContext, MainThreadInlineDiffShape } from '../common/extHost.protocol.js';
|
||||
import { IInlineDiffService } from '../../../editor/browser/services/inlineDiffService/inlineDiffService.js';
|
||||
import { ICodeEditor } from '../../../editor/browser/editorBrowser.js';
|
||||
import { IRange } from '../../../editor/common/core/range.js';
|
||||
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
|
||||
import { IUndoRedoElement, IUndoRedoService, UndoRedoElementType, UndoRedoGroup } from '../../../platform/undoRedo/common/undoRedo.js';
|
||||
import { IBulkEditService } from '../../../editor/browser/services/bulkEditService.js';
|
||||
import { WorkspaceEdit } from '../../../editor/common/languages.js';
|
||||
// import { IHistoryService } from '../../services/history/common/history.js';
|
||||
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadInlineDiff)
|
||||
export class MainThreadInlineDiff extends Disposable implements MainThreadInlineDiffShape {
|
||||
|
||||
// private readonly _proxy: ExtHostEditorInsetsShape;
|
||||
// private readonly _disposables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
context: IExtHostContext,
|
||||
@IInlineDiffService private readonly _inlineDiff: IInlineDiffService,
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||
// @IHistoryService private readonly _historyService: IHistoryService, // history service is the history of pressing alt left/right
|
||||
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService, // undoRedo service is the history of pressing ctrl+z
|
||||
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
|
||||
|
||||
) {
|
||||
super();
|
||||
|
||||
// this._proxy = context.getProxy(ExtHostContext.ExtHostEditorInsets);
|
||||
// this._wcHistoryService.addEntry()
|
||||
}
|
||||
|
||||
_streamingState: { type: 'streaming'; editGroup: UndoRedoGroup } | { type: 'idle' } = { type: 'idle' }
|
||||
|
||||
startStreaming(editorId: string) {
|
||||
const editor = this._getEditor(editorId)
|
||||
if (!editor) return
|
||||
|
||||
const model = editor.getModel()
|
||||
if (!model) return
|
||||
|
||||
// all changes made when streaming should be a part of the group so we can undo them all together
|
||||
this._streamingState = {
|
||||
type: 'streaming',
|
||||
editGroup: new UndoRedoGroup()
|
||||
}
|
||||
|
||||
// TODO probably need to convert this to a stack
|
||||
const diffsSnapshotBefore = { placeholder: '' }
|
||||
const diffsSnapshotAfter = { placeholder: '' }
|
||||
|
||||
const elt: IUndoRedoElement = {
|
||||
type: UndoRedoElementType.Resource,
|
||||
resource: model.uri,
|
||||
label: 'Add Diffs',
|
||||
code: 'undoredo.inlineDiff',
|
||||
undo: () => {
|
||||
// reapply diffareas and diffs here
|
||||
console.log('reverting diffareas...', diffsSnapshotBefore.placeholder)
|
||||
},
|
||||
redo: () => {
|
||||
// reapply diffareas and diffs here
|
||||
// when done, need to record diffSnapshotAfter
|
||||
console.log('re-applying diffareas...', diffsSnapshotAfter.placeholder)
|
||||
}
|
||||
}
|
||||
|
||||
this._undoRedoService.pushElement(elt, this._streamingState.editGroup)
|
||||
|
||||
// ---------- START ----------
|
||||
editor.updateOptions({ readOnly: true })
|
||||
|
||||
|
||||
|
||||
// ---------- WHEN DONE ----------
|
||||
editor.updateOptions({ readOnly: false })
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
streamChange(editorId: string, edit: WorkspaceEdit) {
|
||||
const editor = this._getEditor(editorId)
|
||||
if (!editor) return
|
||||
|
||||
if (this._streamingState.type !== 'streaming') {
|
||||
console.error('Expected streamChange to be in state \'streaming\'.')
|
||||
return
|
||||
}
|
||||
|
||||
// count all changes towards the group
|
||||
this._bulkEditService.apply(edit, { undoRedoGroupId: this._streamingState.editGroup.id, })
|
||||
|
||||
|
||||
}
|
||||
|
||||
_getEditor = (editorId: string): ICodeEditor | undefined => {
|
||||
|
||||
let editor: ICodeEditor | undefined;
|
||||
editorId = editorId.substr(0, editorId.indexOf(',')); //todo@jrieken HACK
|
||||
|
||||
for (const candidate of this._editorService.listCodeEditors()) {
|
||||
if (candidate.getId() === editorId
|
||||
// && candidate.hasModel() && isEqual(candidate.getModel().uri, URI.revive(uri))
|
||||
) {
|
||||
editor = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return editor
|
||||
}
|
||||
|
||||
|
||||
$addDiff(editorId: string, originalText: string, range: IRange): void {
|
||||
|
||||
const editor = this._getEditor(editorId);
|
||||
if (!editor) return
|
||||
|
||||
this._inlineDiff.addDiff(editor, originalText, range)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -221,9 +221,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostStatusBar = rpcProtocol.set(ExtHostContext.ExtHostStatusBar, new ExtHostStatusBar(rpcProtocol, extHostCommands.converter));
|
||||
const extHostSpeech = rpcProtocol.set(ExtHostContext.ExtHostSpeech, new ExtHostSpeech(rpcProtocol));
|
||||
const extHostEmbeddings = rpcProtocol.set(ExtHostContext.ExtHostEmbeddings, new ExtHostEmbeddings(rpcProtocol));
|
||||
|
||||
// Code added this:
|
||||
const extHostInlineDiff = rpcProtocol.set(ExtHostContext.ExtHostInlineDiff, new ExtHostInlineDiff(rpcProtocol.getProxy(MainContext.MainThreadInlineDiff), extHostEditors));
|
||||
rpcProtocol.set(ExtHostContext.ExtHostMcp, accessor.get(IExtHostMpcService));
|
||||
|
||||
// Check that no named customers are missing
|
||||
const expected = Object.values<ProxyIdentifier<any>>(ExtHostContext);
|
||||
@@ -558,17 +556,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
|
||||
},
|
||||
|
||||
// Code added addInlineDiff here:
|
||||
addInlineDiff(editor: vscode.TextEditor, originalText: string, modifiedRange: vscode.Range): void {
|
||||
extHostInlineDiff.addDiff(editor, originalText, modifiedRange)
|
||||
},
|
||||
|
||||
// Code added this (I think will need to add this back when add ctrl+K)
|
||||
// registerCodeCtrlKProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
|
||||
// return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
|
||||
// },
|
||||
|
||||
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
|
||||
},
|
||||
@@ -838,7 +825,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
return extHostWebviewPanels.createWebviewPanel(extension, viewType, title, showOptions, options);
|
||||
},
|
||||
createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options?: vscode.WebviewOptions): vscode.WebviewEditorInset {
|
||||
// checkProposedApiEnabled(extension, 'editorInsets'); // Code commented this out
|
||||
checkProposedApiEnabled(extension, 'editorInsets');
|
||||
return extHostEditorInsets.createWebviewEditorInset(editor, line, height, options, extension);
|
||||
},
|
||||
createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.ExtensionTerminalOptions | string, shellPath?: string, shellArgs?: readonly string[] | string): vscode.Terminal {
|
||||
|
||||
@@ -3145,9 +3145,7 @@ export const MainContext = {
|
||||
MainThreadMcp: createProxyIdentifier<MainThreadMcpShape>('MainThreadMcpShape'),
|
||||
MainThreadAiRelatedInformation: createProxyIdentifier<MainThreadAiRelatedInformationShape>('MainThreadAiRelatedInformation'),
|
||||
MainThreadAiEmbeddingVector: createProxyIdentifier<MainThreadAiEmbeddingVectorShape>('MainThreadAiEmbeddingVector'),
|
||||
|
||||
// Code added this:
|
||||
MainThreadInlineDiff: createProxyIdentifier<MainThreadInlineDiffShape>('MainThreadInlineDiff'),
|
||||
MainThreadChatStatus: createProxyIdentifier<MainThreadChatStatusShape>('MainThreadChatStatus'),
|
||||
};
|
||||
|
||||
export const ExtHostContext = {
|
||||
@@ -3218,16 +3216,5 @@ export const ExtHostContext = {
|
||||
ExtHostTesting: createProxyIdentifier<ExtHostTestingShape>('ExtHostTesting'),
|
||||
ExtHostTelemetry: createProxyIdentifier<ExtHostTelemetryShape>('ExtHostTelemetry'),
|
||||
ExtHostLocalization: createProxyIdentifier<ExtHostLocalizationShape>('ExtHostLocalization'),
|
||||
|
||||
// Code added this:
|
||||
ExtHostInlineDiff: createProxyIdentifier<ExtHostInlineDiffShape>('ExtHostInlineDiff'), // Code added this
|
||||
ExtHostMcp: createProxyIdentifier<ExtHostMcpShape>('ExtHostMcp'),
|
||||
};
|
||||
|
||||
|
||||
// Code added these:
|
||||
export interface ExtHostInlineDiffShape {
|
||||
$onDidDispose(handle: number): void;
|
||||
}
|
||||
export interface MainThreadInlineDiffShape {
|
||||
$addDiff(editorId: string, originalText: string, range: IRange): void;
|
||||
}
|
||||
|
||||
@@ -304,16 +304,6 @@ const newCommands: ApiCommand[] = [
|
||||
})(value);
|
||||
})
|
||||
),
|
||||
// // --- Code code lens
|
||||
// new ApiCommand(
|
||||
// 'vscode.executeCodeCodeLensProvider', '_executeCodeCodeLensProvider', 'Execute Code code lens provider.',
|
||||
// [ApiCommandArgument.Uri, ApiCommandArgument.Number.with('itemResolveCount', 'Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)').optional()],
|
||||
// new ApiCommandResult<languages.CodeLens[], vscode.CodeLens[] | undefined>('A promise that resolves to an array of CodeCodeLens-instances.', (value, _args, converter) => {
|
||||
// return tryMapWith<languages.CodeLens, vscode.CodeLens>(item => {
|
||||
// return new types.CodeLens(typeConverters.Range.to(item.range), item.command && converter.fromInternal(item.command));
|
||||
// })(value);
|
||||
// })
|
||||
// ),
|
||||
// --- code actions
|
||||
new ApiCommand(
|
||||
'vscode.executeCodeActionProvider', '_executeCodeActionProvider', 'Execute code action provider.',
|
||||
@@ -445,7 +435,7 @@ const newCommands: ApiCommand[] = [
|
||||
).optional(),
|
||||
ApiCommandArgument.String.with('label', '').optional()
|
||||
],
|
||||
ApiCommandResult.Code
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.openWith', '_workbench.openWith', 'Opens the provided resource with a specific editor.',
|
||||
@@ -457,7 +447,7 @@ const newCommands: ApiCommand[] = [
|
||||
v => !v ? v : typeof v === 'number' ? [typeConverters.ViewColumn.from(v), undefined] : [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)],
|
||||
).optional()
|
||||
],
|
||||
ApiCommandResult.Code
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.diff', '_workbench.diff', 'Opens the provided resources in the diff editor to compare their contents.',
|
||||
@@ -470,7 +460,7 @@ const newCommands: ApiCommand[] = [
|
||||
v => v && [typeConverters.ViewColumn.from(v.viewColumn), typeConverters.TextEditorOpenOptions.from(v)]
|
||||
).optional(),
|
||||
],
|
||||
ApiCommandResult.Code
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.changes', '_workbench.changes', 'Opens a list of resources in the changes editor to compare their contents.',
|
||||
@@ -495,7 +485,7 @@ const newCommands: ApiCommand[] = [
|
||||
},
|
||||
v => v)
|
||||
],
|
||||
ApiCommandResult.Code
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
// --- type hierarchy
|
||||
new ApiCommand(
|
||||
@@ -517,7 +507,7 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommand(
|
||||
'vscode.revealTestInExplorer', '_revealTestInExplorer', 'Reveals a test instance in the explorer',
|
||||
[ApiCommandArgument.TestItem],
|
||||
ApiCommandResult.Code
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
new ApiCommand(
|
||||
'vscode.startContinuousTestRun', 'testing.startContinuousRunFromExtension', 'Starts running the given tests with continuous run mode.',
|
||||
@@ -533,7 +523,7 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommand(
|
||||
'vscode.experimental.editSession.continue', '_workbench.editSessions.actions.continueEditSession', 'Continue the current edit session in a different workspace',
|
||||
[ApiCommandArgument.Uri.with('workspaceUri', 'The target workspace to continue the current edit session in')],
|
||||
ApiCommandResult.Code
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
// --- context keys
|
||||
new ApiCommand(
|
||||
@@ -542,7 +532,7 @@ const newCommands: ApiCommand[] = [
|
||||
ApiCommandArgument.String.with('name', 'The context key name'),
|
||||
new ApiCommandArgument('value', 'The context key value', () => true, v => v),
|
||||
],
|
||||
ApiCommandResult.Code
|
||||
ApiCommandResult.Void
|
||||
),
|
||||
// --- inline chat
|
||||
new ApiCommand(
|
||||
@@ -561,7 +551,7 @@ const newCommands: ApiCommand[] = [
|
||||
position: v.position ? typeConverters.Position.from(v.position) : undefined,
|
||||
};
|
||||
})],
|
||||
ApiCommandResult.Code
|
||||
ApiCommandResult.Void
|
||||
)
|
||||
];
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
||||
// converted to their internal command and don't need
|
||||
// any indirection commands
|
||||
const candidate = this._apiCommands.get(id);
|
||||
return candidate?.result === ApiCommandResult.Code
|
||||
return candidate?.result === ApiCommandResult.Void
|
||||
? candidate : undefined;
|
||||
},
|
||||
logService
|
||||
@@ -483,7 +483,7 @@ export class ApiCommandArgument<V, O = V> {
|
||||
|
||||
export class ApiCommandResult<V, O = V> {
|
||||
|
||||
static readonly Code = new ApiCommandResult<void, void>('no result', v => v);
|
||||
static readonly Void = new ApiCommandResult<void, void>('no result', v => v);
|
||||
|
||||
constructor(
|
||||
readonly description: string,
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// This file was created by Code
|
||||
// reference extHostCodeInsets.ts
|
||||
|
||||
import { Emitter } from '../../../base/common/event.js';
|
||||
import { DisposableStore } from '../../../base/common/lifecycle.js';
|
||||
import { ExtHostInlineDiffShape, MainThreadInlineDiffShape } from './extHost.protocol.js';
|
||||
import * as vscode from 'vscode'
|
||||
import { ExtHostTextEditor } from './extHostTextEditor.js';
|
||||
import { ExtHostEditors } from './extHostTextEditors.js';
|
||||
import { Range } from '../../../workbench/api/common/extHostTypeConverters.js'
|
||||
|
||||
export class ExtHostInlineDiff implements ExtHostInlineDiffShape {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
private _insets = new Map<number, { editor: vscode.TextEditor; inset: vscode.WebviewEditorInset; onDidReceiveMessage: Emitter<any> }>();
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadInlineDiffShape,
|
||||
private readonly _editors: ExtHostEditors,
|
||||
) { }
|
||||
|
||||
|
||||
dispose(): void {
|
||||
this._insets.forEach(value => value.inset.dispose());
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
|
||||
addDiff(editor: vscode.TextEditor, originalText: string, modifiedRange: vscode.Range) {
|
||||
|
||||
let apiEditor: ExtHostTextEditor | undefined;
|
||||
for (const candidate of this._editors.getVisibleTextEditors(true)) {
|
||||
if (candidate.value === editor) {
|
||||
apiEditor = <ExtHostTextEditor>candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!apiEditor) {
|
||||
throw new Error('not a visible editor');
|
||||
|
||||
}
|
||||
// can't send over the editor, so just send over its id and reconstruct it. This is stupid but it's what VSCode's editorinset does - Andrew
|
||||
const id = apiEditor.id;
|
||||
// let uri = apiEditor.value.document.uri;
|
||||
|
||||
// convert to IRange
|
||||
const range = Range.from(modifiedRange)
|
||||
|
||||
this._proxy.$addDiff(id, originalText, range)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// main thread calls this when disposes diff with this particular handle
|
||||
$onDidDispose(handle: number): void {
|
||||
const value = this._insets.get(handle);
|
||||
if (value) {
|
||||
value.inset.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -89,7 +89,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
|
||||
return v;
|
||||
})
|
||||
],
|
||||
ApiCommandResult.Code);
|
||||
ApiCommandResult.Void);
|
||||
|
||||
const requestKernelVariablesApiCommand = new ApiCommand(
|
||||
'vscode.executeNotebookVariableProvider',
|
||||
|
||||
@@ -35,13 +35,14 @@ export class ToggleAuxiliaryBarAction extends Action2 {
|
||||
title: ToggleAuxiliaryBarAction.LABEL,
|
||||
toggled: {
|
||||
condition: AuxiliaryBarVisibleContext,
|
||||
title: localize('secondary sidebar', "Code Side Bar"),
|
||||
title: localize('closeSecondarySideBar', 'Hide Code Side Bar'),
|
||||
icon: closeIcon,
|
||||
mnemonicTitle: localize({ key: 'secondary sidebar mnemonic', comment: ['&& denotes a mnemonic'] }, "Secondary Si&&de Bar"),
|
||||
},
|
||||
icon: closeIcon, // Ensures no flickering when using toggled.icon
|
||||
category: Categories.View,
|
||||
metadata: {
|
||||
description: localize('openAndCloseAuxiliaryBar', 'Open/Show and Close/Hide Void Side Bar'),
|
||||
description: localize('openAndCloseAuxiliaryBar', 'Open/Show and Close/Hide Code Side Bar'),
|
||||
},
|
||||
f1: true,
|
||||
keybinding: {
|
||||
@@ -80,7 +81,7 @@ registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.closeAuxiliaryBar',
|
||||
title: localize2('closeSecondarySideBar', 'Hide Void Side Bar'),
|
||||
title: localize2('closeSecondarySideBar', 'Hide Code Side Bar'),
|
||||
category: Categories.View,
|
||||
precondition: AuxiliaryBarVisibleContext,
|
||||
f1: true,
|
||||
@@ -165,7 +166,7 @@ registerAction2(class extends SwitchCompositeViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.previousAuxiliaryBarView',
|
||||
title: localize2('previousAuxiliaryBarView', 'Previous Void Side Bar View'),
|
||||
title: localize2('previousAuxiliaryBarView', 'Previous Code Side Bar View'),
|
||||
category: Categories.View,
|
||||
f1: true
|
||||
}, ViewContainerLocation.AuxiliaryBar, -1);
|
||||
@@ -176,7 +177,7 @@ registerAction2(class extends SwitchCompositeViewAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.nextAuxiliaryBarView',
|
||||
title: localize2('nextAuxiliaryBarView', 'Next Void Side Bar View'),
|
||||
title: localize2('nextAuxiliaryBarView', 'Next Code Side Bar View'),
|
||||
category: Categories.View,
|
||||
f1: true
|
||||
}, ViewContainerLocation.AuxiliaryBar, 1);
|
||||
|
||||
@@ -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; // Code changed this (was 170)
|
||||
override readonly minimumWidth: number = 280; // Code changed this (was 170)
|
||||
override readonly maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
override readonly minimumHeight: number = 0;
|
||||
override readonly maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
@@ -233,6 +233,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart {
|
||||
new Separator(),
|
||||
new SubmenuAction('workbench.action.panel.position', localize('activity bar position', "Activity Bar Position"), positionActions),
|
||||
toAction({ id: ToggleSidebarPositionAction.ID, label: currentPositionRight ? localize('move second side bar left', "Move Code Side Bar Left") : localize('move second side bar right', "Move Code Side Bar Right"), run: () => this.commandService.executeCommand(ToggleSidebarPositionAction.ID) }),
|
||||
toggleShowLabelsAction,
|
||||
toAction({ id: ToggleAuxiliaryBarAction.ID, label: localize('hide second side bar', "Hide Code Side Bar"), run: () => this.commandService.executeCommand(ToggleAuxiliaryBarAction.ID) })
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 16px;
|
||||
background-image: url('../../../../browser/media/code-icon-sm.png');
|
||||
background-image: url('../../../../browser/media/code-icon-sm.png'); /* // Code */
|
||||
width: 16px;
|
||||
padding: 0;
|
||||
margin: 0 6px 0 10px;
|
||||
|
||||
@@ -17,12 +17,15 @@ import { editorForeground, registerColor, transparent } from '../../../../platfo
|
||||
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
|
||||
import { isRecentFolder, IWorkspacesService } from '../../../../platform/workspaces/common/workspaces.js';
|
||||
import { IHostService } from '../../../services/host/browser/host.js';
|
||||
import { CODE_OPEN_SETTINGS_ACTION_ID } from '../../../contrib/void/browser/codeSettingsPane.js';
|
||||
import { CODE_CTRL_K_ACTION_ID, CODE_CTRL_L_ACTION_ID } from '../../../contrib/void/browser/actionIDs.js';
|
||||
// import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
|
||||
import { ILabelService, Verbosity } from '../../../../platform/label/common/label.js';
|
||||
import { ColorScheme } from '../../web.api.js';
|
||||
import { OpenFileFolderAction, OpenFolderAction } from '../../actions/workspaceActions.js';
|
||||
import { IWindowOpenable } from '../../../../platform/window/common/window.js';
|
||||
import { splitRecentLabel } from '../../../../base/common/labels.js';
|
||||
import { IViewsService } from '../../../services/views/common/viewsService.js';
|
||||
|
||||
/* eslint-disable */ // Void
|
||||
import { VOID_CTRL_K_ACTION_ID, VOID_CTRL_L_ACTION_ID } from '../../../contrib/void/browser/actionIDs.js';
|
||||
/* eslint-disable */ // Code
|
||||
import { CODE_CTRL_K_ACTION_ID, CODE_CTRL_L_ACTION_ID } from '../../../contrib/code/browser/actionIDs.js';
|
||||
import { VIEWLET_ID as REMOTE_EXPLORER_VIEWLET_ID } from '../../../contrib/remote/browser/remoteExplorer.js';
|
||||
/* eslint-enable */
|
||||
|
||||
@@ -47,15 +50,9 @@ import { VIEWLET_ID as REMOTE_EXPLORER_VIEWLET_ID } from '../../../contrib/remot
|
||||
// const startDebugging: WatermarkEntry = { text: localize('watermark.startDebugging', "Start Debugging"), id: 'workbench.action.debug.start', when: { web: ContextKeyExpr.equals('terminalProcessSupported', true) } };
|
||||
// const openSettings: WatermarkEntry = { text: localize('watermark.openSettings', "Open Settings"), id: 'workbench.action.openSettings' };
|
||||
|
||||
// // shown when Code is emtpty
|
||||
// const noFolderEntries = [
|
||||
// // showCommands,
|
||||
// openFileNonMacOnly,
|
||||
// openFolderNonMacOnly,
|
||||
// openFileOrFolderMacOnly,
|
||||
// openRecent,
|
||||
// // newUntitledFileMacOnly
|
||||
// ];
|
||||
// const showCopilot = ContextKeyExpr.or(ContextKeyExpr.equals('chatSetupHidden', false), ContextKeyExpr.equals('chatSetupInstalled', true));
|
||||
// const openChat: WatermarkEntry = { text: localize('watermark.openChat', "Open Chat"), id: 'workbench.action.chat.open', when: { native: showCopilot, web: showCopilot } };
|
||||
// const openCopilotEdits: WatermarkEntry = { text: localize('watermark.openCopilotEdits', "Open Copilot Edits"), id: 'workbench.action.chat.openEditSession', when: { native: showCopilot, web: showCopilot } };
|
||||
|
||||
// const emptyWindowEntries: WatermarkEntry[] = coalesce([
|
||||
// showCommands,
|
||||
@@ -194,11 +191,11 @@ export class EditorGroupWatermark extends Disposable {
|
||||
buttonContainer.style.alignItems = 'center'; // Center the buttons horizontally
|
||||
buttonContainer.style.gap = '8px'; // Reduce gap between buttons from 16px to 8px
|
||||
buttonContainer.style.marginBottom = '16px';
|
||||
voidIconBox.appendChild(buttonContainer);
|
||||
codeIconBox.appendChild(buttonContainer);
|
||||
|
||||
// Open a folder
|
||||
const openFolderButton = h('button')
|
||||
openFolderButton.root.classList.add('code-watermark-button')
|
||||
openFolderButton.root.classList.add('code-openfolder-button')
|
||||
openFolderButton.root.style.display = 'block'
|
||||
openFolderButton.root.style.width = '124px' // Set width to 124px as requested
|
||||
openFolderButton.root.textContent = 'Open Folder'
|
||||
@@ -210,7 +207,19 @@ export class EditorGroupWatermark extends Disposable {
|
||||
// this.commandService.executeCommand(isMacintosh ? 'workbench.action.files.openFileFolder' : 'workbench.action.files.openFolder');
|
||||
// }
|
||||
}
|
||||
codeIconBox.appendChild(openFolderButton.root);
|
||||
buttonContainer.appendChild(openFolderButton.root);
|
||||
|
||||
// Open SSH button
|
||||
const openSSHButton = h('button')
|
||||
openSSHButton.root.classList.add('code-openssh-button')
|
||||
openSSHButton.root.style.display = 'block'
|
||||
openSSHButton.root.style.backgroundColor = '#5a5a5a' // Made darker than the default gray
|
||||
openSSHButton.root.style.width = '124px' // Set width to 124px as requested
|
||||
openSSHButton.root.textContent = 'Open SSH'
|
||||
openSSHButton.root.onclick = () => {
|
||||
this.viewsService.openViewContainer(REMOTE_EXPLORER_VIEWLET_ID);
|
||||
}
|
||||
buttonContainer.appendChild(openSSHButton.root);
|
||||
|
||||
|
||||
// Recents
|
||||
@@ -296,21 +305,21 @@ export class EditorGroupWatermark extends Disposable {
|
||||
label2.set(keys2);
|
||||
this.currentDisposables.add(label2);
|
||||
|
||||
const keys3 = this.keybindingService.lookupKeybinding('workbench.action.openGlobalKeybindings');
|
||||
const button3 = append(recentsBox, $('button'));
|
||||
button3.textContent = 'Code Settings'
|
||||
button3.style.display = 'block'
|
||||
button3.style.marginLeft = 'auto'
|
||||
button3.style.marginRight = 'auto'
|
||||
button3.classList.add('code-settings-watermark-button')
|
||||
// const keys3 = this.keybindingService.lookupKeybinding('workbench.action.openGlobalKeybindings');
|
||||
// const button3 = append(recentsBox, $('button'));
|
||||
// button3.textContent = `Code Settings`
|
||||
// button3.style.display = 'block'
|
||||
// button3.style.marginLeft = 'auto'
|
||||
// button3.style.marginRight = 'auto'
|
||||
// button3.classList.add('code-settings-watermark-button')
|
||||
|
||||
const label3 = new KeybindingLabel(button3, OS, { renderUnboundKeybindings: true, ...defaultKeybindingLabelStyles });
|
||||
if (keys3)
|
||||
label3.set(keys3);
|
||||
button3.onclick = () => {
|
||||
this.commandService.executeCommand(CODE_OPEN_SETTINGS_ACTION_ID)
|
||||
}
|
||||
this.currentDisposables.add(label3);
|
||||
// const label3 = new KeybindingLabel(button3, OS, { renderUnboundKeybindings: true, ...defaultKeybindingLabelStyles });
|
||||
// if (keys3)
|
||||
// label3.set(keys3);
|
||||
// button3.onclick = () => {
|
||||
// this.commandService.executeCommand(CODE_OPEN_SETTINGS_ACTION_ID)
|
||||
// }
|
||||
// this.currentDisposables.add(label3);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
background-image: url('./void_cube_noshadow.png'); /* // Void */
|
||||
background-image: url('./code_cube_noshadow.png'); /* // Code */
|
||||
background-size: contain;
|
||||
background-position-x: center;
|
||||
background-repeat: no-repeat;
|
||||
@@ -63,17 +63,17 @@
|
||||
|
||||
.void-void-icon,
|
||||
.monaco-workbench.vs-dark .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
|
||||
background-image: url('./void_cube_noshadow.png'); /* // Void */
|
||||
background-image: url('./code_cube_noshadow.png'); /* // Code */
|
||||
}
|
||||
|
||||
.void-void-icon,
|
||||
.monaco-workbench.hc-light .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
|
||||
background-image: url('./void_cube_noshadow.png'); /* // Void */
|
||||
background-image: url('./code_cube_noshadow.png'); /* // Code */
|
||||
}
|
||||
|
||||
.void-void-icon,
|
||||
.monaco-workbench.hc-black .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress {
|
||||
background-image: url('./void_cube_noshadow.png'); /* // Void */
|
||||
background-image: url('./code_cube_noshadow.png'); /* // Code */
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content:not(.empty) .editor-group-container > .editor-group-watermark > .shortcuts,
|
||||
|
||||
@@ -204,7 +204,7 @@ export class PaneCompositeBar extends Disposable {
|
||||
private createMoveAction(viewContainer: ViewContainer, newLocation: ViewContainerLocation, defaultLocation: ViewContainerLocation): IAction {
|
||||
return toAction({
|
||||
id: `moveViewContainerTo${newLocation}`,
|
||||
label: newLocation === ViewContainerLocation.Panel ? localize('panel', "Panel") : newLocation === ViewContainerLocation.Sidebar ? localize('sidebar', "Primary Side Bar") : localize('auxiliarybar', "Void Side Bar"),
|
||||
label: newLocation === ViewContainerLocation.Panel ? localize('panel', "Panel") : newLocation === ViewContainerLocation.Sidebar ? localize('sidebar', "Primary Side Bar") : localize('auxiliarybar', "Code Side Bar"),
|
||||
run: () => {
|
||||
let index: number | undefined;
|
||||
if (newLocation !== defaultLocation) {
|
||||
|
||||
@@ -312,51 +312,6 @@ registerAction2(class extends Action2 {
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.closePanel',
|
||||
title: localize2('closePanel', 'Hide Panel'),
|
||||
category: Categories.View,
|
||||
icon: closeIcon,
|
||||
menu: [{
|
||||
id: MenuId.CommandPalette,
|
||||
when: PanelVisibleContext,
|
||||
}, {
|
||||
id: MenuId.PanelTitle,
|
||||
group: 'navigation',
|
||||
order: 2
|
||||
}]
|
||||
});
|
||||
}
|
||||
run(accessor: ServicesAccessor) {
|
||||
accessor.get(IWorkbenchLayoutService).setPartHidden(true, Parts.PANEL_PART);
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.closeAuxiliaryBar',
|
||||
title: localize2('closeSecondarySideBar', 'Hide Code Side Bar'),
|
||||
category: Categories.View,
|
||||
icon: closeIcon,
|
||||
menu: [{
|
||||
id: MenuId.CommandPalette,
|
||||
when: AuxiliaryBarVisibleContext,
|
||||
}, {
|
||||
id: MenuId.AuxiliaryBarTitle,
|
||||
group: 'navigation',
|
||||
order: 2,
|
||||
when: ContextKeyExpr.notEquals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.TOP)
|
||||
}]
|
||||
});
|
||||
}
|
||||
run(accessor: ServicesAccessor) {
|
||||
accessor.get(IWorkbenchLayoutService).setPartHidden(true, Parts.AUXILIARYBAR_PART);
|
||||
}
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItems([
|
||||
{
|
||||
id: MenuId.LayoutControlMenu,
|
||||
@@ -429,7 +384,7 @@ export class MovePanelToSecondarySideBarAction extends MoveViewsBetweenPanelsAct
|
||||
registerAction2(MovePanelToSidePanelAction);
|
||||
registerAction2(MovePanelToSecondarySideBarAction);
|
||||
|
||||
// --- Move Void Side Bar Views To Panel
|
||||
// --- Move Code Side Bar Views To Panel
|
||||
|
||||
class MoveSidePanelToPanelAction extends MoveViewsBetweenPanelsAction {
|
||||
static readonly ID = 'workbench.action.moveSidePanelToPanel';
|
||||
@@ -437,7 +392,7 @@ class MoveSidePanelToPanelAction extends MoveViewsBetweenPanelsAction {
|
||||
constructor() {
|
||||
super(ViewContainerLocation.AuxiliaryBar, ViewContainerLocation.Panel, {
|
||||
id: MoveSidePanelToPanelAction.ID,
|
||||
title: localize2('moveSidePanelToPanel', "Move Side Bar Views To Panel"), // Code - this seemed to have a typo before
|
||||
title: localize2('moveSidePanelToPanel', "Move Code Side Bar Views To Panel"),
|
||||
category: Categories.View,
|
||||
f1: false
|
||||
});
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-left > .window-appicon:not(.codicon) {
|
||||
background-image: url('../../../media/code-icon-sm.png');
|
||||
background-image: url('../../../media/code-icon.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 16px;
|
||||
@@ -271,23 +271,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .titlebar-container .window-appicon > .home-bar-icon-badge {
|
||||
position: absolute;
|
||||
right: 9px;
|
||||
bottom: 6px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
z-index: 1;
|
||||
/* on top of home indicator */
|
||||
background-image: url('../../../media/code-icon-sm.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 8px;
|
||||
pointer-events: none;
|
||||
border-top: 1px solid transparent;
|
||||
border-left: 1px solid transparent;
|
||||
}
|
||||
|
||||
/* Window Controls Container */
|
||||
.monaco-workbench .part.titlebar .window-controls-container {
|
||||
display: flex;
|
||||
|
||||
@@ -547,12 +547,12 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
|
||||
'type': 'string',
|
||||
'enum': ['default', 'top', 'bottom', 'hidden'],
|
||||
'default': 'default',
|
||||
'markdownDescription': localize({ comment: ['This is the description for a setting'], key: 'activityBarLocation' }, "Controls the location of the Activity Bar relative to the Primary and Code Side Bars."),
|
||||
'markdownDescription': localize({ comment: ['This is the description for a setting'], key: 'activityBarLocation' }, "Controls the location of the Activity Bar relative to the Primary and Code Side Bars."), // Code side bar
|
||||
'enumDescriptions': [
|
||||
localize('workbench.activityBar.location.default', "Show the Activity Bar on the side of the Primary Side Bar and on top of the Code Side Bar."),
|
||||
localize('workbench.activityBar.location.top', "Show the Activity Bar on top of the Primary and Code Side Bars."),
|
||||
localize('workbench.activityBar.location.bottom', "Show the Activity Bar at the bottom of the Primary and Code Side Bars."),
|
||||
localize('workbench.activityBar.location.hide', "Hide the Activity Bar in the Primary and Code Side Bars.")
|
||||
localize('workbench.activityBar.location.default', "Show the Activity Bar on the side of the Primary Side Bar and on top of the Code Side Bar."), // Code side bar
|
||||
localize('workbench.activityBar.location.top', "Show the Activity Bar on top of the Primary and Code Side Bars."), // Code side bar
|
||||
localize('workbench.activityBar.location.bottom', "Show the Activity Bar at the bottom of the Primary and Code Side Bars."), // Code side bar
|
||||
localize('workbench.activityBar.location.hide', "Hide the Activity Bar in the Primary and Code Side Bars.") // Code side bar
|
||||
],
|
||||
},
|
||||
'workbench.activityBar.iconClickBehavior': {
|
||||
@@ -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 ? 300 : 300, // <-- Code edited this to 300 : 300 (was 1500 : 500)
|
||||
'default': 300, // Code changed this from isMacintosh ? 1500 : 500,
|
||||
'minimum': 0
|
||||
},
|
||||
'workbench.reduceMotion': {
|
||||
|
||||
@@ -678,7 +678,7 @@ const defaultChat = {
|
||||
};
|
||||
|
||||
// // Add next to the command center if command center is disabled
|
||||
// Void commented this out with /* */ - copilot head
|
||||
// Code commented this out with /* */ - copilot head
|
||||
/* MenuRegistry.appendMenuItem(MenuId.CommandCenter, {
|
||||
submenu: MenuId.ChatTitleBarMenu,
|
||||
title: localize('title4', "Copilot"),
|
||||
|
||||
@@ -44,7 +44,7 @@ import { ChatViewId } from './chat.js';
|
||||
|
||||
// --- Chat Container & View Registration
|
||||
|
||||
// Void commented this out
|
||||
// Code commented this out
|
||||
// const chatViewContainer: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
|
||||
// id: CHAT_SIDEBAR_PANEL_ID,
|
||||
// title: localize2('chat.viewContainer.label', "Chat"),
|
||||
@@ -86,7 +86,7 @@ import { ChatViewId } from './chat.js';
|
||||
// Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(chatViewDescriptor, chatViewContainer);
|
||||
|
||||
// --- Edits Container & View Registration
|
||||
// Void commented this out
|
||||
// Code commented this out
|
||||
|
||||
// const editsViewContainer: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
|
||||
// id: CHAT_EDITING_SIDEBAR_PANEL_ID,
|
||||
@@ -435,7 +435,7 @@ Registry.as<IExtensionFeaturesRegistry>(Extensions.ExtensionFeaturesRegistry).re
|
||||
});
|
||||
|
||||
|
||||
// Void commented this out
|
||||
// Code commented this out
|
||||
// // TODO@roblourens remove after a few months
|
||||
|
||||
// export class MovedChatEditsViewPane extends ViewPane {
|
||||
|
||||
@@ -33,7 +33,7 @@ registerAction2(class extends Action2 {
|
||||
title: localize2('dummy', 'dummy: Init'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Digit0,
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,3 +4,23 @@
|
||||
export const CODE_CTRL_L_ACTION_ID = 'void.ctrlLAction'
|
||||
|
||||
export const CODE_CTRL_K_ACTION_ID = 'void.ctrlKAction'
|
||||
|
||||
export const CODE_ACCEPT_DIFF_ACTION_ID = 'void.acceptDiff'
|
||||
|
||||
export const CODE_REJECT_DIFF_ACTION_ID = 'void.rejectDiff'
|
||||
|
||||
export const CODE_GOTO_NEXT_DIFF_ACTION_ID = 'void.goToNextDiff'
|
||||
|
||||
export const CODE_GOTO_PREV_DIFF_ACTION_ID = 'void.goToPrevDiff'
|
||||
|
||||
export const CODE_GOTO_NEXT_URI_ACTION_ID = 'void.goToNextUri'
|
||||
|
||||
export const CODE_GOTO_PREV_URI_ACTION_ID = 'void.goToPrevUri'
|
||||
|
||||
export const CODE_ACCEPT_FILE_ACTION_ID = 'void.acceptFile'
|
||||
|
||||
export const CODE_REJECT_FILE_ACTION_ID = 'void.rejectFile'
|
||||
|
||||
export const CODE_ACCEPT_ALL_DIFFS_ACTION_ID = 'void.acceptAllDiffs'
|
||||
|
||||
export const CODE_REJECT_ALL_DIFFS_ACTION_ID = 'void.rejectAllDiffs'
|
||||
|
||||
@@ -18,8 +18,8 @@ import { extractCodeFromRegular } from '../common/helpers/extractCodeFromResult.
|
||||
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.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 { ICodeSettingsService } from '../common/codeSettingsService.js';
|
||||
import { FeatureName } from '../common/codeSettingsTypes.js';
|
||||
import { IConvertToLLMMessageService } from './convertToLLMMessageService.js';
|
||||
// import { IContextGatheringService } from './contextGatheringService.js';
|
||||
|
||||
@@ -28,7 +28,7 @@ import { IConvertToLLMMessageService } from './convertToLLMMessageService.js';
|
||||
const allLinebreakSymbols = ['\r\n', '\n']
|
||||
const _ln = isWindows ? allLinebreakSymbols[0] : allLinebreakSymbols[1]
|
||||
|
||||
// The extension this was called from is here - https://github.com/hanzoai/code/blob/autocomplete/extensions/void/src/extension/extension.ts
|
||||
// The extension this was called from is here - https://github.com/voideditor/void/blob/autocomplete/extensions/code/src/extension/extension.ts
|
||||
|
||||
|
||||
/*
|
||||
@@ -893,7 +893,7 @@ 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,
|
||||
@ICodeSettingsService private readonly _settingsService: ICodeSettingsService,
|
||||
@IConvertToLLMMessageService private readonly _convertToLLMMessageService: IConvertToLLMMessageService
|
||||
// @IContextGatheringService private readonly _contextGatheringService: IContextGatheringService,
|
||||
) {
|
||||
|
||||
@@ -14,8 +14,8 @@ import { ILLMMessageService } from '../common/sendLLMMessageService.js';
|
||||
import { chat_userMessageContent, isABuiltinToolName } from '../common/prompt/prompts.js';
|
||||
import { AnthropicReasoning, getErrorMessage, RawToolCallObj, RawToolParamsObj } from '../common/sendLLMMessageTypes.js';
|
||||
import { generateUuid } from '../../../../base/common/uuid.js';
|
||||
import { FeatureName, ModelSelection, ModelSelectionOptions } from '../common/voidSettingsTypes.js';
|
||||
import { IVoidSettingsService } from '../common/voidSettingsService.js';
|
||||
import { FeatureName, ModelSelection, ModelSelectionOptions } from '../common/codeSettingsTypes.js';
|
||||
import { ICodeSettingsService } from '../common/codeSettingsService.js';
|
||||
import { approvalTypeOfBuiltinToolName, BuiltinToolCallParams, ToolCallParams, ToolName, ToolResult } from '../common/toolsServiceTypes.js';
|
||||
import { IToolsService } from './toolsService.js';
|
||||
import { CancellationToken } from '../../../../base/common/cancellation.js';
|
||||
@@ -24,10 +24,10 @@ import { ChatMessage, CheckpointEntry, CodespanLocationLink, StagingSelectionIte
|
||||
import { Position } from '../../../../editor/common/core/position.js';
|
||||
import { IMetricsService } from '../common/metricsService.js';
|
||||
import { shorten } from '../../../../base/common/labels.js';
|
||||
import { IVoidModelService } from '../common/voidModelService.js';
|
||||
import { ICodeModelService } from '../common/codeModelService.js';
|
||||
import { findLast, findLastIdx } from '../../../../base/common/arraysFind.js';
|
||||
import { IEditCodeService } from './editCodeServiceInterface.js';
|
||||
import { VoidFileSnapshot } from '../common/editCodeServiceTypes.js';
|
||||
import { CodeFileSnapshot } from '../common/editCodeServiceTypes.js';
|
||||
import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';
|
||||
import { truncate } from '../../../../base/common/strings.js';
|
||||
import { THREAD_STORAGE_KEY } from '../common/storageKeys.js';
|
||||
@@ -314,10 +314,10 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IVoidModelService private readonly _voidModelService: IVoidModelService,
|
||||
@ICodeModelService private readonly _codeModelService: ICodeModelService,
|
||||
@ILLMMessageService private readonly _llmMessageService: ILLMMessageService,
|
||||
@IToolsService private readonly _toolsService: IToolsService,
|
||||
@IVoidSettingsService private readonly _settingsService: IVoidSettingsService,
|
||||
@ICodeSettingsService private readonly _settingsService: ICodeSettingsService,
|
||||
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
|
||||
@IMetricsService private readonly _metricsService: IMetricsService,
|
||||
@IEditCodeService private readonly _editCodeService: IEditCodeService,
|
||||
@@ -443,11 +443,11 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
// set streamState
|
||||
const messages = newState.allThreads[threadId]?.messages
|
||||
const lastMessage = messages && messages[messages.length - 1]
|
||||
// if awaiting user but stream state doesn't indicate it (happens if restart Void)
|
||||
// if awaiting user but stream state doesn't indicate it (happens if restart Code)
|
||||
if (lastMessage && lastMessage.role === 'tool' && lastMessage.type === 'tool_request')
|
||||
this._setStreamState(threadId, { isRunning: 'awaiting_user', })
|
||||
|
||||
// if running now but stream state doesn't indicate it (happens if restart Void), cancel that last tool
|
||||
// if running now but stream state doesn't indicate it (happens if restart Code), cancel that last tool
|
||||
if (lastMessage && lastMessage.role === 'tool' && lastMessage.type === 'running_now') {
|
||||
|
||||
this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', content: lastMessage.content, id: lastMessage.id, rawParams: lastMessage.rawParams, result: null, name: lastMessage.name, params: lastMessage.params, mcpServerName: lastMessage.mcpServerName })
|
||||
@@ -945,11 +945,11 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
|
||||
|
||||
private _getCheckpointInfo = (checkpointMessage: ChatMessage & { role: 'checkpoint' }, fsPath: string, opts: { includeUserModifiedChanges: boolean }) => {
|
||||
const voidFileSnapshot = checkpointMessage.voidFileSnapshotOfURI ? checkpointMessage.voidFileSnapshotOfURI[fsPath] ?? null : null
|
||||
if (!opts.includeUserModifiedChanges) { return { voidFileSnapshot, } }
|
||||
const codeFileSnapshot = checkpointMessage.codeFileSnapshotOfURI ? checkpointMessage.codeFileSnapshotOfURI[fsPath] ?? null : null
|
||||
if (!opts.includeUserModifiedChanges) { return { codeFileSnapshot, } }
|
||||
|
||||
const userModifiedVoidFileSnapshot = fsPath in checkpointMessage.userModifications.voidFileSnapshotOfURI ? checkpointMessage.userModifications.voidFileSnapshotOfURI[fsPath] ?? null : null
|
||||
return { voidFileSnapshot: userModifiedVoidFileSnapshot ?? voidFileSnapshot, }
|
||||
const userModifiedCodeFileSnapshot = fsPath in checkpointMessage.userModifications.codeFileSnapshotOfURI ? checkpointMessage.userModifications.codeFileSnapshotOfURI[fsPath] ?? null : null
|
||||
return { codeFileSnapshot: userModifiedCodeFileSnapshot ?? codeFileSnapshot, }
|
||||
}
|
||||
|
||||
private _computeNewCheckpointInfo({ threadId }: { threadId: string }) {
|
||||
@@ -959,59 +959,59 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
const lastCheckpointIdx = findLastIdx(thread.messages, (m) => m.role === 'checkpoint') ?? -1
|
||||
if (lastCheckpointIdx === -1) return
|
||||
|
||||
const voidFileSnapshotOfURI: { [fsPath: string]: VoidFileSnapshot | undefined } = {}
|
||||
const codeFileSnapshotOfURI: { [fsPath: string]: CodeFileSnapshot | undefined } = {}
|
||||
|
||||
// add a change for all the URIs in the checkpoint history
|
||||
const { lastIdxOfURI } = this._getCheckpointsBetween({ threadId, loIdx: 0, hiIdx: lastCheckpointIdx, }) ?? {}
|
||||
for (const fsPath in lastIdxOfURI ?? {}) {
|
||||
const { model } = this._voidModelService.getModelFromFsPath(fsPath)
|
||||
const { model } = this._codeModelService.getModelFromFsPath(fsPath)
|
||||
if (!model) continue
|
||||
const checkpoint2 = thread.messages[lastIdxOfURI[fsPath]] || null
|
||||
if (!checkpoint2) continue
|
||||
if (checkpoint2.role !== 'checkpoint') continue
|
||||
const res = this._getCheckpointInfo(checkpoint2, fsPath, { includeUserModifiedChanges: false })
|
||||
if (!res) continue
|
||||
const { voidFileSnapshot: oldVoidFileSnapshot } = res
|
||||
const { codeFileSnapshot: oldCodeFileSnapshot } = res
|
||||
|
||||
// if there was any change to the str or diffAreaSnapshot, update. rough approximation of equality, oldDiffAreasSnapshot === diffAreasSnapshot is not perfect
|
||||
const voidFileSnapshot = this._editCodeService.getVoidFileSnapshot(URI.file(fsPath))
|
||||
if (oldVoidFileSnapshot === voidFileSnapshot) continue
|
||||
voidFileSnapshotOfURI[fsPath] = voidFileSnapshot
|
||||
const codeFileSnapshot = this._editCodeService.getCodeFileSnapshot(URI.file(fsPath))
|
||||
if (oldCodeFileSnapshot === codeFileSnapshot) continue
|
||||
codeFileSnapshotOfURI[fsPath] = codeFileSnapshot
|
||||
}
|
||||
|
||||
// // add a change for all user-edited files (that aren't in the history)
|
||||
// for (const fsPath of this._userModifiedFilesToCheckInCheckpoints.keys()) {
|
||||
// if (fsPath in lastIdxOfURI) continue // if already visisted, don't visit again
|
||||
// const { model } = this._voidModelService.getModelFromFsPath(fsPath)
|
||||
// const { model } = this._codeModelService.getModelFromFsPath(fsPath)
|
||||
// if (!model) continue
|
||||
// currStrOfFsPath[fsPath] = model.getValue(EndOfLinePreference.LF)
|
||||
// }
|
||||
|
||||
return { voidFileSnapshotOfURI }
|
||||
return { codeFileSnapshotOfURI }
|
||||
}
|
||||
|
||||
|
||||
private _addUserCheckpoint({ threadId }: { threadId: string }) {
|
||||
const { voidFileSnapshotOfURI } = this._computeNewCheckpointInfo({ threadId }) ?? {}
|
||||
const { codeFileSnapshotOfURI } = this._computeNewCheckpointInfo({ threadId }) ?? {}
|
||||
this._addCheckpoint(threadId, {
|
||||
role: 'checkpoint',
|
||||
type: 'user_edit',
|
||||
voidFileSnapshotOfURI: voidFileSnapshotOfURI ?? {},
|
||||
userModifications: { voidFileSnapshotOfURI: {}, },
|
||||
codeFileSnapshotOfURI: codeFileSnapshotOfURI ?? {},
|
||||
userModifications: { codeFileSnapshotOfURI: {}, },
|
||||
})
|
||||
}
|
||||
// call this right after LLM edits a file
|
||||
private _addToolEditCheckpoint({ threadId, uri, }: { threadId: string, uri: URI }) {
|
||||
const thread = this.state.allThreads[threadId]
|
||||
if (!thread) return
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
if (!model) return // should never happen
|
||||
const diffAreasSnapshot = this._editCodeService.getVoidFileSnapshot(uri)
|
||||
const diffAreasSnapshot = this._editCodeService.getCodeFileSnapshot(uri)
|
||||
this._addCheckpoint(threadId, {
|
||||
role: 'checkpoint',
|
||||
type: 'tool_edit',
|
||||
voidFileSnapshotOfURI: { [uri.fsPath]: diffAreasSnapshot },
|
||||
userModifications: { voidFileSnapshotOfURI: {} },
|
||||
codeFileSnapshotOfURI: { [uri.fsPath]: diffAreasSnapshot },
|
||||
userModifications: { codeFileSnapshotOfURI: {} },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1035,7 +1035,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
for (let i = loIdx; i <= hiIdx; i += 1) {
|
||||
const message = thread.messages[i]
|
||||
if (message?.role !== 'checkpoint') continue
|
||||
for (const fsPath in message.voidFileSnapshotOfURI) { // do not include userModified.beforeStrOfURI here, jumping should not include those changes
|
||||
for (const fsPath in message.codeFileSnapshotOfURI) { // do not include userModified.beforeStrOfURI here, jumping should not include those changes
|
||||
lastIdxOfURI[fsPath] = i
|
||||
}
|
||||
}
|
||||
@@ -1055,13 +1055,13 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
||||
return [checkpoint, currCheckpointIdx]
|
||||
}
|
||||
private _addUserModificationsToCurrCheckpoint({ threadId }: { threadId: string }) {
|
||||
const { voidFileSnapshotOfURI } = this._computeNewCheckpointInfo({ threadId }) ?? {}
|
||||
const { codeFileSnapshotOfURI } = this._computeNewCheckpointInfo({ threadId }) ?? {}
|
||||
const res = this._readCurrentCheckpoint(threadId)
|
||||
if (!res) return
|
||||
const [checkpoint, checkpointIdx] = res
|
||||
this._editMessageInThread(threadId, checkpointIdx, {
|
||||
...checkpoint,
|
||||
userModifications: { voidFileSnapshotOfURI: voidFileSnapshotOfURI ?? {}, },
|
||||
userModifications: { codeFileSnapshotOfURI: codeFileSnapshotOfURI ?? {}, },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1139,9 +1139,9 @@ We only need to do it for files that were edited since `to`, ie files between to
|
||||
if (message.role !== 'checkpoint') continue
|
||||
const res = this._getCheckpointInfo(message, fsPath, { includeUserModifiedChanges: jumpToUserModified })
|
||||
if (!res) continue
|
||||
const { voidFileSnapshot } = res
|
||||
if (!voidFileSnapshot) continue
|
||||
this._editCodeService.restoreVoidFileSnapshot(URI.file(fsPath), voidFileSnapshot)
|
||||
const { codeFileSnapshot } = res
|
||||
if (!codeFileSnapshot) continue
|
||||
this._editCodeService.restoreCodeFileSnapshot(URI.file(fsPath), codeFileSnapshot)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1173,9 +1173,9 @@ We only need to do it for files that were edited since `from`, ie files between
|
||||
if (message.role !== 'checkpoint') continue
|
||||
const res = this._getCheckpointInfo(message, fsPath, { includeUserModifiedChanges: jumpToUserModified })
|
||||
if (!res) continue
|
||||
const { voidFileSnapshot } = res
|
||||
if (!voidFileSnapshot) continue
|
||||
this._editCodeService.restoreVoidFileSnapshot(URI.file(fsPath), voidFileSnapshot)
|
||||
const { codeFileSnapshot } = res
|
||||
if (!codeFileSnapshot) continue
|
||||
this._editCodeService.restoreCodeFileSnapshot(URI.file(fsPath), codeFileSnapshot)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -1463,7 +1463,7 @@ We only need to do it for files that were edited since `from`, ie files between
|
||||
// check all prevUris for the target
|
||||
for (const uri of prevUris) {
|
||||
|
||||
const modelRef = await this._voidModelService.getModelSafe(uri)
|
||||
const modelRef = await this._codeModelService.getModelSafe(uri)
|
||||
const { model } = modelRef
|
||||
if (!model) continue
|
||||
|
||||
|
||||
@@ -26,7 +26,60 @@ import './autocompleteService.js'
|
||||
import './codeSettingsPane.js'
|
||||
|
||||
// register css
|
||||
import './media/void.css'
|
||||
import './media/code.css'
|
||||
|
||||
// update (frontend part, also see platform/)
|
||||
import './codeUpdateActions.js'
|
||||
|
||||
import './convertToLLMMessageWorkbenchContrib.js'
|
||||
|
||||
// tools
|
||||
import './toolsService.js'
|
||||
import './terminalToolService.js'
|
||||
|
||||
// register Thread History
|
||||
import './chatThreadService.js'
|
||||
|
||||
// ping
|
||||
import './metricsPollService.js'
|
||||
|
||||
// helper services
|
||||
import './helperServices/consistentItemService.js'
|
||||
|
||||
// register selection helper
|
||||
import './codeSelectionHelperWidget.js'
|
||||
|
||||
// register tooltip service
|
||||
import './tooltipService.js'
|
||||
|
||||
// register onboarding service
|
||||
import './codeOnboardingService.js'
|
||||
|
||||
// register misc service
|
||||
import './miscWokrbenchContrib.js'
|
||||
|
||||
// register file service (for explorer context menu)
|
||||
import './fileService.js'
|
||||
|
||||
// register source control management
|
||||
import './codeSCMService.js'
|
||||
|
||||
// ---------- common (unclear if these actually need to be imported, because they're already imported wherever they're used) ----------
|
||||
|
||||
// llmMessage
|
||||
import '../common/sendLLMMessageService.js'
|
||||
|
||||
// codeSettings
|
||||
import '../common/codeSettingsService.js'
|
||||
|
||||
// refreshModel
|
||||
import '../common/refreshModelService.js'
|
||||
|
||||
// metrics
|
||||
import '../common/metricsService.js'
|
||||
|
||||
// updates
|
||||
import '../common/codeUpdateService.js'
|
||||
|
||||
// model service
|
||||
import '../common/codeModelService.js'
|
||||
|
||||
+53
-53
@@ -11,7 +11,7 @@ import { Widget } from '../../../../base/browser/ui/widget.js';
|
||||
import { IOverlayWidget, ICodeEditor, OverlayWidgetPositionPreference } from '../../../../editor/browser/editorBrowser.js';
|
||||
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
|
||||
import { mountVoidCommandBar } from './react/out/void-editor-widgets-tsx/index.js'
|
||||
import { mountCodeCommandBar } from './react/out/code-editor-widgets-tsx/index.js'
|
||||
import { deepClone } from '../../../../base/common/objects.js';
|
||||
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { IEditCodeService } from './editCodeServiceInterface.js';
|
||||
@@ -19,7 +19,7 @@ import { ITextModel } from '../../../../editor/common/model.js';
|
||||
import { IModelService } from '../../../../editor/common/services/model.js';
|
||||
import { generateUuid } from '../../../../base/common/uuid.js';
|
||||
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
|
||||
import { VOID_ACCEPT_DIFF_ACTION_ID, VOID_REJECT_DIFF_ACTION_ID, VOID_GOTO_NEXT_DIFF_ACTION_ID, VOID_GOTO_PREV_DIFF_ACTION_ID, VOID_GOTO_NEXT_URI_ACTION_ID, VOID_GOTO_PREV_URI_ACTION_ID, VOID_ACCEPT_FILE_ACTION_ID, VOID_REJECT_FILE_ACTION_ID, VOID_ACCEPT_ALL_DIFFS_ACTION_ID, VOID_REJECT_ALL_DIFFS_ACTION_ID } from './actionIDs.js';
|
||||
import { CODE_ACCEPT_DIFF_ACTION_ID, CODE_REJECT_DIFF_ACTION_ID, CODE_GOTO_NEXT_DIFF_ACTION_ID, CODE_GOTO_PREV_DIFF_ACTION_ID, CODE_GOTO_NEXT_URI_ACTION_ID, CODE_GOTO_PREV_URI_ACTION_ID, CODE_ACCEPT_FILE_ACTION_ID, CODE_REJECT_FILE_ACTION_ID, CODE_ACCEPT_ALL_DIFFS_ACTION_ID, CODE_REJECT_ALL_DIFFS_ACTION_ID } from './actionIDs.js';
|
||||
import { localize2 } from '../../../../nls.js';
|
||||
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
|
||||
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
|
||||
@@ -27,11 +27,11 @@ import { IMetricsService } from '../common/metricsService.js';
|
||||
import { KeyMod } from '../../../../editor/common/services/editorBaseApi.js';
|
||||
import { KeyCode } from '../../../../base/common/keyCodes.js';
|
||||
import { ScrollType } from '../../../../editor/common/editorCommon.js';
|
||||
import { IVoidModelService } from '../common/voidModelService.js';
|
||||
import { ICodeModelService } from '../common/codeModelService.js';
|
||||
|
||||
|
||||
|
||||
export interface IVoidCommandBarService {
|
||||
export interface ICodeCommandBarService {
|
||||
readonly _serviceBrand: undefined;
|
||||
stateOfURI: { [uri: string]: CommandBarStateType };
|
||||
sortedURIs: URI[];
|
||||
@@ -54,7 +54,7 @@ export interface IVoidCommandBarService {
|
||||
}
|
||||
|
||||
|
||||
export const IVoidCommandBarService = createDecorator<IVoidCommandBarService>('VoidCommandBarService');
|
||||
export const ICodeCommandBarService = createDecorator<ICodeCommandBarService>('CodeCommandBarService');
|
||||
|
||||
|
||||
export type CommandBarStateType = undefined | {
|
||||
@@ -75,10 +75,10 @@ const defaultState: NonNullable<CommandBarStateType> = {
|
||||
}
|
||||
|
||||
|
||||
export class VoidCommandBarService extends Disposable implements IVoidCommandBarService {
|
||||
export class CodeCommandBarService extends Disposable implements ICodeCommandBarService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
static readonly ID: 'void.VoidCommandBarService'
|
||||
static readonly ID: 'void.CodeCommandBarService'
|
||||
|
||||
// depends on uri -> diffZone -> {streaming, diffs}
|
||||
public stateOfURI: { [uri: string]: CommandBarStateType } = {}
|
||||
@@ -100,7 +100,7 @@ export class VoidCommandBarService extends Disposable implements IVoidCommandBar
|
||||
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IEditCodeService private readonly _editCodeService: IEditCodeService,
|
||||
@IVoidModelService private readonly _voidModelService: IVoidModelService,
|
||||
@ICodeModelService private readonly _codeModelService: ICodeModelService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -460,7 +460,7 @@ export class VoidCommandBarService extends Disposable implements IVoidCommandBar
|
||||
if (!nextURI) return;
|
||||
|
||||
// Get the model for this URI
|
||||
const { model } = await this._voidModelService.getModelSafe(nextURI);
|
||||
const { model } = await this._codeModelService.getModelSafe(nextURI);
|
||||
if (!model) return;
|
||||
|
||||
// Find an editor to use
|
||||
@@ -488,10 +488,10 @@ export class VoidCommandBarService extends Disposable implements IVoidCommandBar
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IVoidCommandBarService, VoidCommandBarService, InstantiationType.Delayed); // delayed is needed here :(
|
||||
registerSingleton(ICodeCommandBarService, CodeCommandBarService, InstantiationType.Delayed); // delayed is needed here :(
|
||||
|
||||
|
||||
export type VoidCommandBarProps = {
|
||||
export type CodeCommandBarProps = {
|
||||
uri: URI | null;
|
||||
editor: ICodeEditor;
|
||||
}
|
||||
@@ -535,12 +535,12 @@ class AcceptRejectAllFloatingWidget extends Widget implements IOverlayWidget {
|
||||
|
||||
this.instantiationService.invokeFunction(accessor => {
|
||||
const uri = editor.getModel()?.uri || null
|
||||
const res = mountVoidCommandBar(root, accessor, { uri, editor } satisfies VoidCommandBarProps)
|
||||
const res = mountCodeCommandBar(root, accessor, { uri, editor } satisfies CodeCommandBarProps)
|
||||
if (!res) return
|
||||
this._register(toDisposable(() => res.dispose?.()))
|
||||
this._register(editor.onWillChangeModel((model) => {
|
||||
const uri = model.newModelUrl
|
||||
res.rerender({ uri, editor } satisfies VoidCommandBarProps)
|
||||
res.rerender({ uri, editor } satisfies CodeCommandBarProps)
|
||||
}))
|
||||
})
|
||||
}
|
||||
@@ -570,20 +570,20 @@ class AcceptRejectAllFloatingWidget extends Widget implements IOverlayWidget {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_ACCEPT_DIFF_ACTION_ID,
|
||||
id: CODE_ACCEPT_DIFF_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidAcceptDiffAction', 'Void: Accept Diff'),
|
||||
title: localize2('codeAcceptDiffAction', 'Code: Accept Diff'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.Enter,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Enter },
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editCodeService = accessor.get(IEditCodeService);
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
|
||||
@@ -613,20 +613,20 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_REJECT_DIFF_ACTION_ID,
|
||||
id: CODE_REJECT_DIFF_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidRejectDiffAction', 'Void: Reject Diff'),
|
||||
title: localize2('codeRejectDiffAction', 'Code: Reject Diff'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.Backspace,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Backspace },
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const editCodeService = accessor.get(IEditCodeService);
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
const activeURI = commandBarService.activeURI;
|
||||
@@ -654,19 +654,19 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_GOTO_NEXT_DIFF_ACTION_ID,
|
||||
id: CODE_GOTO_NEXT_DIFF_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidGoToNextDiffAction', 'Void: Go to Next Diff'),
|
||||
title: localize2('codeGoToNextDiffAction', 'Code: Go to Next Diff'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.DownArrow },
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
const nextDiffIdx = commandBarService.getNextDiffIdx(1);
|
||||
@@ -681,19 +681,19 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_GOTO_PREV_DIFF_ACTION_ID,
|
||||
id: CODE_GOTO_PREV_DIFF_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidGoToPrevDiffAction', 'Void: Go to Previous Diff'),
|
||||
title: localize2('codeGoToPrevDiffAction', 'Code: Go to Previous Diff'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.UpArrow },
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
const prevDiffIdx = commandBarService.getNextDiffIdx(-1);
|
||||
@@ -708,19 +708,19 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_GOTO_NEXT_URI_ACTION_ID,
|
||||
id: CODE_GOTO_NEXT_URI_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidGoToNextUriAction', 'Void: Go to Next File with Diffs'),
|
||||
title: localize2('codeGoToNextUriAction', 'Code: Go to Next File with Diffs'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.RightArrow },
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
const nextUriIdx = commandBarService.getNextUriIdx(1);
|
||||
@@ -735,19 +735,19 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_GOTO_PREV_URI_ACTION_ID,
|
||||
id: CODE_GOTO_PREV_URI_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidGoToPrevUriAction', 'Void: Go to Previous File with Diffs'),
|
||||
title: localize2('codeGoToPrevUriAction', 'Code: Go to Previous File with Diffs'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.LeftArrow },
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
const prevUriIdx = commandBarService.getNextUriIdx(-1);
|
||||
@@ -762,18 +762,18 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_ACCEPT_FILE_ACTION_ID,
|
||||
id: CODE_ACCEPT_FILE_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidAcceptFileAction', 'Void: Accept All Diffs in Current File'),
|
||||
title: localize2('codeAcceptFileAction', 'Code: Accept All Diffs in Current File'),
|
||||
keybinding: {
|
||||
primary: KeyMod.Alt | KeyMod.Shift | KeyCode.Enter,
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const editCodeService = accessor.get(IEditCodeService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
@@ -793,18 +793,18 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_REJECT_FILE_ACTION_ID,
|
||||
id: CODE_REJECT_FILE_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidRejectFileAction', 'Void: Reject All Diffs in Current File'),
|
||||
title: localize2('codeRejectFileAction', 'Code: Reject All Diffs in Current File'),
|
||||
keybinding: {
|
||||
primary: KeyMod.Alt | KeyMod.Shift | KeyCode.Backspace,
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const editCodeService = accessor.get(IEditCodeService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
@@ -824,18 +824,18 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_ACCEPT_ALL_DIFFS_ACTION_ID,
|
||||
id: CODE_ACCEPT_ALL_DIFFS_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidAcceptAllDiffsAction', 'Void: Accept All Diffs in All Files'),
|
||||
title: localize2('codeAcceptAllDiffsAction', 'Code: Accept All Diffs in All Files'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter,
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
if (commandBarService.anyFileIsStreaming()) return;
|
||||
@@ -849,18 +849,18 @@ registerAction2(class extends Action2 {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: VOID_REJECT_ALL_DIFFS_ACTION_ID,
|
||||
id: CODE_REJECT_ALL_DIFFS_ACTION_ID,
|
||||
f1: true,
|
||||
title: localize2('voidRejectAllDiffsAction', 'Void: Reject All Diffs in All Files'),
|
||||
title: localize2('codeRejectAllDiffsAction', 'Code: Reject All Diffs in All Files'),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Backspace,
|
||||
weight: KeybindingWeight.VoidExtension,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const commandBarService = accessor.get(IVoidCommandBarService);
|
||||
const commandBarService = accessor.get(ICodeCommandBarService);
|
||||
const metricsService = accessor.get(IMetricsService);
|
||||
|
||||
if (commandBarService.anyFileIsStreaming()) return;
|
||||
+3
-3
@@ -7,12 +7,12 @@ import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js';
|
||||
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
|
||||
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
|
||||
import { mountVoidOnboarding } from './react/out/void-onboarding/index.js'
|
||||
import { mountCodeOnboarding } from './react/out/code-onboarding/index.js'
|
||||
import { h, getActiveWindow } from '../../../../base/browser/dom.js';
|
||||
|
||||
// Onboarding contribution that mounts the component at startup
|
||||
export class OnboardingContribution extends Disposable implements IWorkbenchContribution {
|
||||
static readonly ID = 'workbench.contrib.voidOnboarding';
|
||||
static readonly ID = 'workbench.contrib.codeOnboarding';
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@@ -33,7 +33,7 @@ export class OnboardingContribution extends Disposable implements IWorkbenchCont
|
||||
const onboardingContainer = h('div.void-onboarding-container').root;
|
||||
workbench.appendChild(onboardingContainer);
|
||||
this.instantiationService.invokeFunction((accessor: ServicesAccessor) => {
|
||||
const result = mountVoidOnboarding(onboardingContainer, accessor);
|
||||
const result = mountCodeOnboarding(onboardingContainer, accessor);
|
||||
if (result && typeof result.dispose === 'function') {
|
||||
this._register(toDisposable(result.dispose));
|
||||
}
|
||||
+21
-21
@@ -9,12 +9,12 @@ import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/c
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'
|
||||
import { ISCMService } from '../../scm/common/scm.js'
|
||||
import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js'
|
||||
import { IVoidSCMService } from '../common/voidSCMTypes.js'
|
||||
import { ICodeSCMService } from '../common/codeSCMTypes.js'
|
||||
import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'
|
||||
import { IVoidSettingsService } from '../common/voidSettingsService.js'
|
||||
import { ICodeSettingsService } from '../common/codeSettingsService.js'
|
||||
import { IConvertToLLMMessageService } from './convertToLLMMessageService.js'
|
||||
import { ILLMMessageService } from '../common/sendLLMMessageService.js'
|
||||
import { ModelSelection, OverridesOfModel, ModelSelectionOptions } from '../common/voidSettingsTypes.js'
|
||||
import { ModelSelection, OverridesOfModel, ModelSelectionOptions } from '../common/codeSettingsTypes.js'
|
||||
import { gitCommitMessage_systemMessage, gitCommitMessage_userMessage } from '../common/prompt/prompts.js'
|
||||
import { LLMChatMessage } from '../common/sendLLMMessageTypes.js'
|
||||
import { generateUuid } from '../../../../base/common/uuid.js'
|
||||
@@ -37,22 +37,22 @@ export interface IGenerateCommitMessageService {
|
||||
abort(): void
|
||||
}
|
||||
|
||||
export const IGenerateCommitMessageService = createDecorator<IGenerateCommitMessageService>('voidGenerateCommitMessageService');
|
||||
export const IGenerateCommitMessageService = createDecorator<IGenerateCommitMessageService>('codeGenerateCommitMessageService');
|
||||
|
||||
const loadingContextKey = 'voidSCMGenerateCommitMessageLoading'
|
||||
const loadingContextKey = 'codeSCMGenerateCommitMessageLoading'
|
||||
|
||||
class GenerateCommitMessageService extends Disposable implements IGenerateCommitMessageService {
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly execute = new ThrottledDelayer(300)
|
||||
private llmRequestId: string | null = null
|
||||
private currentRequestId: string | null = null
|
||||
private voidSCM: IVoidSCMService
|
||||
private codeSCM: ICodeSCMService
|
||||
private loadingContextKey: IContextKey<boolean>
|
||||
|
||||
constructor(
|
||||
@ISCMService private readonly scmService: ISCMService,
|
||||
@IMainProcessService mainProcessService: IMainProcessService,
|
||||
@IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService,
|
||||
@ICodeSettingsService private readonly codeSettingsService: ICodeSettingsService,
|
||||
@IConvertToLLMMessageService private readonly convertToLLMMessageService: IConvertToLLMMessageService,
|
||||
@ILLMMessageService private readonly llmMessageService: ILLMMessageService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@@ -60,7 +60,7 @@ class GenerateCommitMessageService extends Disposable implements IGenerateCommit
|
||||
) {
|
||||
super()
|
||||
this.loadingContextKey = this.contextKeyService.createKey(loadingContextKey, false)
|
||||
this.voidSCM = ProxyChannel.toService<IVoidSCMService>(mainProcessService.getChannel('void-channel-scm'))
|
||||
this.codeSCM = ProxyChannel.toService<ICodeSCMService>(mainProcessService.getChannel('code-channel-scm'))
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
@@ -78,17 +78,17 @@ class GenerateCommitMessageService extends Disposable implements IGenerateCommit
|
||||
try {
|
||||
const { path, repo } = this.gitRepoInfo()
|
||||
const [stat, sampledDiffs, branch, log] = await Promise.all([
|
||||
this.voidSCM.gitStat(path),
|
||||
this.voidSCM.gitSampledDiffs(path),
|
||||
this.voidSCM.gitBranch(path),
|
||||
this.voidSCM.gitLog(path)
|
||||
this.codeSCM.gitStat(path),
|
||||
this.codeSCM.gitSampledDiffs(path),
|
||||
this.codeSCM.gitBranch(path),
|
||||
this.codeSCM.gitLog(path)
|
||||
])
|
||||
|
||||
if (!this.isCurrentRequest(requestId)) { throw new CancellationError() }
|
||||
|
||||
const modelSelection = this.voidSettingsService.state.modelSelectionOfFeature['SCM'] ?? null
|
||||
const modelSelectionOptions = modelSelection ? this.voidSettingsService.state.optionsOfModelSelection['SCM'][modelSelection?.providerName]?.[modelSelection.modelName] : undefined
|
||||
const overridesOfModel = this.voidSettingsService.state.overridesOfModel
|
||||
const modelSelection = this.codeSettingsService.state.modelSelectionOfFeature['SCM'] ?? null
|
||||
const modelSelectionOptions = modelSelection ? this.codeSettingsService.state.optionsOfModelSelection['SCM'][modelSelection?.providerName]?.[modelSelection.modelName] : undefined
|
||||
const overridesOfModel = this.codeSettingsService.state.overridesOfModel
|
||||
|
||||
const modelOptions: ModelOptions = { modelSelection, modelSelectionOptions, overridesOfModel }
|
||||
|
||||
@@ -159,7 +159,7 @@ class GenerateCommitMessageService extends Disposable implements IGenerateCommit
|
||||
onAbort: () => {
|
||||
reject(new CancellationError())
|
||||
},
|
||||
logging: { loggingName: 'VoidSCM - Commit Message' },
|
||||
logging: { loggingName: 'CodeSCM - Commit Message' },
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -177,7 +177,7 @@ class GenerateCommitMessageService extends Disposable implements IGenerateCommit
|
||||
private onError(error: any) {
|
||||
if (!isCancellationError(error)) {
|
||||
console.error(error)
|
||||
this.notificationService.error(localize2('voidFailedToGenerateCommitMessage', 'Failed to generate commit message.').value)
|
||||
this.notificationService.error(localize2('codeFailedToGenerateCommitMessage', 'Failed to generate commit message.').value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,9 +186,9 @@ class GenerateCommitMessageAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'void.generateCommitMessageAction',
|
||||
title: localize2('voidCommitMessagePrompt', 'Void: Generate Commit Message'),
|
||||
title: localize2('codeCommitMessagePrompt', 'Code: Generate Commit Message'),
|
||||
icon: ThemeIcon.fromId('sparkle'),
|
||||
tooltip: localize2('voidCommitMessagePromptTooltip', 'Void: Generate Commit Message'),
|
||||
tooltip: localize2('codeCommitMessagePromptTooltip', 'Code: Generate Commit Message'),
|
||||
f1: true,
|
||||
menu: [{
|
||||
id: MenuId.SCMInputBox,
|
||||
@@ -208,9 +208,9 @@ class LoadingGenerateCommitMessageAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'void.loadingGenerateCommitMessageAction',
|
||||
title: localize2('voidCommitMessagePromptCancel', 'Void: Cancel Commit Message Generation'),
|
||||
title: localize2('codeCommitMessagePromptCancel', 'Code: Cancel Commit Message Generation'),
|
||||
icon: ThemeIcon.fromId('stop-circle'),
|
||||
tooltip: localize2('voidCommitMessagePromptCancelTooltip', 'Void: Cancel Commit Message Generation'),
|
||||
tooltip: localize2('codeCommitMessagePromptCancelTooltip', 'Code: Cancel Commit Message Generation'),
|
||||
f1: false, //Having a cancel command in the command palette is more confusing than useful.
|
||||
menu: [{
|
||||
id: MenuId.SCMInputBox,
|
||||
+8
-8
@@ -11,9 +11,9 @@ import { IEditorContribution } from '../../../../editor/common/editorCommon.js';
|
||||
import { Selection } from '../../../../editor/common/core/selection.js';
|
||||
import { RunOnceScheduler } from '../../../../base/common/async.js';
|
||||
import * as dom from '../../../../base/browser/dom.js';
|
||||
import { mountVoidSelectionHelper } from './react/out/void-editor-widgets-tsx/index.js';
|
||||
import { mountCodeSelectionHelper } from './react/out/code-editor-widgets-tsx/index.js';
|
||||
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IVoidSettingsService } from '../common/voidSettingsService.js';
|
||||
import { ICodeSettingsService } from '../common/codeSettingsService.js';
|
||||
import { EditorOption } from '../../../../editor/common/config/editorOptions.js';
|
||||
import { getLengthOfTextPx } from './editCodeService.js';
|
||||
|
||||
@@ -22,13 +22,13 @@ const minDistanceFromRightPx = 400;
|
||||
const minLeftPx = 60;
|
||||
|
||||
|
||||
export type VoidSelectionHelperProps = {
|
||||
export type CodeSelectionHelperProps = {
|
||||
rerenderKey: number // alternates between 0 and 1
|
||||
}
|
||||
|
||||
|
||||
export class SelectionHelperContribution extends Disposable implements IEditorContribution, IOverlayWidget {
|
||||
public static readonly ID = 'editor.contrib.voidSelectionHelper';
|
||||
public static readonly ID = 'editor.contrib.codeSelectionHelper';
|
||||
// react
|
||||
private _rootHTML: HTMLElement;
|
||||
private _rerender: (props?: any) => void = () => { };
|
||||
@@ -43,7 +43,7 @@ export class SelectionHelperContribution extends Disposable implements IEditorCo
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IVoidSettingsService private readonly _voidSettingsService: IVoidSettingsService
|
||||
@ICodeSettingsService private readonly _codeSettingsService: ICodeSettingsService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -63,7 +63,7 @@ export class SelectionHelperContribution extends Disposable implements IEditorCo
|
||||
if (this._reactComponentDisposable) {
|
||||
this._reactComponentDisposable.dispose();
|
||||
}
|
||||
const res = mountVoidSelectionHelper(content, accessor);
|
||||
const res = mountCodeSelectionHelper(content, accessor);
|
||||
if (!res) return;
|
||||
|
||||
this._reactComponentDisposable = res;
|
||||
@@ -242,11 +242,11 @@ export class SelectionHelperContribution extends Disposable implements IEditorCo
|
||||
this._isVisible = true;
|
||||
|
||||
// rerender
|
||||
const enabled = this._voidSettingsService.state.globalSettings.showInlineSuggestions
|
||||
const enabled = this._codeSettingsService.state.globalSettings.showInlineSuggestions
|
||||
&& this._editor.hasTextFocus() // needed since VS Code counts unfocused selections as selections, which causes this to rerender when it shouldnt (bad ux)
|
||||
|
||||
if (enabled) {
|
||||
this._rerender({ rerenderKey: this._rerenderKey } satisfies VoidSelectionHelperProps)
|
||||
this._rerender({ rerenderKey: this._rerenderKey } satisfies CodeSelectionHelperProps)
|
||||
this._rerenderKey = (this._rerenderKey + 1) % 2;
|
||||
// this._reactComponentRerender();
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class CodeSettingsInput extends EditorInput {
|
||||
}
|
||||
|
||||
override getName(): string {
|
||||
return nls.localize('codeSettingsInputsName', 'Code Settings');
|
||||
return nls.localize('codeSettingsInputsName', 'Code\'s Settings');
|
||||
}
|
||||
|
||||
override getIcon() {
|
||||
@@ -90,7 +90,8 @@ class CodeSettingsPane extends EditorPane {
|
||||
|
||||
// Mount React into the scrollable content
|
||||
this.instantiationService.invokeFunction(accessor => {
|
||||
const disposables: IDisposable[] | undefined = mountCodeSettings(settingsElt, accessor);
|
||||
const disposeFn = mountCodeSettings(settingsElt, accessor)?.dispose;
|
||||
this._register(toDisposable(() => disposeFn?.()))
|
||||
|
||||
// setTimeout(() => { // this is a complete hack and I don't really understand how scrollbar works here
|
||||
// this._scrollbar?.scanDomNode();
|
||||
@@ -111,7 +112,7 @@ class CodeSettingsPane extends EditorPane {
|
||||
|
||||
// register Settings pane
|
||||
Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane).registerEditorPane(
|
||||
EditorPaneDescriptor.create(CodeSettingsPane, CodeSettingsPane.ID, nls.localize('CodeSettingsPane', "Code Settings Pane")),
|
||||
EditorPaneDescriptor.create(CodeSettingsPane, CodeSettingsPane.ID, nls.localize('CodeSettingsPane', "Code\'s Settings Pane")),
|
||||
[new SyncDescriptor(CodeSettingsInput)]
|
||||
);
|
||||
|
||||
@@ -144,17 +145,23 @@ registerAction2(class extends Action2 {
|
||||
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
// close all instances if found
|
||||
const openEditors = editorService.findEditors(CodeSettingsInput.RESOURCE);
|
||||
if (openEditors.length > 0) {
|
||||
await editorService.closeEditors(openEditors);
|
||||
// if is open, close it
|
||||
const openEditors = editorService.findEditors(CodeSettingsInput.RESOURCE); // should only have 0 or 1 elements...
|
||||
if (openEditors.length !== 0) {
|
||||
const openEditor = openEditors[0].editor
|
||||
const isCurrentlyOpen = editorService.activeEditor?.resource?.fsPath === openEditor.resource?.fsPath
|
||||
if (isCurrentlyOpen)
|
||||
await editorService.closeEditors(openEditors)
|
||||
else
|
||||
await editorGroupService.activeGroup.openEditor(openEditor)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// else open it
|
||||
const input = instantiationService.createInstance(CodeSettingsInput);
|
||||
await editorService.openEditor(input);
|
||||
|
||||
await editorGroupService.activeGroup.openEditor(input);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -165,7 +172,7 @@ registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: CODE_OPEN_SETTINGS_ACTION_ID,
|
||||
title: nls.localize2('codeSettings', "Code: Open Settings"),
|
||||
title: nls.localize2('codeSettingsAction2', "Code: Open Settings"),
|
||||
f1: true,
|
||||
icon: Codicon.settingsGear,
|
||||
});
|
||||
@@ -195,7 +202,7 @@ MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '0_command',
|
||||
command: {
|
||||
id: CODE_TOGGLE_SETTINGS_ACTION_ID,
|
||||
title: nls.localize('codeSettings', "Code Settings")
|
||||
title: nls.localize('codeSettingsActionGear', "Code\'s Settings")
|
||||
},
|
||||
order: 1
|
||||
});
|
||||
|
||||
@@ -8,21 +8,111 @@ import Severity from '../../../../base/common/severity.js';
|
||||
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
|
||||
import { localize2 } from '../../../../nls.js';
|
||||
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
|
||||
import { INotificationService } from '../../../../platform/notification/common/notification.js';
|
||||
import { IMetricsService } from '../../../../platform/void/common/metricsService.js';
|
||||
import { ICodeUpdateService } from '../../../../platform/void/common/codeUpdateService.js';
|
||||
import { INotificationActions, INotificationHandle, INotificationService } from '../../../../platform/notification/common/notification.js';
|
||||
import { IMetricsService } from '../common/metricsService.js';
|
||||
import { ICodeUpdateService } from '../common/codeUpdateService.js';
|
||||
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
|
||||
import * as dom from '../../../../base/browser/dom.js';
|
||||
import { IUpdateService } from '../../../../platform/update/common/update.js';
|
||||
import { VoidCheckUpdateRespose } from '../common/voidUpdateServiceTypes.js';
|
||||
import { CodeCheckUpdateRespose } from '../common/codeUpdateServiceTypes.js';
|
||||
import { IAction } from '../../../../base/common/actions.js';
|
||||
|
||||
|
||||
|
||||
|
||||
const notifyYesUpdate = (notifService: INotificationService, msg?: string) => {
|
||||
const message = msg || 'This is a very old version of void, please download the latest version! [Code Editor](https://code.hanzo.ai/download-beta)!'
|
||||
notifService.notify({
|
||||
const notifyUpdate = (res: CodeCheckUpdateRespose & { message: string }, notifService: INotificationService, updateService: IUpdateService): INotificationHandle => {
|
||||
const message = res?.message || 'This is a very old version of Code, please download the latest version! [Code Editor](https://voideditor.com/download-beta)!'
|
||||
|
||||
let actions: INotificationActions | undefined
|
||||
|
||||
if (res?.action) {
|
||||
const primary: IAction[] = []
|
||||
|
||||
if (res.action === 'reinstall') {
|
||||
primary.push({
|
||||
label: `Reinstall`,
|
||||
id: 'void.updater.reinstall',
|
||||
enabled: true,
|
||||
tooltip: '',
|
||||
class: undefined,
|
||||
run: () => {
|
||||
const { window } = dom.getActiveWindow()
|
||||
window.open('https://voideditor.com/download-beta')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (res.action === 'download') {
|
||||
primary.push({
|
||||
label: `Download`,
|
||||
id: 'void.updater.download',
|
||||
enabled: true,
|
||||
tooltip: '',
|
||||
class: undefined,
|
||||
run: () => {
|
||||
updateService.downloadUpdate()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if (res.action === 'apply') {
|
||||
primary.push({
|
||||
label: `Apply`,
|
||||
id: 'void.updater.apply',
|
||||
enabled: true,
|
||||
tooltip: '',
|
||||
class: undefined,
|
||||
run: () => {
|
||||
updateService.applyUpdate()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (res.action === 'restart') {
|
||||
primary.push({
|
||||
label: `Restart`,
|
||||
id: 'void.updater.restart',
|
||||
enabled: true,
|
||||
tooltip: '',
|
||||
class: undefined,
|
||||
run: () => {
|
||||
updateService.quitAndInstall()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
primary.push({
|
||||
id: 'void.updater.site',
|
||||
enabled: true,
|
||||
label: `Code Site`,
|
||||
tooltip: '',
|
||||
class: undefined,
|
||||
run: () => {
|
||||
const { window } = dom.getActiveWindow()
|
||||
window.open('https://voideditor.com/')
|
||||
}
|
||||
})
|
||||
|
||||
actions = {
|
||||
primary: primary,
|
||||
secondary: [{
|
||||
id: 'void.updater.close',
|
||||
enabled: true,
|
||||
label: `Keep current version`,
|
||||
tooltip: '',
|
||||
class: undefined,
|
||||
run: () => {
|
||||
notifController.close()
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
else {
|
||||
actions = undefined
|
||||
}
|
||||
|
||||
const notifController = notifService.notify({
|
||||
severity: Severity.Info,
|
||||
message: message,
|
||||
sticky: true,
|
||||
@@ -36,15 +126,9 @@ const notifyYesUpdate = (notifService: INotificationService, msg?: string) => {
|
||||
// d.dispose()
|
||||
// })
|
||||
}
|
||||
const notifyNoUpdate = (notifService: INotificationService) => {
|
||||
notifService.notify({
|
||||
severity: Severity.Info,
|
||||
message: 'Code is up-to-date!',
|
||||
})
|
||||
}
|
||||
const notifyErrChecking = (notifService: INotificationService) => {
|
||||
const message = `Code Error: There was an error checking for updates. If this persists, please get in touch or reinstall Code [here](https://code.hanzo.ai/download-beta)!`
|
||||
notifService.notify({
|
||||
const notifyErrChecking = (notifService: INotificationService): INotificationHandle => {
|
||||
const message = `Code Error: There was an error checking for updates. If this persists, please get in touch or reinstall Code [here](https://voideditor.com/download-beta)!`
|
||||
const notifController = notifService.notify({
|
||||
severity: Severity.Info,
|
||||
message: message,
|
||||
sticky: true,
|
||||
@@ -53,31 +137,31 @@ const notifyErrChecking = (notifService: INotificationService) => {
|
||||
}
|
||||
|
||||
|
||||
const performVoidCheck = async (
|
||||
const performCodeCheck = async (
|
||||
explicit: boolean,
|
||||
notifService: INotificationService,
|
||||
voidUpdateService: IVoidUpdateService,
|
||||
codeUpdateService: ICodeUpdateService,
|
||||
metricsService: IMetricsService,
|
||||
updateService: IUpdateService,
|
||||
): Promise<INotificationHandle | null> => {
|
||||
|
||||
const metricsTag = explicit ? 'Manual' : 'Auto'
|
||||
|
||||
metricsService.capture(`Void Update ${metricsTag}: Checking...`, {})
|
||||
const res = await voidUpdateService.check(explicit)
|
||||
metricsService.capture(`Code Update ${metricsTag}: Checking...`, {})
|
||||
const res = await codeUpdateService.check(explicit)
|
||||
if (!res) {
|
||||
const notifController = notifyErrChecking(notifService);
|
||||
metricsService.capture(`Void Update ${metricsTag}: Error`, { res })
|
||||
metricsService.capture(`Code Update ${metricsTag}: Error`, { res })
|
||||
return notifController
|
||||
}
|
||||
else {
|
||||
if (res.message) {
|
||||
const notifController = notifyUpdate(res, notifService, updateService)
|
||||
metricsService.capture(`Void Update ${metricsTag}: Yes`, { res })
|
||||
metricsService.capture(`Code Update ${metricsTag}: Yes`, { res })
|
||||
return notifController
|
||||
}
|
||||
else {
|
||||
metricsService.capture(`Void Update ${metricsTag}: No`, { res })
|
||||
metricsService.capture(`Code Update ${metricsTag}: No`, { res })
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -102,11 +186,14 @@ registerAction2(class extends Action2 {
|
||||
const metricsService = accessor.get(IMetricsService)
|
||||
const updateService = accessor.get(IUpdateService)
|
||||
|
||||
metricsService.capture('Code Update Manual: Checking...', {})
|
||||
const res = await codeUpdateService.check()
|
||||
if (!res) { notifyErrChecking(notifService); metricsService.capture('Code Update Manual: Error', { res }) }
|
||||
else if (res.hasUpdate) { notifyYesUpdate(notifService, res.message); metricsService.capture('Code Update Manual: Yes', { res }) }
|
||||
else if (!res.hasUpdate) { notifyNoUpdate(notifService); metricsService.capture('Code Update Manual: No', { res }) }
|
||||
const currNotifController = lastNotifController
|
||||
|
||||
const newController = await performCodeCheck(true, notifService, codeUpdateService, metricsService, updateService)
|
||||
|
||||
if (newController) {
|
||||
currNotifController?.close()
|
||||
lastNotifController = newController
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -114,17 +201,15 @@ registerAction2(class extends Action2 {
|
||||
class CodeUpdateWorkbenchContribution extends Disposable implements IWorkbenchContribution {
|
||||
static readonly ID = 'workbench.contrib.void.codeUpdate'
|
||||
constructor(
|
||||
@ICodeUpdateService private readonly codeUpdateService: ICodeUpdateService,
|
||||
@IMetricsService private readonly metricsService: IMetricsService,
|
||||
@INotificationService private readonly notifService: INotificationService,
|
||||
@ICodeUpdateService codeUpdateService: ICodeUpdateService,
|
||||
@IMetricsService metricsService: IMetricsService,
|
||||
@INotificationService notifService: INotificationService,
|
||||
@IUpdateService updateService: IUpdateService,
|
||||
) {
|
||||
super()
|
||||
const autoCheck = async () => {
|
||||
this.metricsService.capture('Code Update Startup: Checking...', {})
|
||||
const res = await this.codeUpdateService.check()
|
||||
if (!res) { notifyErrChecking(this.notifService); this.metricsService.capture('Code Update Startup: Error', { res }) }
|
||||
else if (res.hasUpdate) { notifyYesUpdate(this.notifService, res.message); this.metricsService.capture('Code Update Startup: Yes', { res }) }
|
||||
else if (!res.hasUpdate) { this.metricsService.capture('Code Update Startup: No', { res }) } // display nothing if up to date
|
||||
|
||||
const autoCheck = () => {
|
||||
performCodeCheck(false, notifService, codeUpdateService, metricsService, updateService)
|
||||
}
|
||||
|
||||
// check once 5 seconds after mount
|
||||
|
||||
@@ -1,57 +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 { URI } from '../../../../base/common/uri.js';
|
||||
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
|
||||
|
||||
|
||||
// service that manages state
|
||||
export type CodeUriState = {
|
||||
currentUri?: URI
|
||||
}
|
||||
|
||||
export interface ICodeUriStateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
readonly state: CodeUriState; // readonly to the user
|
||||
setState(newState: Partial<CodeUriState>): void;
|
||||
onDidChangeState: Event<void>;
|
||||
}
|
||||
|
||||
export const ICodeUriStateService = createDecorator<ICodeUriStateService>('codeUriStateService');
|
||||
class CodeUriStateService extends Disposable implements ICodeUriStateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
static readonly ID = 'codeUriStateService';
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
|
||||
|
||||
|
||||
// state
|
||||
state: CodeUriState
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super()
|
||||
|
||||
// initial state
|
||||
this.state = { currentUri: undefined }
|
||||
}
|
||||
|
||||
setState(newState: Partial<CodeUriState>) {
|
||||
|
||||
this.state = { ...this.state, ...newState }
|
||||
this._onDidChangeState.fire()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(ICodeUriStateService, CodeUriStateService, InstantiationType.Eager);
|
||||
@@ -9,11 +9,11 @@ import { ChatMessage } from '../common/chatThreadServiceTypes.js';
|
||||
import { getIsReasoningEnabledState, getReservedOutputTokenSpace, getModelCapabilities } from '../common/modelCapabilities.js';
|
||||
import { reParsedToolXMLString, chat_systemMessage } from '../common/prompt/prompts.js';
|
||||
import { AnthropicLLMChatMessage, AnthropicReasoning, GeminiLLMChatMessage, LLMChatMessage, LLMFIMMessage, OpenAILLMChatMessage, RawToolParamsObj } from '../common/sendLLMMessageTypes.js';
|
||||
import { IVoidSettingsService } from '../common/voidSettingsService.js';
|
||||
import { ChatMode, FeatureName, ModelSelection, ProviderName } from '../common/voidSettingsTypes.js';
|
||||
import { ICodeSettingsService } from '../common/codeSettingsService.js';
|
||||
import { ChatMode, FeatureName, ModelSelection, ProviderName } from '../common/codeSettingsTypes.js';
|
||||
import { IDirectoryStrService } from '../common/directoryStrService.js';
|
||||
import { ITerminalToolService } from './terminalToolService.js';
|
||||
import { IVoidModelService } from '../common/voidModelService.js';
|
||||
import { ICodeModelService } from '../common/codeModelService.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { EndOfLinePreference } from '../../../../editor/common/model.js';
|
||||
import { ToolName } from '../common/toolsServiceTypes.js';
|
||||
@@ -538,25 +538,25 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IDirectoryStrService private readonly directoryStrService: IDirectoryStrService,
|
||||
@ITerminalToolService private readonly terminalToolService: ITerminalToolService,
|
||||
@IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService,
|
||||
@IVoidModelService private readonly voidModelService: IVoidModelService,
|
||||
@ICodeSettingsService private readonly codeSettingsService: ICodeSettingsService,
|
||||
@ICodeModelService private readonly codeModelService: ICodeModelService,
|
||||
@IMCPService private readonly mcpService: IMCPService,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
// Read .voidrules files from workspace folders
|
||||
private _getVoidRulesFileContents(): string {
|
||||
private _getCodeRulesFileContents(): string {
|
||||
try {
|
||||
const workspaceFolders = this.workspaceContextService.getWorkspace().folders;
|
||||
let voidRules = '';
|
||||
let codeRules = '';
|
||||
for (const folder of workspaceFolders) {
|
||||
const uri = URI.joinPath(folder.uri, '.voidrules')
|
||||
const { model } = this.voidModelService.getModel(uri)
|
||||
const { model } = this.codeModelService.getModel(uri)
|
||||
if (!model) continue
|
||||
voidRules += model.getValue(EndOfLinePreference.LF) + '\n\n';
|
||||
codeRules += model.getValue(EndOfLinePreference.LF) + '\n\n';
|
||||
}
|
||||
return voidRules.trim();
|
||||
return codeRules.trim();
|
||||
}
|
||||
catch (e) {
|
||||
return ''
|
||||
@@ -565,12 +565,12 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
|
||||
|
||||
// Get combined AI instructions from settings and .voidrules files
|
||||
private _getCombinedAIInstructions(): string {
|
||||
const globalAIInstructions = this.voidSettingsService.state.globalSettings.aiInstructions;
|
||||
const voidRulesFileContent = this._getVoidRulesFileContents();
|
||||
const globalAIInstructions = this.codeSettingsService.state.globalSettings.aiInstructions;
|
||||
const codeRulesFileContent = this._getCodeRulesFileContents();
|
||||
|
||||
const ans: string[] = []
|
||||
if (globalAIInstructions) ans.push(globalAIInstructions)
|
||||
if (voidRulesFileContent) ans.push(voidRulesFileContent)
|
||||
if (codeRulesFileContent) ans.push(codeRulesFileContent)
|
||||
return ans.join('\n\n')
|
||||
}
|
||||
|
||||
@@ -637,7 +637,7 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
|
||||
prepareLLMSimpleMessages: IConvertToLLMMessageService['prepareLLMSimpleMessages'] = ({ simpleMessages, systemMessage, modelSelection, featureName }) => {
|
||||
if (modelSelection === null) return { messages: [], separateSystemMessage: undefined }
|
||||
|
||||
const { overridesOfModel } = this.voidSettingsService.state
|
||||
const { overridesOfModel } = this.codeSettingsService.state
|
||||
|
||||
const { providerName, modelName } = modelSelection
|
||||
const {
|
||||
@@ -646,7 +646,7 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
|
||||
supportsSystemMessage,
|
||||
} = getModelCapabilities(providerName, modelName, overridesOfModel)
|
||||
|
||||
const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName]
|
||||
const modelSelectionOptions = this.codeSettingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName]
|
||||
|
||||
// Get combined AI instructions
|
||||
const aiInstructions = this._getCombinedAIInstructions();
|
||||
@@ -670,7 +670,7 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
|
||||
prepareLLMChatMessages: IConvertToLLMMessageService['prepareLLMChatMessages'] = async ({ chatMessages, chatMode, modelSelection }) => {
|
||||
if (modelSelection === null) return { messages: [], separateSystemMessage: undefined }
|
||||
|
||||
const { overridesOfModel } = this.voidSettingsService.state
|
||||
const { overridesOfModel } = this.codeSettingsService.state
|
||||
|
||||
const { providerName, modelName } = modelSelection
|
||||
const {
|
||||
@@ -679,11 +679,11 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess
|
||||
supportsSystemMessage,
|
||||
} = getModelCapabilities(providerName, modelName, overridesOfModel)
|
||||
|
||||
const { disableSystemMessage } = this.voidSettingsService.state.globalSettings;
|
||||
const { disableSystemMessage } = this.codeSettingsService.state.globalSettings;
|
||||
const fullSystemMessage = await this._generateChatMessagesSystemMessage(chatMode, specialToolFormat)
|
||||
const systemMessage = disableSystemMessage ? '' : fullSystemMessage;
|
||||
|
||||
const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection['Chat'][modelSelection.providerName]?.[modelSelection.modelName]
|
||||
const modelSelectionOptions = this.codeSettingsService.state.optionsOfModelSelection['Chat'][modelSelection.providerName]?.[modelSelection.modelName]
|
||||
|
||||
// Get combined AI instructions
|
||||
const aiInstructions = this._getCombinedAIInstructions();
|
||||
|
||||
@@ -7,22 +7,22 @@ import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
|
||||
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
|
||||
import { IVoidModelService } from '../common/voidModelService.js';
|
||||
import { ICodeModelService } from '../common/codeModelService.js';
|
||||
|
||||
class ConvertContribWorkbenchContribution extends Disposable implements IWorkbenchContribution {
|
||||
static readonly ID = 'workbench.contrib.void.convertcontrib'
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IVoidModelService private readonly voidModelService: IVoidModelService,
|
||||
@ICodeModelService private readonly codeModelService: ICodeModelService,
|
||||
@IWorkspaceContextService private readonly workspaceContext: IWorkspaceContextService,
|
||||
) {
|
||||
super()
|
||||
|
||||
const initializeURI = (uri: URI) => {
|
||||
this.workspaceContext.getWorkspace()
|
||||
const voidRulesURI = URI.joinPath(uri, '.voidrules')
|
||||
this.voidModelService.initializeModel(voidRulesURI)
|
||||
const codeRulesURI = URI.joinPath(uri, '.voidrules')
|
||||
this.codeModelService.initializeModel(codeRulesURI)
|
||||
}
|
||||
|
||||
// call
|
||||
|
||||
@@ -23,10 +23,10 @@ import * as dom from '../../../../base/browser/dom.js';
|
||||
import { Widget } from '../../../../base/browser/ui/widget.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { IConsistentEditorItemService, IConsistentItemService } from './helperServices/consistentItemService.js';
|
||||
import { voidPrefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, defaultQuickEditFimTags, rewriteCode_systemMessage, rewriteCode_userMessage, searchReplaceGivenDescription_systemMessage, searchReplaceGivenDescription_userMessage, tripleTick, } from '../common/prompt/prompts.js';
|
||||
import { IVoidCommandBarService } from './voidCommandBarService.js';
|
||||
import { codePrefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, defaultQuickEditFimTags, rewriteCode_systemMessage, rewriteCode_userMessage, searchReplaceGivenDescription_systemMessage, searchReplaceGivenDescription_userMessage, tripleTick, } from '../common/prompt/prompts.js';
|
||||
import { ICodeCommandBarService } from './codeCommandBarService.js';
|
||||
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
|
||||
import { VOID_ACCEPT_DIFF_ACTION_ID, VOID_REJECT_DIFF_ACTION_ID } from './actionIDs.js';
|
||||
import { CODE_ACCEPT_DIFF_ACTION_ID, CODE_REJECT_DIFF_ACTION_ID } from './actionIDs.js';
|
||||
|
||||
import { mountCtrlK } from './react/out/quick-edit-tsx/index.js'
|
||||
import { QuickEditPropsType } from './quickEditActions.js';
|
||||
@@ -39,15 +39,15 @@ import { ILLMMessageService } from '../common/sendLLMMessageService.js';
|
||||
import { LLMChatMessage } from '../common/sendLLMMessageTypes.js';
|
||||
import { IMetricsService } from '../common/metricsService.js';
|
||||
import { IEditCodeService, AddCtrlKOpts, StartApplyingOpts, CallBeforeStartApplyingOpts, } from './editCodeServiceInterface.js';
|
||||
import { IVoidSettingsService } from '../common/voidSettingsService.js';
|
||||
import { FeatureName } from '../common/voidSettingsTypes.js';
|
||||
import { IVoidModelService } from '../common/voidModelService.js';
|
||||
import { ICodeSettingsService } from '../common/codeSettingsService.js';
|
||||
import { FeatureName } from '../common/codeSettingsTypes.js';
|
||||
import { ICodeModelService } from '../common/codeModelService.js';
|
||||
import { deepClone } from '../../../../base/common/objects.js';
|
||||
import { acceptBg, acceptBorder, buttonFontSize, buttonTextColor, rejectBg, rejectBorder } from '../common/helpers/colors.js';
|
||||
import { DiffArea, Diff, CtrlKZone, VoidFileSnapshot, DiffAreaSnapshotEntry, diffAreaSnapshotKeys, DiffZone, TrackingZone, ComputedDiff } from '../common/editCodeServiceTypes.js';
|
||||
import { DiffArea, Diff, CtrlKZone, CodeFileSnapshot, DiffAreaSnapshotEntry, diffAreaSnapshotKeys, DiffZone, TrackingZone, ComputedDiff } from '../common/editCodeServiceTypes.js';
|
||||
import { IConvertToLLMMessageService } from './convertToLLMMessageService.js';
|
||||
// import { isMacintosh } from '../../../../base/common/platform.js';
|
||||
// import { VOID_OPEN_SETTINGS_ACTION_ID } from './voidSettingsPane.js';
|
||||
// import { CODE_OPEN_SETTINGS_ACTION_ID } from './codeSettingsPane.js';
|
||||
|
||||
const numLinesOfStr = (str: string) => str.split('\n').length
|
||||
|
||||
@@ -192,9 +192,9 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
@IMetricsService private readonly _metricsService: IMetricsService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
// @ICommandService private readonly _commandService: ICommandService,
|
||||
@IVoidSettingsService private readonly _settingsService: IVoidSettingsService,
|
||||
@ICodeSettingsService private readonly _settingsService: ICodeSettingsService,
|
||||
// @IFileService private readonly _fileService: IFileService,
|
||||
@IVoidModelService private readonly _voidModelService: IVoidModelService,
|
||||
@ICodeModelService private readonly _codeModelService: ICodeModelService,
|
||||
@IConvertToLLMMessageService private readonly _convertToLLMMessageService: IConvertToLLMMessageService,
|
||||
) {
|
||||
super();
|
||||
@@ -203,7 +203,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
const registeredModelURIs = new Set<string>()
|
||||
const initializeModel = async (model: ITextModel) => {
|
||||
|
||||
await this._voidModelService.initializeModel(model.uri)
|
||||
await this._codeModelService.initializeModel(model.uri)
|
||||
|
||||
// do not add listeners to the same model twice - important, or will see duplicates
|
||||
if (registeredModelURIs.has(model.uri.fsPath)) return
|
||||
@@ -279,15 +279,15 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
// const details = errorDetails(e.fullError)
|
||||
// this._notificationService.notify({
|
||||
// severity: Severity.Warning,
|
||||
// message: `Void Error: ${e.message}`,
|
||||
// message: `Code Error: ${e.message}`,
|
||||
// actions: {
|
||||
// secondary: [{
|
||||
// id: 'void.onerror.opensettings',
|
||||
// enabled: true,
|
||||
// label: `Open Void's settings`,
|
||||
// label: `Open Code's settings`,
|
||||
// tooltip: '',
|
||||
// class: undefined,
|
||||
// run: () => { this._commandService.executeCommand(VOID_OPEN_SETTINGS_ACTION_ID) }
|
||||
// run: () => { this._commandService.executeCommand(CODE_OPEN_SETTINGS_ACTION_ID) }
|
||||
// }]
|
||||
// },
|
||||
// source: details ? `(Hold ${isMacintosh ? 'Option' : 'Alt'} to hover) - ${details}\n\nIf this persists, feel free to [report](https://github.com/voideditor/void/issues/new) it.` : undefined
|
||||
@@ -315,7 +315,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
|
||||
|
||||
private _addDiffAreaStylesToURI = (uri: URI) => {
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
|
||||
for (const diffareaid of this.diffAreasOfURI[uri.fsPath] || []) {
|
||||
const diffArea = this.diffAreaOfId[diffareaid]
|
||||
@@ -324,10 +324,10 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
// add sweep styles to the diffZone
|
||||
if (diffArea._streamState.isStreaming) {
|
||||
// sweepLine ... sweepLine
|
||||
const fn1 = this._addLineDecoration(model, diffArea._streamState.line, diffArea._streamState.line, 'void-sweepIdxBG')
|
||||
const fn1 = this._addLineDecoration(model, diffArea._streamState.line, diffArea._streamState.line, 'code-sweepIdxBG')
|
||||
// sweepLine+1 ... endLine
|
||||
const fn2 = diffArea._streamState.line + 1 <= diffArea.endLine ?
|
||||
this._addLineDecoration(model, diffArea._streamState.line + 1, diffArea.endLine, 'void-sweepBG')
|
||||
this._addLineDecoration(model, diffArea._streamState.line + 1, diffArea.endLine, 'code-sweepBG')
|
||||
: null
|
||||
diffArea._removeStylesFns.add(() => { fn1?.(); fn2?.(); })
|
||||
|
||||
@@ -336,7 +336,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
|
||||
else if (diffArea.type === 'CtrlKZone' && diffArea._linkedStreamingDiffZone === null) {
|
||||
// highlight zone's text
|
||||
const fn = this._addLineDecoration(model, diffArea.startLine, diffArea.endLine, 'void-highlightBG')
|
||||
const fn = this._addLineDecoration(model, diffArea.startLine, diffArea.endLine, 'code-highlightBG')
|
||||
diffArea._removeStylesFns.add(() => fn?.());
|
||||
}
|
||||
}
|
||||
@@ -344,7 +344,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
|
||||
|
||||
private _computeDiffsAndAddStylesToURI = (uri: URI) => {
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
if (model === null) return
|
||||
const fullFileText = model.getValue(EndOfLinePreference.LF)
|
||||
|
||||
@@ -477,11 +477,11 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
|
||||
const disposeInThisEditorFns: (() => void)[] = []
|
||||
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
|
||||
// green decoration and minimap decoration
|
||||
if (type !== 'deletion') {
|
||||
const fn = this._addLineDecoration(model, diff.startLine, diff.endLine, 'void-greenBG', {
|
||||
const fn = this._addLineDecoration(model, diff.startLine, diff.endLine, 'code-greenBG', {
|
||||
minimap: { color: { id: 'minimapGutter.addedBackground' }, position: 2 },
|
||||
overviewRuler: { color: { id: 'editorOverviewRuler.addedForeground' }, position: 7 }
|
||||
})
|
||||
@@ -496,7 +496,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
fn: (editor) => {
|
||||
|
||||
const domNode = document.createElement('div');
|
||||
domNode.className = 'void-redBG'
|
||||
domNode.className = 'code-redBG'
|
||||
|
||||
const renderOptions = RenderOptions.fromEditor(editor)
|
||||
|
||||
@@ -585,7 +585,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
offsetLines = 1
|
||||
}
|
||||
}
|
||||
else { throw new Error('Void 1') }
|
||||
else { throw new Error('Code 1') }
|
||||
|
||||
const buttonsWidget = this._instantiationService.createInstance(AcceptRejectInlineWidget, {
|
||||
editor,
|
||||
@@ -625,7 +625,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
|
||||
weAreWriting = false
|
||||
private _writeURIText(uri: URI, text: string, range_: IRange | 'wholeFileRange', { shouldRealignDiffAreas, }: { shouldRealignDiffAreas: boolean, }) {
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
if (!model) {
|
||||
this._refreshStylesAndDiffsInURI(uri) // at the end of a write, we still expect to refresh all styles. e.g. sometimes we expect to restore all the decorations even if no edits were made when _writeText is used
|
||||
return
|
||||
@@ -663,8 +663,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
|
||||
|
||||
|
||||
private _getCurrentVoidFileSnapshot = (uri: URI): VoidFileSnapshot => {
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
private _getCurrentCodeFileSnapshot = (uri: URI): CodeFileSnapshot => {
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
const snapshottedDiffAreaOfId: Record<string, DiffAreaSnapshotEntry> = {}
|
||||
|
||||
for (const diffareaid in this.diffAreaOfId) {
|
||||
@@ -687,7 +687,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
}
|
||||
|
||||
|
||||
private _restoreVoidFileSnapshot = async (uri: URI, snapshot: VoidFileSnapshot) => {
|
||||
private _restoreCodeFileSnapshot = async (uri: URI, snapshot: CodeFileSnapshot) => {
|
||||
// for each diffarea in this uri, stop streaming if currently streaming
|
||||
for (const diffareaid in this.diffAreaOfId) {
|
||||
const diffArea = this.diffAreaOfId[diffareaid]
|
||||
@@ -737,34 +737,34 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
}
|
||||
|
||||
private _addToHistory(uri: URI, opts?: { onWillUndo?: () => void }) {
|
||||
const beforeSnapshot: VoidFileSnapshot = this._getCurrentVoidFileSnapshot(uri)
|
||||
let afterSnapshot: VoidFileSnapshot | null = null
|
||||
const beforeSnapshot: CodeFileSnapshot = this._getCurrentCodeFileSnapshot(uri)
|
||||
let afterSnapshot: CodeFileSnapshot | null = null
|
||||
|
||||
const elt: IUndoRedoElement = {
|
||||
type: UndoRedoElementType.Resource,
|
||||
resource: uri,
|
||||
label: 'Void Agent',
|
||||
label: 'Code Agent',
|
||||
code: 'undoredo.editCode',
|
||||
undo: async () => { opts?.onWillUndo?.(); await this._restoreVoidFileSnapshot(uri, beforeSnapshot) },
|
||||
redo: async () => { if (afterSnapshot) await this._restoreVoidFileSnapshot(uri, afterSnapshot) }
|
||||
undo: async () => { opts?.onWillUndo?.(); await this._restoreCodeFileSnapshot(uri, beforeSnapshot) },
|
||||
redo: async () => { if (afterSnapshot) await this._restoreCodeFileSnapshot(uri, afterSnapshot) }
|
||||
}
|
||||
this._undoRedoService.pushElement(elt)
|
||||
|
||||
const onFinishEdit = async () => {
|
||||
afterSnapshot = this._getCurrentVoidFileSnapshot(uri)
|
||||
await this._voidModelService.saveModel(uri)
|
||||
afterSnapshot = this._getCurrentCodeFileSnapshot(uri)
|
||||
await this._codeModelService.saveModel(uri)
|
||||
}
|
||||
return { onFinishEdit }
|
||||
}
|
||||
|
||||
|
||||
public getVoidFileSnapshot(uri: URI) {
|
||||
return this._getCurrentVoidFileSnapshot(uri)
|
||||
public getCodeFileSnapshot(uri: URI) {
|
||||
return this._getCurrentCodeFileSnapshot(uri)
|
||||
}
|
||||
|
||||
|
||||
public restoreVoidFileSnapshot(uri: URI, snapshot: VoidFileSnapshot): void {
|
||||
this._restoreVoidFileSnapshot(uri, snapshot)
|
||||
public restoreCodeFileSnapshot(uri: URI, snapshot: CodeFileSnapshot): void {
|
||||
this._restoreCodeFileSnapshot(uri, snapshot)
|
||||
}
|
||||
|
||||
|
||||
@@ -997,7 +997,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
else if (lastDiff.type === 'deletion')
|
||||
endLineInLlmTextSoFar = lastDiff.startLine
|
||||
else
|
||||
throw new Error(`Void: diff.type not recognized on: ${lastDiff}`)
|
||||
throw new Error(`Code: diff.type not recognized on: ${lastDiff}`)
|
||||
}
|
||||
|
||||
// at the start, add a newline between the stream and originalCode to make reasoning easier
|
||||
@@ -1127,8 +1127,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
public async callBeforeApplyOrEdit(givenURI: URI | 'current') {
|
||||
const uri = this._uriOfGivenURI(givenURI)
|
||||
if (!uri) return
|
||||
await this._voidModelService.initializeModel(uri)
|
||||
await this._voidModelService.saveModel(uri) // save the URI
|
||||
await this._codeModelService.initializeModel(uri)
|
||||
await this._codeModelService.saveModel(uri) // save the URI
|
||||
}
|
||||
|
||||
|
||||
@@ -1269,7 +1269,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
linkedCtrlKZone: CtrlKZone | null,
|
||||
onWillUndo: () => void,
|
||||
}) {
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
if (!model) return
|
||||
|
||||
// treat like full file, unless linkedCtrlKZone was provided in which case use its diff's range
|
||||
@@ -1376,10 +1376,10 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
startRange = [startLine_, endLine_]
|
||||
}
|
||||
else {
|
||||
throw new Error(`Void: diff.type not recognized on: ${from}`)
|
||||
throw new Error(`Code: diff.type not recognized on: ${from}`)
|
||||
}
|
||||
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
if (!model) return
|
||||
|
||||
let streamRequestIdRef: { current: string | null } = { current: null } // can use this as a proxy to set the diffArea's stream state requestId
|
||||
@@ -1408,7 +1408,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
|
||||
const startLine = startRange === 'fullFile' ? 1 : startRange[0]
|
||||
const endLine = startRange === 'fullFile' ? model.getLineCount() : startRange[1]
|
||||
const { prefix, suffix } = voidPrefixAndSuffix({ fullFileStr: originalFileCode, startLine, endLine })
|
||||
const { prefix, suffix } = codePrefixAndSuffix({ fullFileStr: originalFileCode, startLine, endLine })
|
||||
const userContent = ctrlKStream_userMessage({ selection: originalCode, instructions: instructions, prefix, suffix, fimTags: quickEditFIMTags, language })
|
||||
|
||||
const { messages: a, separateSystemMessage: b } = this._convertToLLMMessageService.prepareLLMSimpleMessages({
|
||||
@@ -1480,7 +1480,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
else if (from === 'ClickApply') {
|
||||
return extractCodeFromRegular({ text: fullText, recentlyAddedTextLen })
|
||||
}
|
||||
throw new Error('Void 1')
|
||||
throw new Error('Code 1')
|
||||
}
|
||||
|
||||
// refresh now in case onText takes a while to get 1st message
|
||||
@@ -1578,7 +1578,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
_fileLengthOfGivenURI(givenURI: URI | 'current') {
|
||||
const uri = this._uriOfGivenURI(givenURI)
|
||||
if (!uri) return null
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
if (!model) return null
|
||||
const numCharsInFile = model.getValueLength(EndOfLinePreference.LF)
|
||||
return numCharsInFile
|
||||
@@ -1617,7 +1617,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
const blocks = extractSearchReplaceBlocks(blocksStr)
|
||||
if (blocks.length === 0) throw new Error(`No Search/Replace blocks were received!`)
|
||||
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
if (!model) throw new Error(`Error applying Search/Replace blocks: File does not exist.`)
|
||||
const modelStr = model.getValue(EndOfLinePreference.LF)
|
||||
// .split('\n').map(l => '\t' + l).join('\n') // for testing purposes only, remember to remove this
|
||||
@@ -1678,7 +1678,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
const uri = this._getURIBeforeStartApplying(opts)
|
||||
if (!uri) return
|
||||
|
||||
const { model } = this._voidModelService.getModel(uri)
|
||||
const { model } = this._codeModelService.getModel(uri)
|
||||
if (!model) return
|
||||
|
||||
let streamRequestIdRef: { current: string | null } = { current: null } // can use this as a proxy to set the diffArea's stream state requestId
|
||||
@@ -1968,7 +1968,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
|
||||
const blocks = extractSearchReplaceBlocks(fullText)
|
||||
if (blocks.length === 0) {
|
||||
this._notificationService.info(`Void: We ran Fast Apply, but the LLM didn't output any changes.`)
|
||||
this._notificationService.info(`Code: We ran Fast Apply, but the LLM didn't output any changes.`)
|
||||
}
|
||||
this._writeURIText(uri, originalFileCode, 'wholeFileRange', { shouldRealignDiffAreas: true })
|
||||
|
||||
@@ -2158,7 +2158,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
].join('\n')
|
||||
}
|
||||
else {
|
||||
throw new Error(`Void error: ${diff}.type not recognized`)
|
||||
throw new Error(`Code error: ${diff}.type not recognized`)
|
||||
}
|
||||
|
||||
// console.log('DIFF', diff)
|
||||
@@ -2250,7 +2250,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
||||
toRange = { startLineNumber: diff.startLine, startColumn: 1, endLineNumber: diff.endLine, endColumn: Number.MAX_SAFE_INTEGER } // 1-indexed
|
||||
}
|
||||
else {
|
||||
throw new Error(`Void error: ${diff}.type not recognized`)
|
||||
throw new Error(`Code error: ${diff}.type not recognized`)
|
||||
}
|
||||
|
||||
// update the file
|
||||
@@ -2305,7 +2305,7 @@ class AcceptRejectInlineWidget extends Widget implements IOverlayWidget {
|
||||
startLine: number,
|
||||
offsetLines: number
|
||||
},
|
||||
@IVoidCommandBarService private readonly _voidCommandBarService: IVoidCommandBarService,
|
||||
@ICodeCommandBarService private readonly _codeCommandBarService: ICodeCommandBarService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@IEditCodeService private readonly _editCodeService: IEditCodeService,
|
||||
) {
|
||||
@@ -2328,15 +2328,15 @@ class AcceptRejectInlineWidget extends Widget implements IOverlayWidget {
|
||||
const lineHeight = editor.getOption(EditorOption.lineHeight);
|
||||
|
||||
const getAcceptRejectText = () => {
|
||||
const acceptKeybinding = this._keybindingService.lookupKeybinding(VOID_ACCEPT_DIFF_ACTION_ID);
|
||||
const rejectKeybinding = this._keybindingService.lookupKeybinding(VOID_REJECT_DIFF_ACTION_ID);
|
||||
const acceptKeybinding = this._keybindingService.lookupKeybinding(CODE_ACCEPT_DIFF_ACTION_ID);
|
||||
const rejectKeybinding = this._keybindingService.lookupKeybinding(CODE_REJECT_DIFF_ACTION_ID);
|
||||
|
||||
// Use the standalone function directly since we're in a nested class that
|
||||
// can't access EditCodeService's methods
|
||||
const acceptKeybindLabel = this._editCodeService.processRawKeybindingText(acceptKeybinding && acceptKeybinding.getLabel() || '');
|
||||
const rejectKeybindLabel = this._editCodeService.processRawKeybindingText(rejectKeybinding && rejectKeybinding.getLabel() || '');
|
||||
|
||||
const commandBarStateAtUri = this._voidCommandBarService.stateOfURI[uri.fsPath];
|
||||
const commandBarStateAtUri = this._codeCommandBarService.stateOfURI[uri.fsPath];
|
||||
const selectedDiffIdx = commandBarStateAtUri?.diffIdx ?? 0; // 0th item is selected by default
|
||||
const thisDiffIdx = commandBarStateAtUri?.sortedDiffIds.indexOf(diffid) ?? null;
|
||||
|
||||
@@ -2435,7 +2435,7 @@ class AcceptRejectInlineWidget extends Widget implements IOverlayWidget {
|
||||
|
||||
|
||||
// Listen for state changes in the command bar service
|
||||
this._register(this._voidCommandBarService.onDidChangeState(e => {
|
||||
this._register(this._codeCommandBarService.onDidChangeState(e => {
|
||||
if (uri && e.uri.fsPath === uri.fsPath) {
|
||||
|
||||
const { acceptText, rejectText } = getAcceptRejectText()
|
||||
|
||||
@@ -7,7 +7,7 @@ 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';
|
||||
import { Diff, DiffArea, CodeFileSnapshot } from '../common/editCodeServiceTypes.js';
|
||||
|
||||
|
||||
export type StartBehavior = 'accept-conflicts' | 'reject-conflicts' | 'keep-conflicts'
|
||||
@@ -73,6 +73,6 @@ export interface IEditCodeService {
|
||||
interruptURIStreaming(opts: { uri: URI }): void;
|
||||
|
||||
// testDiffs(): void;
|
||||
getVoidFileSnapshot(uri: URI): VoidFileSnapshot;
|
||||
restoreVoidFileSnapshot(uri: URI, snapshot: VoidFileSnapshot): void;
|
||||
getCodeFileSnapshot(uri: URI): CodeFileSnapshot;
|
||||
restoreCodeFileSnapshot(uri: URI, snapshot: CodeFileSnapshot): void;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ const extensionBlacklist = [
|
||||
// ignore extensions
|
||||
'ms-vscode-remote.remote', // ms-vscode-remote.remote-ssh, ms-vscode-remote.remote-wsl
|
||||
'ms-vscode.remote', // ms-vscode.remote-explorer
|
||||
// ignore other AI copilots that could conflict with Void keybindings
|
||||
// ignore other AI copilots that could conflict with Code keybindings
|
||||
'sourcegraph.cody-ai',
|
||||
'continue.continue',
|
||||
'codeium.codeium',
|
||||
@@ -195,10 +195,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
if (fromEditor === 'VS Code') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.vscode', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'),
|
||||
@@ -207,10 +207,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
} else if (fromEditor === 'Cursor') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.cursor', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'),
|
||||
@@ -219,10 +219,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
} else if (fromEditor === 'Windsurf') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Windsurf', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Windsurf', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.windsurf', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'),
|
||||
@@ -238,10 +238,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
if (fromEditor === 'VS Code') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.vscode', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'),
|
||||
@@ -250,10 +250,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
} else if (fromEditor === 'Cursor') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Cursor', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Cursor', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.cursor', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'),
|
||||
@@ -262,10 +262,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
} else if (fromEditor === 'Windsurf') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Windsurf', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Windsurf', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.windsurf', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'),
|
||||
@@ -283,10 +283,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
if (fromEditor === 'VS Code') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.vscode', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.void-editor', 'extensions'),
|
||||
@@ -295,10 +295,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
} else if (fromEditor === 'Cursor') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Cursor', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Cursor', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.cursor', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.void-editor', 'extensions'),
|
||||
@@ -307,10 +307,10 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEdit
|
||||
} else if (fromEditor === 'Windsurf') {
|
||||
return [{
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Windsurf', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'settings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'settings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Windsurf', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'keybindings.json'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'keybindings.json'),
|
||||
}, {
|
||||
from: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.windsurf', 'extensions'),
|
||||
to: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.void-editor', 'extensions'),
|
||||
|
||||
@@ -7,17 +7,17 @@ import { IFileService } from '../../../../platform/files/common/files.js';
|
||||
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
||||
import { IDirectoryStrService } from '../common/directoryStrService.js';
|
||||
import { messageOfSelection } from '../common/prompt/prompts.js';
|
||||
import { IVoidModelService } from '../common/voidModelService.js';
|
||||
import { ICodeModelService } from '../common/codeModelService.js';
|
||||
|
||||
|
||||
|
||||
class FilePromptActionService extends Action2 {
|
||||
private static readonly VOID_COPY_FILE_PROMPT_ID = 'void.copyfileprompt'
|
||||
private static readonly CODE_COPY_FILE_PROMPT_ID = 'void.copyfileprompt'
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: FilePromptActionService.VOID_COPY_FILE_PROMPT_ID,
|
||||
title: localize2('voidCopyPrompt', 'Void: Copy Prompt'),
|
||||
id: FilePromptActionService.CODE_COPY_FILE_PROMPT_ID,
|
||||
title: localize2('codeCopyPrompt', 'Code: Copy Prompt'),
|
||||
menu: [{
|
||||
id: MenuId.ExplorerContext,
|
||||
group: '8_void',
|
||||
@@ -31,7 +31,7 @@ class FilePromptActionService extends Action2 {
|
||||
const fileService = accessor.get(IFileService);
|
||||
const clipboardService = accessor.get(IClipboardService)
|
||||
const directoryStrService = accessor.get(IDirectoryStrService)
|
||||
const voidModelService = accessor.get(IVoidModelService)
|
||||
const codeModelService = accessor.get(ICodeModelService)
|
||||
|
||||
const stat = await fileService.stat(uri)
|
||||
|
||||
@@ -45,7 +45,7 @@ class FilePromptActionService extends Action2 {
|
||||
m = await messageOfSelection({
|
||||
type: 'File',
|
||||
uri,
|
||||
language: (await voidModelService.getModelSafe(uri)).model?.getLanguageId() || '',
|
||||
language: (await codeModelService.getModelSafe(uri)).model?.getLanguageId() || '',
|
||||
state: { wasAddedAsCurrentFile: false, },
|
||||
}, {
|
||||
folderOpts,
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
|
||||
// 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];
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/*--------------------------------------------------------------------------------------
|
||||
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
class SurroundingsRemover {
|
||||
readonly originalS: string
|
||||
i: number
|
||||
j: number
|
||||
|
||||
// string is s[i...j]
|
||||
|
||||
constructor(s: string) {
|
||||
this.originalS = s
|
||||
this.i = 0
|
||||
this.j = s.length - 1
|
||||
}
|
||||
value() {
|
||||
return this.originalS.substring(this.i, this.j + 1)
|
||||
}
|
||||
|
||||
// returns whether it removed the whole prefix
|
||||
removePrefix = (prefix: string): boolean => {
|
||||
let offset = 0
|
||||
// console.log('prefix', prefix, Math.min(this.j, prefix.length - 1))
|
||||
while (this.i <= this.j && offset <= prefix.length - 1) {
|
||||
if (this.originalS.charAt(this.i) !== prefix.charAt(offset))
|
||||
break
|
||||
offset += 1
|
||||
this.i += 1
|
||||
}
|
||||
return offset === prefix.length
|
||||
}
|
||||
|
||||
// // removes suffix from right to left
|
||||
removeSuffix = (suffix: string): boolean => {
|
||||
// e.g. suffix = <PRE/>, the string is <PRE>hi<P
|
||||
const s = this.value()
|
||||
// for every possible prefix of `suffix`, check if string ends with it
|
||||
for (let len = Math.min(s.length, suffix.length); len >= 1; len -= 1) {
|
||||
if (s.endsWith(suffix.substring(0, len))) { // the end of the string equals a prefix
|
||||
this.j -= len
|
||||
return len === suffix.length
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
// removeSuffix = (suffix: string): boolean => {
|
||||
// let offset = 0
|
||||
|
||||
// while (this.j >= Math.max(this.i, 0)) {
|
||||
// if (this.originalS.charAt(this.j) !== suffix.charAt(suffix.length - 1 - offset))
|
||||
// break
|
||||
// offset += 1
|
||||
// this.j -= 1
|
||||
// }
|
||||
// return offset === suffix.length
|
||||
// }
|
||||
|
||||
removeFromStartUntil = (until: string, alsoRemoveUntilStr: boolean) => {
|
||||
const index = this.originalS.indexOf(until, this.i)
|
||||
|
||||
if (index === -1) {
|
||||
this.i = this.j + 1
|
||||
return false
|
||||
}
|
||||
// console.log('index', index, until.length)
|
||||
|
||||
if (alsoRemoveUntilStr)
|
||||
this.i = index + until.length
|
||||
else
|
||||
this.i = index
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
removeCodeBlock = () => {
|
||||
// Match either:
|
||||
// 1. ```language\n<code>\n```\n?
|
||||
// 2. ```<code>\n```\n?
|
||||
|
||||
const pm = this
|
||||
const foundCodeBlock = pm.removePrefix('```')
|
||||
if (!foundCodeBlock) return false
|
||||
|
||||
pm.removeFromStartUntil('\n', true) // language
|
||||
|
||||
const j = pm.j
|
||||
let foundCodeBlockEnd = pm.removeSuffix('```')
|
||||
|
||||
if (pm.j === j) foundCodeBlockEnd = pm.removeSuffix('```\n') // if no change, try again with \n after ```
|
||||
|
||||
if (!foundCodeBlockEnd) return false
|
||||
|
||||
pm.removeSuffix('\n') // remove the newline before ```
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
deltaInfo = (recentlyAddedTextLen: number) => {
|
||||
// aaaaaatextaaaaaa{recentlyAdded}
|
||||
// ^ i j len
|
||||
// |
|
||||
// recentyAddedIdx
|
||||
const recentlyAddedIdx = this.originalS.length - recentlyAddedTextLen
|
||||
const actualDelta = this.originalS.substring(Math.max(this.i, recentlyAddedIdx), this.j + 1)
|
||||
const ignoredSuffix = this.originalS.substring(Math.max(this.j + 1, recentlyAddedIdx), Infinity)
|
||||
return [actualDelta, ignoredSuffix] as const
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const extractCodeFromRegular = ({ text, recentlyAddedTextLen }: { text: string, recentlyAddedTextLen: number }): [string, string, string] => {
|
||||
|
||||
const pm = new SurroundingsRemover(text)
|
||||
|
||||
pm.removeCodeBlock()
|
||||
|
||||
const s = pm.value()
|
||||
const [delta, ignoredSuffix] = pm.deltaInfo(recentlyAddedTextLen)
|
||||
|
||||
return [s, delta, ignoredSuffix]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Ollama has its own FIM, we should not use this if we use that
|
||||
export const extractCodeFromFIM = ({ text, recentlyAddedTextLen, midTag, }: { text: string, recentlyAddedTextLen: number, midTag: string }): [string, string, string] => {
|
||||
|
||||
/* ------------- summary of the regex -------------
|
||||
[optional ` | `` | ```]
|
||||
(match optional_language_name)
|
||||
[optional strings here]
|
||||
[required <MID> tag]
|
||||
(match the stuff between mid tags)
|
||||
[optional <MID/> tag]
|
||||
[optional ` | `` | ```]
|
||||
*/
|
||||
|
||||
const pm = new SurroundingsRemover(text)
|
||||
|
||||
pm.removeCodeBlock()
|
||||
|
||||
const foundMid = pm.removePrefix(`<${midTag}>`)
|
||||
|
||||
if (foundMid) {
|
||||
pm.removeSuffix(`</${midTag}>`)
|
||||
}
|
||||
const s = pm.value()
|
||||
const [delta, ignoredSuffix] = pm.deltaInfo(recentlyAddedTextLen)
|
||||
|
||||
return [s, delta, ignoredSuffix]
|
||||
|
||||
|
||||
// // const regex = /[\s\S]*?(?:`{1,3}\s*([a-zA-Z_]+[\w]*)?[\s\S]*?)?<MID>([\s\S]*?)(?:<\/MID>|`{1,3}|$)/;
|
||||
// const regex = new RegExp(
|
||||
// `[\\s\\S]*?(?:\`{1,3}\\s*([a-zA-Z_]+[\\w]*)?[\\s\\S]*?)?<${midTag}>([\\s\\S]*?)(?:</${midTag}>|\`{1,3}|$)`,
|
||||
// ''
|
||||
// );
|
||||
// const match = text.match(regex);
|
||||
// if (match) {
|
||||
// const [_, languageName, codeBetweenMidTags] = match;
|
||||
// return [languageName, codeBetweenMidTags] as const
|
||||
|
||||
// } else {
|
||||
// return [undefined, extractCodeFromRegular(text)] as const
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { URI } from '../../../../../base/common/uri'
|
||||
import { EndOfLinePreference } from '../../../../../editor/common/model'
|
||||
import { IModelService } from '../../../../../editor/common/services/model.js'
|
||||
|
||||
// read files from VSCode
|
||||
export const VSReadFile = async (modelService: IModelService, uri: URI): Promise<string | null> => {
|
||||
const model = modelService.getModel(uri)
|
||||
if (!model) return null
|
||||
return model.getValue(EndOfLinePreference.LF)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,28 +3,29 @@
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .code-sweepIdxBG {
|
||||
background-color: var(--vscode-code-sweepIdxBG);
|
||||
.monaco-editor .void-sweepIdxBG {
|
||||
background-color: var(--vscode-void-sweepIdxBG);
|
||||
}
|
||||
|
||||
.code-sweepBG {
|
||||
background-color: var(--vscode-code-sweepBG);
|
||||
.void-sweepBG {
|
||||
background-color: var(--vscode-void-sweepBG);
|
||||
}
|
||||
|
||||
.code-highlightBG {
|
||||
background-color: var(--vscode-code-highlightBG);
|
||||
.void-highlightBG {
|
||||
background-color: var(--vscode-void-highlightBG);
|
||||
}
|
||||
|
||||
.code-greenBG {
|
||||
background-color: var(--vscode-code-greenBG);
|
||||
.void-greenBG {
|
||||
background-color: var(--vscode-void-greenBG);
|
||||
}
|
||||
|
||||
.code-redBG {
|
||||
background-color: var(--vscode-code-redBG);
|
||||
.void-redBG {
|
||||
background-color: var(--vscode-void-redBG);
|
||||
}
|
||||
|
||||
.code-watermark-button {
|
||||
margin: 8px 0;
|
||||
|
||||
/* Renamed from void-watermark-button to void-openfolder-button */
|
||||
.void-openfolder-button {
|
||||
padding: 8px 20px;
|
||||
background-color: #306dce;
|
||||
color: white;
|
||||
@@ -54,20 +55,18 @@
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.code-watermark-button:hover {
|
||||
background-color: #2563eb;
|
||||
.void-openssh-button:hover {
|
||||
background-color: #474747; /* Darker on hover */
|
||||
}
|
||||
.code-watermark-button:active {
|
||||
background-color: #2563eb;
|
||||
.void-openssh-button:active {
|
||||
background-color: #474747;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.code-settings-watermark-button {
|
||||
margin: 8px 0;
|
||||
padding: 8px 20px;
|
||||
background-color: var(--vscode-input-background);
|
||||
.void-settings-watermark-button {
|
||||
margin: 8px 0;
|
||||
padding: 8px 20px;
|
||||
background-color: var(--vscode-input-background);
|
||||
color: var(--vscode-input-foreground);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
@@ -76,22 +75,22 @@
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.code-settings-watermark-button:hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
.code-settings-watermark-button:active {
|
||||
|
||||
.void-settings-watermark-button:hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.void-settings-watermark-button:active {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.code-link {
|
||||
.void-link {
|
||||
color: #3b82f6;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.code-link:hover {
|
||||
|
||||
.void-link:hover {
|
||||
opacity: 80%;
|
||||
}
|
||||
|
||||
@@ -165,89 +164,41 @@
|
||||
/* For Firefox */
|
||||
}
|
||||
|
||||
.code-scrollable-element::-webkit-scrollbar,
|
||||
.code-scrollable-element *::-webkit-scrollbar {
|
||||
width: 14px !important;
|
||||
height: 14px !important;
|
||||
.void-scrollable-element::-webkit-scrollbar,
|
||||
.void-scrollable-element *::-webkit-scrollbar {
|
||||
width: var(--scrollbar-vertical-width) !important;
|
||||
height: var(--scrollbar-horizontal-height) !important;
|
||||
background-color: var(--void-bg-3) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element::-webkit-scrollbar-track,
|
||||
.code-scrollable-element *::-webkit-scrollbar-track {
|
||||
background: transparent !important;
|
||||
.void-scrollable-element::-webkit-scrollbar-thumb,
|
||||
.void-scrollable-element *::-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;
|
||||
}
|
||||
|
||||
.code-scrollable-element::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element *::-webkit-scrollbar-thumb {
|
||||
background-color: transparent !important;
|
||||
border-radius: 0px !important;
|
||||
.void-scrollable-element::-webkit-scrollbar-thumb:hover,
|
||||
.void-scrollable-element *::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--void-bg-1) !important;
|
||||
filter: brightness(1.1) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element::-webkit-scrollbar-thumb:hover,
|
||||
.code-scrollable-element *::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--vscode-scrollbarSlider-hoverBackground) !important;
|
||||
.void-scrollable-element::-webkit-scrollbar-thumb:active,
|
||||
.void-scrollable-element *::-webkit-scrollbar-thumb:active {
|
||||
background-color: var(--void-bg-1) !important;
|
||||
filter: brightness(1.2) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element::-webkit-scrollbar-thumb:active,
|
||||
.code-scrollable-element *::-webkit-scrollbar-thumb:active {
|
||||
background-color: var(--vscode-scrollbarSlider-activeBackground) !important;
|
||||
.void-scrollable-element::-webkit-scrollbar-track,
|
||||
.void-scrollable-element *::-webkit-scrollbar-track {
|
||||
background-color: var(--void-bg-3) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element::-webkit-scrollbar-corner,
|
||||
.code-scrollable-element *::-webkit-scrollbar-corner {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-0::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-0 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 0%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-1::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-1 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 10%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-2::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-2 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 20%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-3::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-3 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 30%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-4::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-4 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 40%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-5::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-5 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 50%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-6::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-6 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 60%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-7::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-7 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 70%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-8::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-8 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 80%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-9::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-9 *::-webkit-scrollbar-thumb {
|
||||
background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 90%, transparent) !important;
|
||||
}
|
||||
|
||||
.code-scrollable-element.show-scrollbar-10::-webkit-scrollbar-thumb,
|
||||
.code-scrollable-element.show-scrollbar-10 *::-webkit-scrollbar-thumb {
|
||||
background-color: var(--vscode-scrollbarSlider-background) !important;
|
||||
.void-scrollable-element::-webkit-scrollbar-corner,
|
||||
.void-scrollable-element *::-webkit-scrollbar-corner {
|
||||
background-color: var(--void-bg-3) !important;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ export interface IMetricsPollService {
|
||||
|
||||
const PING_EVERY_MS = 15 * 1000 * 60 // 15 minutes
|
||||
|
||||
export const IMetricsPollService = createDecorator<IMetricsPollService>('voidMetricsPollService');
|
||||
export const IMetricsPollService = createDecorator<IMetricsPollService>('codeMetricsPollService');
|
||||
class MetricsPollService extends Disposable implements IMetricsPollService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
static readonly ID = 'voidMetricsPollService';
|
||||
static readonly ID = 'codeMetricsPollService';
|
||||
|
||||
|
||||
private readonly intervalID: number
|
||||
|
||||
@@ -13,7 +13,7 @@ import { getActiveWindow } from '../../../../base/browser/dom.js';
|
||||
|
||||
// Onboarding contribution that mounts the component at startup
|
||||
export class MiscWorkbenchContribs extends Disposable implements IWorkbenchContribution {
|
||||
static readonly ID = 'workbench.contrib.voidMiscWorkbenchContribs';
|
||||
static readonly ID = 'workbench.contrib.codeMiscWorkbenchContribs';
|
||||
|
||||
constructor(
|
||||
@IExtensionTransferService private readonly extensionTransferService: IExtensionTransferService,
|
||||
@@ -26,7 +26,7 @@ export class MiscWorkbenchContribs extends Disposable implements IWorkbenchContr
|
||||
private initialize(): void {
|
||||
|
||||
// delete blacklisted extensions once (this is for people who already installed them)
|
||||
const deleteExtensionsStorageId = 'void-deleted-blacklist-2'
|
||||
const deleteExtensionsStorageId = 'code-deleted-blacklist-2'
|
||||
const alreadyDeleted = this.storageService.get(deleteExtensionsStorageId, StorageScope.APPLICATION)
|
||||
if (!alreadyDeleted) {
|
||||
this.storageService.store(deleteExtensionsStorageId, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE)
|
||||
|
||||
@@ -1,326 +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 '../chatThreadService.js';
|
||||
import { VSReadFile } from '../helpers/readFile.js';
|
||||
import { IModelService } from '../../../../../editor/common/services/model.js';
|
||||
|
||||
export const chat_systemMessage = `\
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## EXAMPLE 1
|
||||
FILES
|
||||
math.ts
|
||||
\`\`\`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)
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
|
||||
SELECTIONS
|
||||
math.ts (lines 3:3)
|
||||
\`\`\`typescript
|
||||
const subtractNumbers = (a, b) => a - b
|
||||
\`\`\`
|
||||
|
||||
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:
|
||||
\`\`\`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
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
|
||||
## EXAMPLE 2
|
||||
FILES
|
||||
fib.ts
|
||||
\`\`\`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)
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
SELECTIONS
|
||||
fib.ts (lines 10:10)
|
||||
\`\`\`typescript
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
\`\`\`
|
||||
|
||||
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:
|
||||
\`\`\`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];
|
||||
}
|
||||
\`\`\`
|
||||
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 = FileSelection & { content: string }
|
||||
const stringifyFileSelection = ({ fileURI, selectionStr, range, content }: FileSelnLocal) => {
|
||||
return `\
|
||||
${fileURI.fsPath}
|
||||
\`\`\`${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
|
||||
${content}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
const stringifyCodeSelection = ({ fileURI, selectionStr, range }: CodeSelection) => {
|
||||
return `\
|
||||
${fileURI.fsPath} (lines ${range.startLineNumber}:${range.endLineNumber})
|
||||
\`\`\`${filenameToVscodeLanguage(fileURI.fsPath) ?? ''}
|
||||
${selectionStr}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
|
||||
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[], modelService: IModelService) => {
|
||||
if (fileSelections.length === 0) return null
|
||||
const fileSlns: FileSelnLocal[] = await Promise.all(fileSelections.map(async (sel) => {
|
||||
const content = await VSReadFile(modelService, 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')
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const chat_userMessage = async (instructions: string, selections: StagingSelectionItem[] | null, modelService: IModelService) => {
|
||||
const fileSelections = selections?.filter(s => s.type === 'File') as FileSelection[]
|
||||
const codeSelections = selections?.filter(s => s.type === 'Selection') as CodeSelection[]
|
||||
|
||||
const filesStr = await stringifyFileSelections(fileSelections, modelService)
|
||||
const codeStr = stringifyCodeSelections(codeSelections)
|
||||
|
||||
let str = ''
|
||||
if (filesStr) str += `FILES\n${filesStr}\n`
|
||||
if (codeStr) str += `SELECTIONS\n${codeStr}\n`
|
||||
str += `INSTRUCTIONS\n${instructions}`
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
export const fastApply_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 fastApply_userMessage = ({ originalCode, applyStr, uri }: { originalCode: string, applyStr: string, uri: URI }) => {
|
||||
|
||||
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
|
||||
|
||||
return `\
|
||||
ORIGINAL_FILE
|
||||
\`\`\`${language}
|
||||
${originalCode}
|
||||
\`\`\`
|
||||
|
||||
CHANGE
|
||||
\`\`\`
|
||||
${applyStr}
|
||||
\`\`\`
|
||||
|
||||
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 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 FimTagsType = {
|
||||
preTag: string,
|
||||
sufTag: string,
|
||||
midTag: string
|
||||
}
|
||||
export const defaultFimTags: FimTagsType = {
|
||||
preTag: 'ABOVE',
|
||||
sufTag: 'BELOW',
|
||||
midTag: 'SELECTION',
|
||||
}
|
||||
|
||||
// this should probably be longer
|
||||
export const ctrlKStream_systemMessage = ({ fimTags: { preTag, midTag, sufTag } }: { fimTags: FimTagsType }) => {
|
||||
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: FimTagsType, 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
|
||||
\`\`\`${language}
|
||||
<${midTag}>${selection}</${midTag}>
|
||||
\`\`\`
|
||||
|
||||
INSTRUCTIONS
|
||||
${instructions}
|
||||
|
||||
<${preTag}>${prefix}</${preTag}>
|
||||
<${sufTag}>${suffix}</${sufTag}>
|
||||
|
||||
Return only the completion block of code (of the form \`\`\`${language}
|
||||
<${midTag}>...new code</${midTag}>
|
||||
\`\`\`).`
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ registerAction2(class extends Action2 {
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KeyK,
|
||||
weight: KeybindingWeight.CodeExtension,
|
||||
when: ContextKeyExpr.deserialize('editorFocus && !terminalFocus'),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 CodeQuickEditState = {
|
||||
quickEditsOfDocument: { [uri: string]: QuickEdit }
|
||||
}
|
||||
|
||||
export interface IQuickEditStateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
readonly state: CodeQuickEditState; // readonly to the user
|
||||
setState(newState: Partial<CodeQuickEditState>): void;
|
||||
onDidChangeState: Event<void>;
|
||||
|
||||
onDidFocusChat: Event<void>;
|
||||
onDidBlurChat: Event<void>;
|
||||
fireFocusChat(): void;
|
||||
fireBlurChat(): void;
|
||||
|
||||
}
|
||||
|
||||
export const IQuickEditStateService = createDecorator<IQuickEditStateService>('codeQuickEditStateService');
|
||||
class CodeQuickEditStateService extends Disposable implements IQuickEditStateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
static readonly ID = 'codeQuickEditStateService';
|
||||
|
||||
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: CodeQuickEditState
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super()
|
||||
|
||||
// initial state
|
||||
this.state = { quickEditsOfDocument: {} }
|
||||
}
|
||||
|
||||
|
||||
setState(newState: Partial<CodeQuickEditState>) {
|
||||
|
||||
this.state = { ...this.state, ...newState }
|
||||
this._onDidChangeState.fire()
|
||||
}
|
||||
|
||||
fireFocusChat() {
|
||||
this._onFocusChat.fire()
|
||||
}
|
||||
|
||||
fireBlurChat() {
|
||||
this._onBlurChat.fire()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IQuickEditStateService, CodeQuickEditStateService, InstantiationType.Eager);
|
||||
@@ -60,7 +60,7 @@ function findDesiredPathFromLocalPath(localDesiredPath, currentPath) {
|
||||
function saveStylesFile() {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const pathToCssFile = findDesiredPathFromLocalPath('./src/vs/workbench/contrib/void/browser/react/src2/styles.css', __dirname);
|
||||
const pathToCssFile = findDesiredPathFromLocalPath('./src/vs/workbench/contrib/code/browser/react/src2/styles.css', __dirname);
|
||||
|
||||
if (pathToCssFile === undefined) {
|
||||
console.error('[scope-tailwind] Error finding styles.css');
|
||||
@@ -87,7 +87,7 @@ if (isWatch) {
|
||||
try {
|
||||
console.log('🔨 Running initial scope-tailwind build to create src2 folder...');
|
||||
execSync(
|
||||
'npx scope-tailwind ./src -o src2/ -s code-scope -c styles.css -p "code-"',
|
||||
'npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "code-"',
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
console.log('✅ src2/ created successfully.');
|
||||
@@ -103,7 +103,7 @@ if (isWatch) {
|
||||
'--watch', 'src',
|
||||
'--ext', 'ts,tsx,css',
|
||||
'--exec',
|
||||
'npx scope-tailwind ./src -o src2/ -s code-scope -c styles.css -p "code-"'
|
||||
'npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "code-"'
|
||||
]);
|
||||
|
||||
const tsupWatcher = spawn('npx', [
|
||||
@@ -145,7 +145,7 @@ if (isWatch) {
|
||||
console.log('📦 Building...');
|
||||
|
||||
// Run scope-tailwind once
|
||||
execSync('npx scope-tailwind ./src -o src2/ -s code-scope -c styles.css -p "code-"', { stdio: 'inherit' });
|
||||
execSync('npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "code-"', { stdio: 'inherit' });
|
||||
|
||||
// Run tsup once
|
||||
execSync('npx tsup', { stdio: 'inherit' });
|
||||
|
||||
+30
-30
@@ -10,26 +10,26 @@ import '../styles.css'
|
||||
import { useCallback, useEffect, useState, useRef } from 'react';
|
||||
import { ScrollType } from '../../../../../../../editor/common/editorCommon.js';
|
||||
import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg, rejectBg, rejectBorder } from '../../../../common/helpers/colors.js';
|
||||
import { VoidCommandBarProps } from '../../../voidCommandBarService.js';
|
||||
import { CodeCommandBarProps } from '../../../codeCommandBarService.js';
|
||||
import { Check, EllipsisVertical, Menu, MoveDown, MoveLeft, MoveRight, MoveUp, X } from 'lucide-react';
|
||||
import {
|
||||
VOID_GOTO_NEXT_DIFF_ACTION_ID,
|
||||
VOID_GOTO_PREV_DIFF_ACTION_ID,
|
||||
VOID_GOTO_NEXT_URI_ACTION_ID,
|
||||
VOID_GOTO_PREV_URI_ACTION_ID,
|
||||
VOID_ACCEPT_FILE_ACTION_ID,
|
||||
VOID_REJECT_FILE_ACTION_ID,
|
||||
VOID_ACCEPT_ALL_DIFFS_ACTION_ID,
|
||||
VOID_REJECT_ALL_DIFFS_ACTION_ID
|
||||
CODE_GOTO_NEXT_DIFF_ACTION_ID,
|
||||
CODE_GOTO_PREV_DIFF_ACTION_ID,
|
||||
CODE_GOTO_NEXT_URI_ACTION_ID,
|
||||
CODE_GOTO_PREV_URI_ACTION_ID,
|
||||
CODE_ACCEPT_FILE_ACTION_ID,
|
||||
CODE_REJECT_FILE_ACTION_ID,
|
||||
CODE_ACCEPT_ALL_DIFFS_ACTION_ID,
|
||||
CODE_REJECT_ALL_DIFFS_ACTION_ID
|
||||
} from '../../../actionIDs.js';
|
||||
|
||||
export const VoidCommandBarMain = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
export const CodeCommandBarMain = ({ uri, editor }: CodeCommandBarProps) => {
|
||||
const isDark = useIsDark()
|
||||
|
||||
return <div
|
||||
className={`@@void-scope ${isDark ? 'dark' : ''}`}
|
||||
>
|
||||
<VoidCommandBar uri={uri} editor={editor} />
|
||||
<CodeCommandBar uri={uri} editor={editor} />
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -83,14 +83,14 @@ export const RejectAllButtonWrapper = ({ text, onClick, className, ...props }: {
|
||||
|
||||
|
||||
|
||||
export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
export const CodeCommandBar = ({ uri, editor }: CodeCommandBarProps) => {
|
||||
const accessor = useAccessor()
|
||||
const editCodeService = accessor.get('IEditCodeService')
|
||||
const editorService = accessor.get('ICodeEditorService')
|
||||
const metricsService = accessor.get('IMetricsService')
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const commandBarService = accessor.get('IVoidCommandBarService')
|
||||
const voidModelService = accessor.get('IVoidModelService')
|
||||
const commandBarService = accessor.get('ICodeCommandBarService')
|
||||
const codeModelService = accessor.get('ICodeModelService')
|
||||
const keybindingService = accessor.get('IKeybindingService')
|
||||
const { stateOfURI: commandBarState, sortedURIs: sortedCommandBarURIs } = useCommandBarState()
|
||||
const [showAcceptRejectAllButtons, setShowAcceptRejectAllButtons] = useState(false)
|
||||
@@ -171,14 +171,14 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
|
||||
|
||||
|
||||
const _upKeybinding = keybindingService.lookupKeybinding(VOID_GOTO_PREV_DIFF_ACTION_ID);
|
||||
const _downKeybinding = keybindingService.lookupKeybinding(VOID_GOTO_NEXT_DIFF_ACTION_ID);
|
||||
const _leftKeybinding = keybindingService.lookupKeybinding(VOID_GOTO_PREV_URI_ACTION_ID);
|
||||
const _rightKeybinding = keybindingService.lookupKeybinding(VOID_GOTO_NEXT_URI_ACTION_ID);
|
||||
const _acceptFileKeybinding = keybindingService.lookupKeybinding(VOID_ACCEPT_FILE_ACTION_ID);
|
||||
const _rejectFileKeybinding = keybindingService.lookupKeybinding(VOID_REJECT_FILE_ACTION_ID);
|
||||
const _acceptAllKeybinding = keybindingService.lookupKeybinding(VOID_ACCEPT_ALL_DIFFS_ACTION_ID);
|
||||
const _rejectAllKeybinding = keybindingService.lookupKeybinding(VOID_REJECT_ALL_DIFFS_ACTION_ID);
|
||||
const _upKeybinding = keybindingService.lookupKeybinding(CODE_GOTO_PREV_DIFF_ACTION_ID);
|
||||
const _downKeybinding = keybindingService.lookupKeybinding(CODE_GOTO_NEXT_DIFF_ACTION_ID);
|
||||
const _leftKeybinding = keybindingService.lookupKeybinding(CODE_GOTO_PREV_URI_ACTION_ID);
|
||||
const _rightKeybinding = keybindingService.lookupKeybinding(CODE_GOTO_NEXT_URI_ACTION_ID);
|
||||
const _acceptFileKeybinding = keybindingService.lookupKeybinding(CODE_ACCEPT_FILE_ACTION_ID);
|
||||
const _rejectFileKeybinding = keybindingService.lookupKeybinding(CODE_REJECT_FILE_ACTION_ID);
|
||||
const _acceptAllKeybinding = keybindingService.lookupKeybinding(CODE_ACCEPT_ALL_DIFFS_ACTION_ID);
|
||||
const _rejectAllKeybinding = keybindingService.lookupKeybinding(CODE_REJECT_ALL_DIFFS_ACTION_ID);
|
||||
|
||||
const upKeybindLabel = editCodeService.processRawKeybindingText(_upKeybinding?.getLabel() || '');
|
||||
const downKeybindLabel = editCodeService.processRawKeybindingText(_downKeybinding?.getLabel() || '');
|
||||
@@ -231,7 +231,7 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
<AcceptAllButtonWrapper
|
||||
// text={`Accept All${acceptAllKeybindLabel ? ` ${acceptAllKeybindLabel}` : ''}`}
|
||||
text={`Accept All`}
|
||||
data-tooltip-id='void-tooltip'
|
||||
data-tooltip-id='code-tooltip'
|
||||
data-tooltip-content={acceptAllKeybindLabel}
|
||||
data-tooltip-delay-show={500}
|
||||
onClick={onAcceptAll}
|
||||
@@ -239,7 +239,7 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
<RejectAllButtonWrapper
|
||||
// text={`Reject All${rejectAllKeybindLabel ? ` ${rejectAllKeybindLabel}` : ''}`}
|
||||
text={`Reject All`}
|
||||
data-tooltip-id='void-tooltip'
|
||||
data-tooltip-id='code-tooltip'
|
||||
data-tooltip-content={rejectAllKeybindLabel}
|
||||
data-tooltip-delay-show={500}
|
||||
onClick={onRejectAll}
|
||||
@@ -263,7 +263,7 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
commandBarService.goToDiffIdx(prevDiffIdx);
|
||||
}
|
||||
}}
|
||||
data-tooltip-id="void-tooltip"
|
||||
data-tooltip-id="code-tooltip"
|
||||
data-tooltip-content={`${upKeybindLabel ? `${upKeybindLabel}` : ''}`}
|
||||
data-tooltip-delay-show={500}
|
||||
>
|
||||
@@ -288,7 +288,7 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
commandBarService.goToDiffIdx(nextDiffIdx);
|
||||
}
|
||||
}}
|
||||
data-tooltip-id="void-tooltip"
|
||||
data-tooltip-id="code-tooltip"
|
||||
data-tooltip-content={`${downKeybindLabel ? `${downKeybindLabel}` : ''}`}
|
||||
data-tooltip-delay-show={500}
|
||||
>
|
||||
@@ -310,7 +310,7 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
commandBarService.goToURIIdx(prevURIIdx);
|
||||
}
|
||||
}}
|
||||
data-tooltip-id="void-tooltip"
|
||||
data-tooltip-id="code-tooltip"
|
||||
data-tooltip-content={`${leftKeybindLabel ? `${leftKeybindLabel}` : ''}`}
|
||||
data-tooltip-delay-show={500}
|
||||
>
|
||||
@@ -332,7 +332,7 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
commandBarService.goToURIIdx(nextURIIdx);
|
||||
}
|
||||
}}
|
||||
data-tooltip-id="void-tooltip"
|
||||
data-tooltip-id="code-tooltip"
|
||||
data-tooltip-content={`${rightKeybindLabel ? `${rightKeybindLabel}` : ''}`}
|
||||
data-tooltip-delay-show={500}
|
||||
>
|
||||
@@ -347,7 +347,7 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
<AcceptAllButtonWrapper
|
||||
// text={`Accept File${acceptFileKeybindLabel ? ` ${acceptFileKeybindLabel}` : ''}`}
|
||||
text={`Accept File`}
|
||||
data-tooltip-id='void-tooltip'
|
||||
data-tooltip-id='code-tooltip'
|
||||
data-tooltip-content={acceptFileKeybindLabel}
|
||||
data-tooltip-delay-show={500}
|
||||
onClick={onAcceptFile}
|
||||
@@ -355,7 +355,7 @@ export const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
|
||||
<RejectAllButtonWrapper
|
||||
// text={`Reject File${rejectFileKeybindLabel ? ` ${rejectFileKeybindLabel}` : ''}`}
|
||||
text={`Reject File`}
|
||||
data-tooltip-id='void-tooltip'
|
||||
data-tooltip-id='code-tooltip'
|
||||
data-tooltip-content={rejectFileKeybindLabel}
|
||||
data-tooltip-delay-show={500}
|
||||
onClick={onRejectFile}
|
||||
+12
-12
@@ -7,36 +7,36 @@
|
||||
import { useAccessor, useActiveURI, useIsDark, useSettingsState } from '../util/services.js';
|
||||
|
||||
import '../styles.css'
|
||||
import { VOID_CTRL_K_ACTION_ID, VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js';
|
||||
import { CODE_CTRL_K_ACTION_ID, CODE_CTRL_L_ACTION_ID } from '../../../actionIDs.js';
|
||||
import { Circle, MoreVertical } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { VoidSelectionHelperProps } from '../../../../../../contrib/void/browser/voidSelectionHelperWidget.js';
|
||||
import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
|
||||
import { CodeSelectionHelperProps } from '../../../../../../contrib/code/browser/codeSelectionHelperWidget.js';
|
||||
import { CODE_OPEN_SETTINGS_ACTION_ID } from '../../../codeSettingsPane.js';
|
||||
|
||||
|
||||
export const VoidSelectionHelperMain = (props: VoidSelectionHelperProps) => {
|
||||
export const CodeSelectionHelperMain = (props: CodeSelectionHelperProps) => {
|
||||
|
||||
const isDark = useIsDark()
|
||||
|
||||
return <div
|
||||
className={`@@void-scope ${isDark ? 'dark' : ''}`}
|
||||
>
|
||||
<VoidSelectionHelper {...props} />
|
||||
<CodeSelectionHelper {...props} />
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
const VoidSelectionHelper = ({ rerenderKey }: VoidSelectionHelperProps) => {
|
||||
const CodeSelectionHelper = ({ rerenderKey }: CodeSelectionHelperProps) => {
|
||||
|
||||
|
||||
const accessor = useAccessor()
|
||||
const keybindingService = accessor.get('IKeybindingService')
|
||||
const commandService = accessor.get('ICommandService')
|
||||
|
||||
const ctrlLKeybind = keybindingService.lookupKeybinding(VOID_CTRL_L_ACTION_ID)
|
||||
const ctrlKKeybind = keybindingService.lookupKeybinding(VOID_CTRL_K_ACTION_ID)
|
||||
const ctrlLKeybind = keybindingService.lookupKeybinding(CODE_CTRL_L_ACTION_ID)
|
||||
const ctrlKKeybind = keybindingService.lookupKeybinding(CODE_CTRL_K_ACTION_ID)
|
||||
|
||||
const dividerHTML = <div className='w-[0.5px] bg-void-border-3'></div>
|
||||
|
||||
@@ -45,7 +45,7 @@ const VoidSelectionHelper = ({ rerenderKey }: VoidSelectionHelperProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
const disposable = commandService.onWillExecuteCommand(e => {
|
||||
if (e.commandId === VOID_CTRL_L_ACTION_ID || e.commandId === VOID_CTRL_K_ACTION_ID) {
|
||||
if (e.commandId === CODE_CTRL_L_ACTION_ID || e.commandId === CODE_CTRL_K_ACTION_ID) {
|
||||
setClickState('clickedOption')
|
||||
}
|
||||
});
|
||||
@@ -79,7 +79,7 @@ const VoidSelectionHelper = ({ rerenderKey }: VoidSelectionHelperProps) => {
|
||||
cursor-pointer
|
||||
'
|
||||
onClick={() => {
|
||||
commandService.executeCommand(VOID_CTRL_L_ACTION_ID)
|
||||
commandService.executeCommand(CODE_CTRL_L_ACTION_ID)
|
||||
setClickState('clickedOption');
|
||||
}}
|
||||
>
|
||||
@@ -99,7 +99,7 @@ const VoidSelectionHelper = ({ rerenderKey }: VoidSelectionHelperProps) => {
|
||||
cursor-pointer
|
||||
'
|
||||
onClick={() => {
|
||||
commandService.executeCommand(VOID_CTRL_K_ACTION_ID)
|
||||
commandService.executeCommand(CODE_CTRL_K_ACTION_ID)
|
||||
setClickState('clickedOption');
|
||||
}}
|
||||
>
|
||||
@@ -133,7 +133,7 @@ const VoidSelectionHelper = ({ rerenderKey }: VoidSelectionHelperProps) => {
|
||||
cursor-pointer
|
||||
'
|
||||
onClick={() => {
|
||||
commandService.executeCommand(VOID_OPEN_SETTINGS_ACTION_ID);
|
||||
commandService.executeCommand(CODE_OPEN_SETTINGS_ACTION_ID);
|
||||
setClickState('clickedOption');
|
||||
}}
|
||||
>
|
||||
+4
-4
@@ -4,10 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mountFnGenerator } from '../util/mountFnGenerator.js'
|
||||
import { VoidCommandBarMain } from './VoidCommandBar.js'
|
||||
import { VoidSelectionHelperMain } from './VoidSelectionHelper.js'
|
||||
import { CodeCommandBarMain } from './CodeCommandBar.js'
|
||||
import { CodeSelectionHelperMain } from './CodeSelectionHelper.js'
|
||||
|
||||
export const mountVoidCommandBar = mountFnGenerator(VoidCommandBarMain)
|
||||
export const mountCodeCommandBar = mountFnGenerator(CodeCommandBarMain)
|
||||
|
||||
export const mountVoidSelectionHelper = mountFnGenerator(VoidSelectionHelperMain)
|
||||
export const mountCodeSelectionHelper = mountFnGenerator(CodeSelectionHelperMain)
|
||||
|
||||
+28
-28
@@ -6,19 +6,19 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useAccessor, useIsDark, useSettingsState } from '../util/services.js';
|
||||
import { Brain, Check, ChevronRight, DollarSign, ExternalLink, Lock, X } from 'lucide-react';
|
||||
import { displayInfoOfProviderName, ProviderName, providerNames, localProviderNames, featureNames, FeatureName, isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js';
|
||||
import { displayInfoOfProviderName, ProviderName, providerNames, localProviderNames, featureNames, FeatureName, isFeatureNameDisabled } from '../../../../common/codeSettingsTypes.js';
|
||||
import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js';
|
||||
import { OllamaSetupInstructions, OneClickSwitchButton, SettingsForProvider, ModelDump } from '../void-settings-tsx/Settings.js';
|
||||
import { OllamaSetupInstructions, OneClickSwitchButton, SettingsForProvider, ModelDump } from '../code-settings-tsx/Settings.js';
|
||||
import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js';
|
||||
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js';
|
||||
import { isLinux } from '../../../../../../../base/common/platform.js';
|
||||
|
||||
const OVERRIDE_VALUE = false
|
||||
|
||||
export const VoidOnboarding = () => {
|
||||
export const CodeOnboarding = () => {
|
||||
|
||||
const voidSettingsState = useSettingsState()
|
||||
const isOnboardingComplete = voidSettingsState.globalSettings.isOnboardingComplete || OVERRIDE_VALUE
|
||||
const codeSettingsState = useSettingsState()
|
||||
const isOnboardingComplete = codeSettingsState.globalSettings.isOnboardingComplete || OVERRIDE_VALUE
|
||||
|
||||
const isDark = useIsDark()
|
||||
|
||||
@@ -32,14 +32,14 @@ export const VoidOnboarding = () => {
|
||||
style={{ height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
||||
>
|
||||
<ErrorBoundary>
|
||||
<VoidOnboardingContent />
|
||||
<CodeOnboardingContent />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const VoidIcon = () => {
|
||||
const CodeIcon = () => {
|
||||
const accessor = useAccessor()
|
||||
const themeService = accessor.get('IThemeService')
|
||||
|
||||
@@ -206,7 +206,7 @@ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setP
|
||||
Add {displayInfoOfProviderName(providerName).title}
|
||||
{providerName === 'gemini' && (
|
||||
<span
|
||||
data-tooltip-id="void-tooltip-provider-info"
|
||||
data-tooltip-id="code-tooltip-provider-info"
|
||||
data-tooltip-content="Gemini 2.5 Pro offers 25 free messages a day, and Gemini 2.5 Flash offers 500. We recommend using models down the line as you run out of free credits."
|
||||
data-tooltip-place="right"
|
||||
className="ml-1 text-xs align-top text-blue-400"
|
||||
@@ -214,7 +214,7 @@ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setP
|
||||
)}
|
||||
{providerName === 'openRouter' && (
|
||||
<span
|
||||
data-tooltip-id="void-tooltip-provider-info"
|
||||
data-tooltip-id="code-tooltip-provider-info"
|
||||
data-tooltip-content="OpenRouter offers 50 free messages a day, and 1000 if you deposit $10. Only applies to models labeled ':free'."
|
||||
data-tooltip-place="right"
|
||||
className="ml-1 text-xs align-top text-blue-400"
|
||||
@@ -275,7 +275,7 @@ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setP
|
||||
// OnboardingPage
|
||||
// title:
|
||||
// div
|
||||
// "Welcome to Void"
|
||||
// "Welcome to Code"
|
||||
// image
|
||||
// content:<></>
|
||||
// title
|
||||
@@ -285,7 +285,7 @@ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setP
|
||||
// OnboardingPage
|
||||
// title:
|
||||
// div
|
||||
// "How would you like to use Void?"
|
||||
// "How would you like to use Code?"
|
||||
// content:
|
||||
// ModelQuestionContent
|
||||
// |
|
||||
@@ -328,7 +328,7 @@ const NextButton = ({ onClick, ...props }: { onClick: () => void } & React.Butto
|
||||
} rounded text-black duration-600 transition-all
|
||||
`}
|
||||
{...disabled && {
|
||||
'data-tooltip-id': 'void-tooltip',
|
||||
'data-tooltip-id': 'code-tooltip',
|
||||
"data-tooltip-content": 'Please enter all required fields or choose another provider', // (double-click to proceed anyway, can come back in Settings)
|
||||
"data-tooltip-place": 'top',
|
||||
}}
|
||||
@@ -467,14 +467,14 @@ const PrimaryActionButton = ({ children, className, ringSize, ...props }: { chil
|
||||
|
||||
type WantToUseOption = 'smart' | 'private' | 'cheap' | 'all'
|
||||
|
||||
const VoidOnboardingContent = () => {
|
||||
const CodeOnboardingContent = () => {
|
||||
|
||||
|
||||
const accessor = useAccessor()
|
||||
const voidSettingsService = accessor.get('IVoidSettingsService')
|
||||
const voidMetricsService = accessor.get('IMetricsService')
|
||||
const codeSettingsService = accessor.get('ICodeSettingsService')
|
||||
const codeMetricsService = accessor.get('IMetricsService')
|
||||
|
||||
const voidSettingsState = useSettingsState()
|
||||
const codeSettingsState = useSettingsState()
|
||||
|
||||
const [pageIndex, setPageIndex] = useState(0)
|
||||
|
||||
@@ -518,9 +518,9 @@ const VoidOnboardingContent = () => {
|
||||
|
||||
|
||||
const selectedProviderName = getSelectedProvider();
|
||||
const didFillInProviderSettings = selectedProviderName && voidSettingsState.settingsOfProvider[selectedProviderName]._didFillInProviderSettings
|
||||
const isApiKeyLongEnoughIfApiKeyExists = selectedProviderName && voidSettingsState.settingsOfProvider[selectedProviderName].apiKey ? voidSettingsState.settingsOfProvider[selectedProviderName].apiKey.length > 15 : true
|
||||
const isAtLeastOneModel = selectedProviderName && voidSettingsState.settingsOfProvider[selectedProviderName].models.length >= 1
|
||||
const didFillInProviderSettings = selectedProviderName && codeSettingsState.settingsOfProvider[selectedProviderName]._didFillInProviderSettings
|
||||
const isApiKeyLongEnoughIfApiKeyExists = selectedProviderName && codeSettingsState.settingsOfProvider[selectedProviderName].apiKey ? codeSettingsState.settingsOfProvider[selectedProviderName].apiKey.length > 15 : true
|
||||
const isAtLeastOneModel = selectedProviderName && codeSettingsState.settingsOfProvider[selectedProviderName].models.length >= 1
|
||||
|
||||
const didFillInSelectedProviderSettings = !!(didFillInProviderSettings && isApiKeyLongEnoughIfApiKeyExists && isAtLeastOneModel)
|
||||
|
||||
@@ -543,11 +543,11 @@ const VoidOnboardingContent = () => {
|
||||
/>
|
||||
<PrimaryActionButton
|
||||
onClick={() => {
|
||||
voidSettingsService.setGlobalSetting('isOnboardingComplete', true);
|
||||
voidMetricsService.capture('Completed Onboarding', { selectedProviderName, wantToUseOption })
|
||||
codeSettingsService.setGlobalSetting('isOnboardingComplete', true);
|
||||
codeMetricsService.capture('Completed Onboarding', { selectedProviderName, wantToUseOption })
|
||||
}}
|
||||
ringSize={voidSettingsState.globalSettings.isOnboardingComplete ? 'screen' : undefined}
|
||||
>Enter the Void</PrimaryActionButton>
|
||||
ringSize={codeSettingsState.globalSettings.isOnboardingComplete ? 'screen' : undefined}
|
||||
>Enter the Code</PrimaryActionButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -586,21 +586,21 @@ const VoidOnboardingContent = () => {
|
||||
|
||||
// reset the page to page 0 if the user redos onboarding
|
||||
useEffect(() => {
|
||||
if (!voidSettingsState.globalSettings.isOnboardingComplete) {
|
||||
if (!codeSettingsState.globalSettings.isOnboardingComplete) {
|
||||
setPageIndex(0)
|
||||
}
|
||||
}, [setPageIndex, voidSettingsState.globalSettings.isOnboardingComplete])
|
||||
}, [setPageIndex, codeSettingsState.globalSettings.isOnboardingComplete])
|
||||
|
||||
|
||||
const contentOfIdx: { [pageIndex: number]: React.ReactNode } = {
|
||||
0: <OnboardingPageShell
|
||||
content={
|
||||
<div className='flex flex-col items-center gap-8'>
|
||||
<div className="text-5xl font-light text-center">Welcome to Void</div>
|
||||
<div className="text-5xl font-light text-center">Welcome to Code</div>
|
||||
|
||||
{/* Slice of Void image */}
|
||||
{/* Slice of Code image */}
|
||||
<div className='max-w-md w-full h-[30vh] mx-auto flex items-center justify-center'>
|
||||
{!isLinux && <VoidIcon />}
|
||||
{!isLinux && <CodeIcon />}
|
||||
</div>
|
||||
|
||||
|
||||
+2
-2
@@ -4,6 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mountFnGenerator } from '../util/mountFnGenerator.js'
|
||||
import { VoidOnboarding } from './VoidOnboarding.js'
|
||||
import { CodeOnboarding } from './CodeOnboarding.js'
|
||||
|
||||
export const mountVoidOnboarding = mountFnGenerator(VoidOnboarding)
|
||||
export const mountCodeOnboarding = mountFnGenerator(CodeOnboarding)
|
||||
+10
-61
@@ -4,15 +4,15 @@
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { FeatureName, featureNames, ModelSelection, modelSelectionsEqual, ProviderName, providerNames } from '../../../../../../../platform/void/common/codeSettingsTypes.js'
|
||||
import { FeatureName, featureNames, isFeatureNameDisabled, ModelSelection, modelSelectionsEqual, ProviderName, providerNames, SettingsOfProvider } from '../../../../../../../workbench/contrib/code/common/codeSettingsTypes.js'
|
||||
import { useSettingsState, useRefreshModelState, useAccessor } from '../util/services.js'
|
||||
import { _CodeSelectBox, CodeCustomSelectBox } from '../util/inputs.js'
|
||||
import { _CodeSelectBox, CodeCustomDropdownBox } from '../util/inputs.js'
|
||||
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'
|
||||
import { IconWarning } from '../sidebar-tsx/SidebarChat.js'
|
||||
import { CODE_OPEN_SETTINGS_ACTION_ID, CODE_TOGGLE_SETTINGS_ACTION_ID } from '../../../codeSettingsPane.js'
|
||||
import { ModelOption } from '../../../../../../../platform/void/common/codeSettingsService.js'
|
||||
|
||||
|
||||
import { modelFilterOfFeatureName, ModelOption } from '../../../../../../../workbench/contrib/code/common/codeSettingsService.js'
|
||||
import { WarningBox } from './WarningBox.js'
|
||||
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'
|
||||
|
||||
const optionsEqual = (m1: ModelOption[], m2: ModelOption[]) => {
|
||||
if (m1.length !== m2.length) return false
|
||||
@@ -27,13 +27,13 @@ const ModelSelectBox = ({ options, featureName, className }: { options: ModelOpt
|
||||
const codeSettingsService = accessor.get('ICodeSettingsService')
|
||||
|
||||
const selection = codeSettingsService.state.modelSelectionOfFeature[featureName]
|
||||
const selectedOption = selection ? codeSettingsService.state._modelOptions.find(v => modelSelectionsEqual(v.selection, selection)) : options[0]
|
||||
const selectedOption = selection ? codeSettingsService.state._modelOptions.find(v => modelSelectionsEqual(v.selection, selection))! : options[0]
|
||||
|
||||
const onChangeOption = useCallback((newOption: ModelOption) => {
|
||||
codeSettingsService.setModelSelectionOfFeature(featureName, newOption.selection)
|
||||
}, [codeSettingsService, featureName])
|
||||
|
||||
return <CodeCustomSelectBox
|
||||
return <CodeCustomDropdownBox
|
||||
options={options}
|
||||
selectedOption={selectedOption}
|
||||
onChangeOption={onChangeOption}
|
||||
@@ -41,40 +41,13 @@ const ModelSelectBox = ({ options, featureName, className }: { options: ModelOpt
|
||||
getOptionDropdownName={(option) => option.selection.modelName}
|
||||
getOptionDropdownDetail={(option) => option.selection.providerName}
|
||||
getOptionsEqual={(a, b) => optionsEqual([a], [b])}
|
||||
className={`text-xs text-code-fg-3 px-1`}
|
||||
className={className}
|
||||
matchInputWidth={false}
|
||||
/>
|
||||
}
|
||||
|
||||
// const codeSettingsService = accessor.get('ICodeSettingsService')
|
||||
|
||||
// let weChangedText = false
|
||||
|
||||
// return <CodeSelectBox
|
||||
// className='@@[&_select]:!code-text-xs text-code-fg-3'
|
||||
// options={options}
|
||||
// onChangeSelection={useCallback((newVal: ModelSelection) => {
|
||||
// if (weChangedText) return
|
||||
// codeSettingsService.setModelSelectionOfFeature(featureName, newVal)
|
||||
// }, [codeSettingsService, featureName])}
|
||||
// // we are responsible for setting the initial state here. always sync instance when state changes.
|
||||
// onCreateInstance={useCallback((instance: SelectBox) => {
|
||||
// const syncInstance = () => {
|
||||
// const modelsListRef = codeSettingsService.state._modelOptions // as a ref
|
||||
// const settingsAtProvider = codeSettingsService.state.modelSelectionOfFeature[featureName]
|
||||
// const selectionIdx = settingsAtProvider === null ? -1 : modelsListRef.findIndex(v => modelSelectionsEqual(v.value, settingsAtProvider))
|
||||
// weChangedText = true
|
||||
// instance.select(selectionIdx === -1 ? 0 : selectionIdx)
|
||||
// weChangedText = false
|
||||
// }
|
||||
// syncInstance()
|
||||
// const disposable = codeSettingsService.onDidChangeState(syncInstance)
|
||||
// return [disposable]
|
||||
// }, [codeSettingsService, featureName])}
|
||||
// />
|
||||
// }
|
||||
|
||||
const MemoizedModelSelectBox = ({ featureName }: { featureName: FeatureName }) => {
|
||||
const MemoizedModelDropdown = ({ featureName, className }: { featureName: FeatureName, className: string }) => {
|
||||
const settingsState = useSettingsState()
|
||||
const oldOptionsRef = useRef<ModelOption[]>([])
|
||||
const [memoizedOptions, setMemoizedOptions] = useState(oldOptionsRef.current)
|
||||
@@ -99,31 +72,7 @@ const MemoizedModelSelectBox = ({ featureName }: { featureName: FeatureName }) =
|
||||
|
||||
}
|
||||
|
||||
export const WarningBox = ({ text, onClick, className }: { text: string; onClick?: () => void; className?: string }) => {
|
||||
|
||||
return <div
|
||||
className={`
|
||||
text-code-warning brightness-90 opacity-90
|
||||
text-xs text-ellipsis
|
||||
${onClick ? `hover:brightness-75 transition-all duration-200 cursor-pointer` : ''}
|
||||
flex items-center flex-nowrap
|
||||
${className}
|
||||
`}
|
||||
onClick={onClick}
|
||||
>
|
||||
<IconWarning
|
||||
size={14}
|
||||
className='mr-1'
|
||||
/>
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
// return <CodeSelectBox
|
||||
// options={[{ text: 'Please add a model!', value: null }]}
|
||||
// onChangeSelection={() => { }}
|
||||
// />
|
||||
}
|
||||
|
||||
export const ModelDropdown = ({ featureName }: { featureName: FeatureName }) => {
|
||||
export const ModelDropdown = ({ featureName, className }: { featureName: FeatureName, className: string }) => {
|
||||
const settingsState = useSettingsState()
|
||||
|
||||
const accessor = useAccessor()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ export const WarningBox = ({ text, onClick, className }: { text: string; onClick
|
||||
/>
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
// return <VoidSelectBox
|
||||
// return <CodeSelectBox
|
||||
// options={[{ text: 'Please add a model!', value: null }]}
|
||||
// onChangeSelection={() => { }}
|
||||
// />
|
||||
|
||||
+8
-8
@@ -11,10 +11,10 @@ import { useIsDark } from '../util/services.js';
|
||||
/**
|
||||
* Creates a configured global tooltip component with consistent styling
|
||||
* To use:
|
||||
* 1. Mount a Tooltip with some id eg id='void-tooltip'
|
||||
* 2. Add data-tooltip-id="void-tooltip" and data-tooltip-content="Your tooltip text" to any element
|
||||
* 1. Mount a Tooltip with some id eg id='code-tooltip'
|
||||
* 2. Add data-tooltip-id="code-tooltip" and data-tooltip-content="Your tooltip text" to any element
|
||||
*/
|
||||
export const VoidTooltip = () => {
|
||||
export const CodeTooltip = () => {
|
||||
|
||||
|
||||
const isDark = useIsDark()
|
||||
@@ -82,26 +82,26 @@ export const VoidTooltip = () => {
|
||||
|
||||
|
||||
<Tooltip
|
||||
id="void-tooltip"
|
||||
id="code-tooltip"
|
||||
// border='1px solid var(--vscode-editorGroup-border)'
|
||||
border='1px solid rgba(100,100,100,.2)'
|
||||
opacity={1}
|
||||
delayShow={50}
|
||||
/>
|
||||
<Tooltip
|
||||
id="void-tooltip-orange"
|
||||
id="code-tooltip-orange"
|
||||
border='1px solid rgba(200,200,200,.3)'
|
||||
opacity={1}
|
||||
delayShow={50}
|
||||
/>
|
||||
<Tooltip
|
||||
id="void-tooltip-green"
|
||||
id="code-tooltip-green"
|
||||
border='1px solid rgba(200,200,200,.3)'
|
||||
opacity={1}
|
||||
delayShow={50}
|
||||
/>
|
||||
<Tooltip
|
||||
id="void-tooltip-ollama-settings"
|
||||
id="code-tooltip-ollama-settings"
|
||||
border='1px solid rgba(100,100,100,.2)'
|
||||
opacity={1}
|
||||
openEvents={{ mouseover: true, click: true, focus: true }}
|
||||
@@ -127,7 +127,7 @@ export const VoidTooltip = () => {
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip
|
||||
id="void-tooltip-provider-info"
|
||||
id="code-tooltip-provider-info"
|
||||
border='1px solid rgba(100,100,100,.2)'
|
||||
opacity={1}
|
||||
delayShow={50}
|
||||
+2
-2
@@ -4,6 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mountFnGenerator } from '../util/mountFnGenerator.js'
|
||||
import { VoidTooltip } from './VoidTooltip.js'
|
||||
import { CodeTooltip } from './CodeTooltip.js'
|
||||
|
||||
export const mountVoidTooltip = mountFnGenerator(VoidTooltip)
|
||||
export const mountCodeTooltip = mountFnGenerator(CodeTooltip)
|
||||
+14
-14
@@ -6,11 +6,11 @@
|
||||
import { useState, useEffect, useCallback, useRef, Fragment } from 'react'
|
||||
import { useAccessor, useChatThreadsState, useChatThreadsStreamState, useCommandBarState, useCommandBarURIListener, useSettingsState } from '../util/services.js'
|
||||
import { usePromise, useRefState } from '../util/helpers.js'
|
||||
import { isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js'
|
||||
import { isFeatureNameDisabled } from '../../../../common/codeSettingsTypes.js'
|
||||
import { URI } from '../../../../../../../base/common/uri.js'
|
||||
import { FileSymlink, LucideIcon, RotateCw, Terminal } from 'lucide-react'
|
||||
import { Check, X, Square, Copy, Play, } from 'lucide-react'
|
||||
import { getBasename, ListableToolItem, voidOpenFileFn, ToolChildrenWrapper } from '../sidebar-tsx/SidebarChat.js'
|
||||
import { getBasename, ListableToolItem, codeOpenFileFn, ToolChildrenWrapper } from '../sidebar-tsx/SidebarChat.js'
|
||||
import { PlacesType, VariantType } from 'react-tooltip'
|
||||
|
||||
enum CopyButtonText {
|
||||
@@ -109,7 +109,7 @@ export const JumpToFileButton = ({ uri, ...props }: { uri: URI | 'current' } & R
|
||||
<IconShell1
|
||||
Icon={FileSymlink}
|
||||
onClick={() => {
|
||||
voidOpenFileFn(uri, accessor)
|
||||
codeOpenFileFn(uri, accessor)
|
||||
}}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Go to file' })}
|
||||
{...props}
|
||||
@@ -141,13 +141,13 @@ const getUriBeingApplied = (applyBoxId: string) => {
|
||||
|
||||
export const useApplyStreamState = ({ applyBoxId }: { applyBoxId: string }) => {
|
||||
const accessor = useAccessor()
|
||||
const voidCommandBarService = accessor.get('IVoidCommandBarService')
|
||||
const codeCommandBarService = accessor.get('ICodeCommandBarService')
|
||||
|
||||
const getStreamState = useCallback(() => {
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (!uri) return 'idle-no-changes'
|
||||
return voidCommandBarService.getStreamState(uri)
|
||||
}, [voidCommandBarService, applyBoxId])
|
||||
return codeCommandBarService.getStreamState(uri)
|
||||
}, [codeCommandBarService, applyBoxId])
|
||||
|
||||
|
||||
const [currStreamStateRef, setStreamState] = useRefState(getStreamState())
|
||||
@@ -190,7 +190,7 @@ export const StatusIndicator = ({ indicatorColor, title, className, ...props }:
|
||||
};
|
||||
|
||||
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-id': color === 'orange' ? `code-tooltip-orange` : color === 'green' ? 'code-tooltip-green' : 'code-tooltip',
|
||||
'data-tooltip-place': position as PlacesType,
|
||||
'data-tooltip-content': `${tooltipName}`,
|
||||
'data-tooltip-offset': offset,
|
||||
@@ -198,13 +198,13 @@ const tooltipPropsForApplyBlock = ({ tooltipName, color = undefined, position =
|
||||
|
||||
export const useEditToolStreamState = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI }) => {
|
||||
const accessor = useAccessor()
|
||||
const voidCommandBarService = accessor.get('IVoidCommandBarService')
|
||||
const [streamState, setStreamState] = useState(voidCommandBarService.getStreamState(uri))
|
||||
const codeCommandBarService = accessor.get('ICodeCommandBarService')
|
||||
const [streamState, setStreamState] = useState(codeCommandBarService.getStreamState(uri))
|
||||
// listen for stream updates on this box
|
||||
useCommandBarURIListener(useCallback((uri_) => {
|
||||
const shouldUpdate = uri.fsPath === uri_.fsPath
|
||||
if (shouldUpdate) { setStreamState(voidCommandBarService.getStreamState(uri)) }
|
||||
}, [voidCommandBarService, applyBoxId, uri]))
|
||||
if (shouldUpdate) { setStreamState(codeCommandBarService.getStreamState(uri)) }
|
||||
}, [codeCommandBarService, applyBoxId, uri]))
|
||||
|
||||
return { streamState, }
|
||||
}
|
||||
@@ -351,14 +351,14 @@ const ApplyButtonsForEdit = ({
|
||||
setApplying(newApplyingUri)
|
||||
|
||||
if (!applyDonePromise) {
|
||||
notificationService.info(`Void Error: We couldn't run Apply here. ${uri === 'current' ? 'This Apply block wants to run on the current file, but you might not have a file open.' : `This Apply block wants to run on ${uri.fsPath}, but it might not exist.`}`)
|
||||
notificationService.info(`Code Error: We couldn't run Apply here. ${uri === 'current' ? 'This Apply block wants to run on the current file, but you might not have a file open.' : `This Apply block wants to run on ${uri.fsPath}, but it might not exist.`}`)
|
||||
}
|
||||
|
||||
// catch any errors by interrupting the stream
|
||||
applyDonePromise?.catch(e => {
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (uri) editCodeService.interruptURIStreaming({ uri: uri })
|
||||
notificationService.info(`Void Error: There was a problem running Apply: ${e}.`)
|
||||
notificationService.info(`Code Error: There was a problem running Apply: ${e}.`)
|
||||
|
||||
})
|
||||
metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only
|
||||
@@ -530,7 +530,7 @@ export const BlockCodeApplyWrapper = ({
|
||||
name={<span className='not-italic'>{getBasename(uri.fsPath)}</span>}
|
||||
isSmall={true}
|
||||
showDot={false}
|
||||
onClick={() => { voidOpenFileFn(uri, accessor) }}
|
||||
onClick={() => { codeOpenFileFn(uri, accessor) }}
|
||||
/>
|
||||
: <span>{language}</span>
|
||||
|
||||
|
||||
@@ -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 { CodeCodeEditor, CodeCodeEditorProps } from '../util/inputs.js';
|
||||
|
||||
|
||||
export const BlockCode = ({ buttonsOnHover, ...codeEditorProps }: { buttonsOnHover?: React.ReactNode } & CodeCodeEditorProps) => {
|
||||
const isSingleLine = !codeEditorProps.initValue.includes('\n')
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative group w-full overflow-hidden my-4">
|
||||
{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>
|
||||
)}
|
||||
|
||||
<CodeCodeEditor {...codeEditorProps} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import { isAbsolute } from '../../../../../../../base/common/path.js'
|
||||
import { separateOutFirstLine } from '../../../../common/helpers/util.js'
|
||||
import { BlockCode } from '../util/inputs.js'
|
||||
import { CodespanLocationLink } from '../../../../common/chatThreadServiceTypes.js'
|
||||
import { getBasename, getRelative, voidOpenFileFn } from '../sidebar-tsx/SidebarChat.js'
|
||||
import { getBasename, getRelative, codeOpenFileFn } from '../sidebar-tsx/SidebarChat.js'
|
||||
|
||||
|
||||
export type ChatMessageLocation = {
|
||||
@@ -97,7 +97,7 @@ const Codespan = ({ text, className, onClick, tooltip }: { text: string, classNa
|
||||
className={`font-mono font-medium rounded-sm bg-void-bg-1 px-1 ${className}`}
|
||||
onClick={onClick}
|
||||
{...tooltip ? {
|
||||
'data-tooltip-id': 'void-tooltip',
|
||||
'data-tooltip-id': 'code-tooltip',
|
||||
'data-tooltip-content': tooltip,
|
||||
'data-tooltip-place': 'top',
|
||||
} : {}}
|
||||
@@ -150,42 +150,118 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string
|
||||
|
||||
const onClick = () => {
|
||||
if (!link) return;
|
||||
// Use the updated voidOpenFileFn to open the file and handle selection
|
||||
// Use the updated codeOpenFileFn to open the file and handle selection
|
||||
if (link.selection)
|
||||
voidOpenFileFn(link.uri, accessor, [link.selection.startLineNumber, link.selection.endLineNumber]);
|
||||
codeOpenFileFn(link.uri, accessor, [link.selection.startLineNumber, link.selection.endLineNumber]);
|
||||
else
|
||||
voidOpenFileFn(link.uri, accessor);
|
||||
codeOpenFileFn(link.uri, accessor);
|
||||
}
|
||||
|
||||
return <>
|
||||
<button
|
||||
className={`${isSingleLine ? '' : 'px-1 py-0.5'} text-sm bg-code-bg-1 text-code-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
|
||||
onClick={onCopy}
|
||||
>
|
||||
{copyButtonState}
|
||||
</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-code-bg-1 text-code-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
|
||||
onClick={onApply}
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
</>
|
||||
return <Codespan
|
||||
text={displayText}
|
||||
onClick={onClick}
|
||||
className={link ? 'underline hover:brightness-90 transition-all duration-200 cursor-pointer' : ''}
|
||||
tooltip={tooltip || undefined}
|
||||
/>
|
||||
}
|
||||
|
||||
export const CodeSpan = ({ children, className }: { children: React.ReactNode, className?: string }) => {
|
||||
return <code className={`
|
||||
bg-code-bg-1
|
||||
px-1
|
||||
rounded-sm
|
||||
font-mono font-medium
|
||||
break-all
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
|
||||
const paragraphToLatexSegments = (paragraphText: string) => {
|
||||
|
||||
const segments: React.ReactNode[] = [];
|
||||
|
||||
if (paragraphText
|
||||
&& !(paragraphText.includes('#') || paragraphText.includes('`')) // don't process latex if a codespan or header tag
|
||||
&& !/^[\w\s.()[\]{}]+$/.test(paragraphText) // don't process latex if string only contains alphanumeric chars, whitespace, periods, and brackets
|
||||
) {
|
||||
const rawText = paragraphText;
|
||||
// Regular expressions to match LaTeX delimiters
|
||||
const displayMathRegex = /\$\$(.*?)\$\$/g; // Display math: $$...$$
|
||||
const inlineMathRegex = /\$((?!\$).*?)\$/g; // Inline math: $...$ (but not $$)
|
||||
|
||||
// Check if the paragraph contains any LaTeX expressions
|
||||
if (displayMathRegex.test(rawText) || inlineMathRegex.test(rawText)) {
|
||||
// Reset the regex state (since we used .test earlier)
|
||||
displayMathRegex.lastIndex = 0;
|
||||
inlineMathRegex.lastIndex = 0;
|
||||
|
||||
// Parse the text into segments of regular text and LaTeX
|
||||
let lastIndex = 0;
|
||||
let segmentId = 0;
|
||||
|
||||
// First replace display math ($$...$$)
|
||||
let match;
|
||||
while ((match = displayMathRegex.exec(rawText)) !== null) {
|
||||
const [fullMatch, formula] = match;
|
||||
const matchIndex = match.index;
|
||||
|
||||
// Add text before the LaTeX expression
|
||||
if (matchIndex > lastIndex) {
|
||||
const textBefore = rawText.substring(lastIndex, matchIndex);
|
||||
segments.push(
|
||||
<span key={`text-${segmentId++}`}>
|
||||
{textBefore}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// Add the LaTeX expression
|
||||
segments.push(
|
||||
<LatexRender key={`latex-${segmentId++}`} latex={fullMatch} />
|
||||
);
|
||||
|
||||
lastIndex = matchIndex + fullMatch.length;
|
||||
}
|
||||
|
||||
// Add any remaining text (which might contain inline math)
|
||||
if (lastIndex < rawText.length) {
|
||||
const remainingText = rawText.substring(lastIndex);
|
||||
|
||||
// Process inline math in the remaining text
|
||||
lastIndex = 0;
|
||||
inlineMathRegex.lastIndex = 0;
|
||||
const inlineSegments: React.ReactNode[] = [];
|
||||
|
||||
while ((match = inlineMathRegex.exec(remainingText)) !== null) {
|
||||
const [fullMatch] = match;
|
||||
const matchIndex = match.index;
|
||||
|
||||
// Add text before the inline LaTeX
|
||||
if (matchIndex > lastIndex) {
|
||||
const textBefore = remainingText.substring(lastIndex, matchIndex);
|
||||
inlineSegments.push(
|
||||
<span key={`inline-text-${segmentId++}`}>
|
||||
{textBefore}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// Add the inline LaTeX
|
||||
inlineSegments.push(
|
||||
<LatexRender key={`inline-latex-${segmentId++}`} latex={fullMatch} />
|
||||
);
|
||||
|
||||
lastIndex = matchIndex + fullMatch.length;
|
||||
}
|
||||
|
||||
// Add any remaining text after all inline math
|
||||
if (lastIndex < remainingText.length) {
|
||||
inlineSegments.push(
|
||||
<span key={`inline-final-${segmentId++}`}>
|
||||
{remainingText.substring(lastIndex)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
segments.push(...inlineSegments);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return segments
|
||||
}
|
||||
|
||||
|
||||
@@ -263,45 +339,32 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, ..
|
||||
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-code-bg-2",
|
||||
h2: "text-3xl font-semibold mt-6 mb-4 pb-2 border-b border-code-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"
|
||||
}
|
||||
return <HeadingTag className={headingClasses[HeadingTag]}>{t.text}</HeadingTag>
|
||||
|
||||
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-code-bg-2">
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr className="bg-code-bg-1">
|
||||
{t.header.map((cell: any, index: number) => (
|
||||
<th
|
||||
key={index}
|
||||
className="px-4 py-2 border border-code-bg-2 font-semibold"
|
||||
style={{ textAlign: t.align[index] || "left" }}
|
||||
>
|
||||
{cell.raw}
|
||||
<tr>
|
||||
{t.header.map((h, hIdx: number) => (
|
||||
<th key={hIdx}>
|
||||
{h.text}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{t.rows.map((row: any[], rowIndex: number) => (
|
||||
<tr key={rowIndex} className={rowIndex % 2 === 0 ? 'bg-white' : 'bg-code-bg-1'}>
|
||||
{row.map((cell: any, cellIndex: number) => (
|
||||
<td
|
||||
key={cellIndex}
|
||||
className="px-4 py-2 border border-code-bg-2"
|
||||
style={{ textAlign: t.align[cellIndex] || "left" }}
|
||||
>
|
||||
{cell.raw}
|
||||
{t.rows.map((row, rowIdx: number) => (
|
||||
<tr key={rowIdx}>
|
||||
{row.map((r, rIdx: number) => (
|
||||
<td key={rIdx} >
|
||||
{r.text}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
@@ -346,12 +409,12 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, ..
|
||||
// )
|
||||
}
|
||||
|
||||
if (t.type === "hr") {
|
||||
return <hr className="my-6 border-t border-code-bg-2" />
|
||||
if (t.type === 'hr') {
|
||||
return <hr />
|
||||
}
|
||||
|
||||
if (t.type === "blockquote") {
|
||||
return <blockquote className={`pl-4 border-l-4 border-code-bg-2 italic ${noSpace ? '' : 'my-4'}`}>{t.text}</blockquote>
|
||||
if (t.type === 'blockquote') {
|
||||
return <blockquote>{t.text}</blockquote>
|
||||
}
|
||||
|
||||
if (t.type === 'list_item') {
|
||||
|
||||
@@ -13,7 +13,7 @@ export const QuickEdit = (props: QuickEditPropsType) => {
|
||||
|
||||
const isDark = useIsDark()
|
||||
|
||||
return <div className={`@@code-scope ${isDark ? 'dark' : ''}`}>
|
||||
return <div className={`@@void-scope ${isDark ? 'dark' : ''}`}>
|
||||
<ErrorBoundary>
|
||||
<QuickEditChat {...props} />
|
||||
</ErrorBoundary>
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
* 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 } from '../util/services.js';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useSettingsState, useAccessor, useCtrlKZoneStreamingState } from '../util/services.js';
|
||||
import { TextAreaFns, CodeInputBox2 } from '../util/inputs.js';
|
||||
import { QuickEditPropsType } from '../../../quickEditActions.js';
|
||||
import { ButtonStop, ButtonSubmit, IconX } from '../sidebar-tsx/SidebarChat.js';
|
||||
import { ModelDropdown } from '../code-settings-tsx/ModelDropdown.js';
|
||||
import { ButtonStop, ButtonSubmit, IconX, CodeChatArea } from '../sidebar-tsx/SidebarChat.js';
|
||||
import { CODE_CTRL_K_ACTION_ID } from '../../../actionIDs.js';
|
||||
import { useRefState } from '../util/helpers.js';
|
||||
import { isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
|
||||
import { isFeatureNameDisabled } from '../../../../../../../workbench/contrib/code/common/codeSettingsTypes.js';
|
||||
|
||||
|
||||
|
||||
@@ -96,108 +95,43 @@ export const QuickEditChat = ({
|
||||
|
||||
const chatAreaRef = useRef<HTMLDivElement | null>(null)
|
||||
return <div ref={sizerRef} style={{ maxWidth: 450 }} className={`py-2 w-full`}>
|
||||
<form
|
||||
// copied from SidebarChat.tsx
|
||||
className={`
|
||||
flex flex-col gap-2 p-2 relative input text-left shrink-0
|
||||
transition-all duration-200
|
||||
rounded-md
|
||||
bg-vscode-input-bg
|
||||
border border-code-border-3 focus-within:border-code-border-1 hover:border-code-border-1
|
||||
`}
|
||||
onClick={(e) => {
|
||||
textAreaRef.current?.focus()
|
||||
}}
|
||||
<CodeChatArea
|
||||
featureName='Ctrl+K'
|
||||
divRef={chatAreaRef}
|
||||
onSubmit={onSubmit}
|
||||
onAbort={onInterrupt}
|
||||
onClose={onX}
|
||||
isStreaming={isStreamingRef.current}
|
||||
loadingIcon={loadingIcon}
|
||||
isDisabled={isDisabled}
|
||||
onClickAnywhere={() => { textAreaRef.current?.focus() }}
|
||||
>
|
||||
|
||||
{/* // this div is used to position the input box properly */}
|
||||
<div
|
||||
className={`w-full z-[999] relative`}
|
||||
>
|
||||
<div className='flex flex-row items-center justify-between items-end gap-1'>
|
||||
|
||||
{/* input */}
|
||||
<div // copied from SidebarChat.tsx
|
||||
className={`w-full`}
|
||||
>
|
||||
{/* text input */}
|
||||
<CodeInputBox2
|
||||
className='px-1'
|
||||
initValue={initText}
|
||||
|
||||
ref={useCallback((r: HTMLTextAreaElement | null) => {
|
||||
textAreaRef.current = r
|
||||
textAreaRef_(r)
|
||||
|
||||
// if presses the esc key, X
|
||||
r?.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape')
|
||||
onX()
|
||||
})
|
||||
|
||||
}, [textAreaRef_, onX])}
|
||||
|
||||
fnsRef={textAreaFnsRef}
|
||||
|
||||
placeholder={`Enter instructions...`}
|
||||
// ${keybindingString} to select.
|
||||
|
||||
onChangeText={useCallback((newStr: string) => {
|
||||
setInstructionsAreEmpty(!newStr)
|
||||
onChangeText_(newStr)
|
||||
}, [onChangeText_])}
|
||||
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
onSubmit(e)
|
||||
return
|
||||
}
|
||||
}}
|
||||
|
||||
multiline={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* X button */}
|
||||
<div className='absolute -top-1 -right-1 cursor-pointer z-1'>
|
||||
<IconX
|
||||
size={12}
|
||||
className="stroke-[2] opacity-80 text-code-fg-3 hover:brightness-95"
|
||||
onClick={onX}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* bottom row */}
|
||||
<div
|
||||
className='flex flex-row justify-between items-end gap-1'
|
||||
>
|
||||
{/* submit options */}
|
||||
<div className='max-w-[150px]
|
||||
@@[&_select]:!code-border-none
|
||||
@@[&_select]:!code-outline-none'
|
||||
>
|
||||
<ModelDropdown featureName='Ctrl+K' />
|
||||
</div>
|
||||
|
||||
{/* submit / stop button */}
|
||||
{isStreaming ?
|
||||
// stop button
|
||||
<ButtonStop
|
||||
onClick={onInterrupt}
|
||||
/>
|
||||
:
|
||||
// submit button (up arrow)
|
||||
<ButtonSubmit
|
||||
onClick={onSubmit}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
<CodeInputBox2
|
||||
className='px-1'
|
||||
initValue={initText}
|
||||
ref={useCallback((r: HTMLTextAreaElement | null) => {
|
||||
textAreaRef.current = r
|
||||
textAreaRef_(r)
|
||||
r?.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape')
|
||||
onX()
|
||||
})
|
||||
}, [textAreaRef_, onX])}
|
||||
fnsRef={textAreaFnsRef}
|
||||
placeholder="Enter instructions..."
|
||||
onChangeText={useCallback((newStr: string) => {
|
||||
setInstructionsAreEmpty(!newStr)
|
||||
onChangeText_(newStr)
|
||||
}, [onChangeText_])}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
onSubmit()
|
||||
return
|
||||
}
|
||||
}}
|
||||
multiline={true}
|
||||
/>
|
||||
</VoidChatArea>
|
||||
</CodeChatArea>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
||||
import { WarningBox } from '../void-settings-tsx/WarningBox.js';
|
||||
import { WarningBox } from '../code-settings-tsx/WarningBox.js';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -25,8 +25,7 @@ export const ErrorDisplay = ({
|
||||
const details = errorDetails(fullError)
|
||||
const isExpandable = !!details
|
||||
|
||||
const message = message_ === 'TypeError: fetch failed' ? 'TypeError: fetch failed. This likely means you specified the wrong endpoint in Code Settings.' : message_ + ''
|
||||
|
||||
const message = message_ + ''
|
||||
|
||||
return (
|
||||
<div className={`rounded-lg border border-red-200 bg-red-50 p-4 overflow-auto`}>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user