From 8b25f80e9bf9628910e8cb2a8865a21ce0cd56a3 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 15 Dec 2024 20:43:08 -0800 Subject: [PATCH] model selection is accurate now, and lots of files moved to be more accurate --- .eslintrc.json | 1 + .../void/browser/void.contribution.ts | 4 +- .../platform/void/common/llmMessageService.ts | 10 +- .../platform/void/common/llmMessageTypes.ts | 2 +- .../void/common/refreshModelService.ts | 14 +- .../platform/void/common/voidConfigService.ts | 137 ------------- .../void/common/voidSettingsService.ts | 191 ++++++++++++++++++ ...oidConfigTypes.ts => voidSettingsTypes.ts} | 34 ++-- .../electron-main/llmMessage/anthropic.ts | 2 +- .../workbench/contrib/void/browser/.gitignore | 1 - ...Autocomplete.ts => autocompleteService.ts} | 0 .../void/browser/{ => helpers}/findDiffs.ts | 2 +- .../void/browser/{ => helpers}/getCmdKey.ts | 2 +- .../browser/helpers/reactServicesHelper.ts | 51 +++++ ...erInlineDiffs.ts => inlineDiffsService.ts} | 4 +- .../browser/prompt/stringifySelections.ts | 2 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 9 +- .../react/src/util/mountFnGenerator.tsx | 2 +- .../void/browser/react/src/util/services.tsx | 49 ++--- .../ModelSelectionSettings.tsx | 52 ++--- .../VoidProviderSettings.tsx | 39 ++-- .../contrib/void/browser/reactServices.ts | 51 ----- .../{registerActions.ts => sidebarActions.ts} | 13 +- .../void/browser/sidebarStateService.ts | 84 ++++++++ ...{registerSidebar.ts => sidebarViewPane.ts} | 88 +------- ...sterThreads.ts => threadHistoryService.ts} | 4 +- .../contrib/void/browser/void.contribution.ts | 16 +- ...tingsEditorPane.ts => voidSettingsPane.ts} | 36 ++-- 28 files changed, 462 insertions(+), 438 deletions(-) delete mode 100644 src/vs/platform/void/common/voidConfigService.ts create mode 100644 src/vs/platform/void/common/voidSettingsService.ts rename src/vs/platform/void/common/{voidConfigTypes.ts => voidSettingsTypes.ts} (91%) delete mode 100644 src/vs/workbench/contrib/void/browser/.gitignore rename src/vs/workbench/contrib/void/browser/{registerAutocomplete.ts => autocompleteService.ts} (100%) rename src/vs/workbench/contrib/void/browser/{ => helpers}/findDiffs.ts (99%) rename src/vs/workbench/contrib/void/browser/{ => helpers}/getCmdKey.ts (88%) create mode 100644 src/vs/workbench/contrib/void/browser/helpers/reactServicesHelper.ts rename src/vs/workbench/contrib/void/browser/{registerInlineDiffs.ts => inlineDiffsService.ts} (99%) delete mode 100644 src/vs/workbench/contrib/void/browser/reactServices.ts rename src/vs/workbench/contrib/void/browser/{registerActions.ts => sidebarActions.ts} (92%) create mode 100644 src/vs/workbench/contrib/void/browser/sidebarStateService.ts rename src/vs/workbench/contrib/void/browser/{registerSidebar.ts => sidebarViewPane.ts} (67%) rename src/vs/workbench/contrib/void/browser/{registerThreads.ts => threadHistoryService.ts} (98%) rename src/vs/workbench/contrib/void/browser/{voidSettingsEditorPane.ts => voidSettingsPane.ts} (84%) diff --git a/.eslintrc.json b/.eslintrc.json index 1edb79cf9..16884e649 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,6 +10,7 @@ "jsdoc", "header", "local" + // "react" // Void ], "rules": { "constructor-super": "warn", diff --git a/src/vs/platform/void/browser/void.contribution.ts b/src/vs/platform/void/browser/void.contribution.ts index 5209680d9..2d261ea37 100644 --- a/src/vs/platform/void/browser/void.contribution.ts +++ b/src/vs/platform/void/browser/void.contribution.ts @@ -5,8 +5,8 @@ // llmMessage import '../common/llmMessageService.js' -// voidConfig -import '../common/voidConfigService.js' +// voidSettings +import '../common/voidSettingsService.js' // refreshModel import '../common/refreshModelService.js' diff --git a/src/vs/platform/void/common/llmMessageService.ts b/src/vs/platform/void/common/llmMessageService.ts index 967b5a9f5..e0ec2b122 100644 --- a/src/vs/platform/void/common/llmMessageService.ts +++ b/src/vs/platform/void/common/llmMessageService.ts @@ -11,7 +11,7 @@ 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 { IVoidConfigStateService } from './voidConfigService.js'; +import { IVoidSettingsService } from './voidSettingsService.js'; // import { INotificationService } from '../../notification/common/notification.js'; // calls channel to implement features @@ -42,7 +42,7 @@ export class LLMMessageService extends Disposable implements ILLMMessageService constructor( @IMainProcessService private readonly mainProcessService: IMainProcessService, // used as a renderer (only usable on client side) - @IVoidConfigStateService private readonly voidConfigStateService: IVoidConfigStateService, + @IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService, // @INotificationService private readonly notificationService: INotificationService, ) { super() @@ -79,7 +79,7 @@ export class LLMMessageService extends Disposable implements ILLMMessageService const { featureName } = proxyParams // end early if no provider - const modelSelection = this.voidConfigStateService.state.modelSelectionOfFeature[featureName] + const modelSelection = this.voidSettingsService.state.modelSelectionOfFeature[featureName] if (modelSelection === null) { onError({ message: 'Please add a Provider in Settings!', fullError: null }) return null @@ -92,7 +92,7 @@ export class LLMMessageService extends Disposable implements ILLMMessageService this.onFinalMessageHooks_llm[requestId_] = onFinalMessage this.onErrorHooks_llm[requestId_] = onError - const { settingsOfProvider } = this.voidConfigStateService.state + const { settingsOfProvider } = this.voidSettingsService.state // params will be stripped of all its functions over the IPC channel this.channel.call('sendLLMMessage', { @@ -116,7 +116,7 @@ export class LLMMessageService extends Disposable implements ILLMMessageService ollamaList = (params: ServiceOllamaListParams) => { const { onSuccess, onError, ...proxyParams } = params - const { settingsOfProvider } = this.voidConfigStateService.state + const { settingsOfProvider } = this.voidSettingsService.state // add state for request id const requestId_ = generateUuid(); diff --git a/src/vs/platform/void/common/llmMessageTypes.ts b/src/vs/platform/void/common/llmMessageTypes.ts index 3db5d5957..b4e3c54bc 100644 --- a/src/vs/platform/void/common/llmMessageTypes.ts +++ b/src/vs/platform/void/common/llmMessageTypes.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IRange } from '../../../editor/common/core/range' -import { ProviderName, SettingsOfProvider } from './voidConfigTypes' +import { ProviderName, SettingsOfProvider } from './voidSettingsTypes.js' export type OnText = (p: { newText: string, fullText: string }) => void diff --git a/src/vs/platform/void/common/refreshModelService.ts b/src/vs/platform/void/common/refreshModelService.ts index a724d8ced..f1f0c5752 100644 --- a/src/vs/platform/void/common/refreshModelService.ts +++ b/src/vs/platform/void/common/refreshModelService.ts @@ -5,7 +5,7 @@ import { createDecorator } from '../../instantiation/common/instantiation.js'; import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js'; -import { IVoidConfigStateService } from './voidConfigService.js'; +import { IVoidSettingsService } from './voidSettingsService.js'; import { ILLMMessageService } from './llmMessageService.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable } from '../../../base/common/lifecycle.js'; @@ -38,7 +38,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ readonly onDidChangeState: Event = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes constructor( - @IVoidConfigStateService private readonly voidConfigStateService: IVoidConfigStateService, + @IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService, @ILLMMessageService private readonly llmMessageService: ILLMMessageService, ) { super() @@ -46,11 +46,11 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ // on mount, refresh ollama models this.refreshOllamaModels() - // every time ollama.enabled changes, refresh ollama models - let relevantVals = () => [this.voidConfigStateService.state.settingsOfProvider.ollama.enabled, this.voidConfigStateService.state.settingsOfProvider.ollama.endpoint] + // every time ollama.enabled changes, refresh ollama models, like useEffect + let relevantVals = () => [this.voidSettingsService.state.settingsOfProvider.ollama.enabled, this.voidSettingsService.state.settingsOfProvider.ollama.endpoint] let prevVals = relevantVals() this._register( - this.voidConfigStateService.onDidChangeState(() => { // we might want to debounce this + this.voidSettingsService.onDidChangeState(() => { // we might want to debounce this const newVals = relevantVals() if (!eq(prevVals, newVals)) { this.refreshOllamaModels() @@ -75,7 +75,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ this._cancelTimeout() // if ollama is disabled, obivously done - if (this.voidConfigStateService.state.settingsOfProvider.ollama.enabled !== 'true') { + if (this.voidSettingsService.state.settingsOfProvider.ollama.enabled !== 'true') { this._setState('done') return } @@ -85,7 +85,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ this.llmMessageService.ollamaList({ onSuccess: ({ models }) => { - this.voidConfigStateService.setSettingOfProvider('ollama', 'models', models.map(model => model.name)) + this.voidSettingsService.setSettingOfProvider('ollama', 'models', models.map(model => model.name)) this._setState('done') }, onError: ({ error }) => { diff --git a/src/vs/platform/void/common/voidConfigService.ts b/src/vs/platform/void/common/voidConfigService.ts deleted file mode 100644 index 4b065f70d..000000000 --- a/src/vs/platform/void/common/voidConfigService.ts +++ /dev/null @@ -1,137 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Glass Devtools, Inc. All rights reserved. - * Void Editor additions licensed under the AGPL 3.0 License. - *--------------------------------------------------------------------------------------------*/ - -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 { defaultVoidProviderState, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName } from './voidConfigTypes.js'; - - -const STORAGE_KEY = 'void.voidConfigStateII' - -type SetSettingOfProviderFn = ( - providerName: ProviderName, - settingName: S, - newVal: SettingsOfProvider[ProviderName][S extends keyof SettingsOfProvider[ProviderName] ? S : never], -) => Promise; - -type SetModelSelectionOfFeature = ( - featureName: K, - newVal: ModelSelectionOfFeature[K], -) => Promise; - - -type VoidConfigState = { - readonly settingsOfProvider: SettingsOfProvider; // optionsOfProvider - readonly modelSelectionOfFeature: ModelSelectionOfFeature; // stateOfFeature -} - -export interface IVoidConfigStateService { - readonly _serviceBrand: undefined; - readonly state: VoidConfigState; - onDidChangeState: Event; - setSettingOfProvider: SetSettingOfProviderFn; - setModelSelectionOfFeature: SetModelSelectionOfFeature; -} - - -const defaultState = () => { - const d: VoidConfigState = { - settingsOfProvider: deepClone(defaultVoidProviderState), - modelSelectionOfFeature: { 'Ctrl+L': null, 'Ctrl+K': null, 'Autocomplete': null } - } - return d -} - - -export const IVoidConfigStateService = createDecorator('VoidConfigStateService'); -class VoidConfigService extends Disposable implements IVoidConfigStateService { - _serviceBrand: undefined; - - private readonly _onDidChangeState = new Emitter(); - readonly onDidChangeState: Event = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes - - state: VoidConfigState; - - constructor( - @IStorageService private readonly _storageService: IStorageService, - @IEncryptionService private readonly _encryptionService: IEncryptionService, - // 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() - - // read and update the actual state immediately - this._readVoidConfigState().then(voidConfigState => { - this._setState(voidConfigState) - }) - - } - - private async _readVoidConfigState(): Promise { - const encryptedPartialConfig = this._storageService.get(STORAGE_KEY, StorageScope.APPLICATION) - - if (!encryptedPartialConfig) - return defaultState() - - const voidConfigStateStr = await this._encryptionService.decrypt(encryptedPartialConfig) - return JSON.parse(voidConfigStateStr) - } - - - private async _storeVoidConfigState(voidConfigState: VoidConfigState) { - const encryptedVoidConfigStr = await this._encryptionService.encrypt(JSON.stringify(voidConfigState)) - this._storageService.store(STORAGE_KEY, encryptedVoidConfigStr, StorageScope.APPLICATION, StorageTarget.USER); - } - - setSettingOfProvider: SetSettingOfProviderFn = async (providerName, settingName, newVal) => { - const newState: VoidConfigState = { - ...this.state, - settingsOfProvider: { - ...this.state.settingsOfProvider, - [providerName]: { - ...this.state.settingsOfProvider[providerName], - [settingName]: newVal, - } - }, - } - // console.log('NEW STATE I', JSON.stringify(newState, null, 2)) - - await this._storeVoidConfigState(newState) - this._setState(newState) - } - - setModelSelectionOfFeature: SetModelSelectionOfFeature = async (featureName, newVal) => { - const newState: VoidConfigState = { - ...this.state, - modelSelectionOfFeature: { - ...this.state.modelSelectionOfFeature, - [featureName]: newVal - } - } - // console.log('NEW STATE II', JSON.stringify(newState, null, 2)) - - await this._storeVoidConfigState(newState) - this._setState(newState) - } - - - - // internal function to update state, should be called every time state changes - private async _setState(voidConfigState: VoidConfigState) { - this.state = voidConfigState - this._onDidChangeState.fire() - } - -} - -registerSingleton(IVoidConfigStateService, VoidConfigService, InstantiationType.Eager); diff --git a/src/vs/platform/void/common/voidSettingsService.ts b/src/vs/platform/void/common/voidSettingsService.ts new file mode 100644 index 000000000..6309213b6 --- /dev/null +++ b/src/vs/platform/void/common/voidSettingsService.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPL 3.0 License. + *--------------------------------------------------------------------------------------------*/ + +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 { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames } from './voidSettingsTypes.js'; + + +const STORAGE_KEY = 'void.voidConfigStateIV' + +type SetSettingOfProviderFn = ( + providerName: ProviderName, + settingName: S, + newVal: SettingsOfProvider[ProviderName][S extends keyof SettingsOfProvider[ProviderName] ? S : never], +) => Promise; + +type SetModelSelectionOfFeature = ( + featureName: K, + newVal: ModelSelectionOfFeature[K], + options?: { doNotApplyEffects?: true } +) => Promise; + + + +type ModelOption = { text: string, value: ModelSelection } + + + +export type VoidSettingsState = { + readonly settingsOfProvider: SettingsOfProvider; // optionsOfProvider + readonly modelSelectionOfFeature: ModelSelectionOfFeature; // stateOfFeature + + readonly _modelsList: ModelOption[] // computed based on the two above items +} + + + +export interface IVoidSettingsService { + readonly _serviceBrand: undefined; + readonly state: VoidSettingsState; + onDidChangeState: Event; + setSettingOfProvider: SetSettingOfProviderFn; + setModelSelectionOfFeature: SetModelSelectionOfFeature; +} + + +let _computeModelsList = (settingsOfProvider: SettingsOfProvider) => { + let modelsList: ModelOption[] = [] + for (const providerName of providerNames) { + const providerConfig = settingsOfProvider[providerName] + if (providerConfig.enabled !== 'true') continue + providerConfig.models?.forEach(modelName => { + modelsList.push({ text: `${modelName} (${providerName})`, value: { providerName, modelName } }) + }) + } + return modelsList +} + + +const defaultState = () => { + const d: VoidSettingsState = { + settingsOfProvider: deepClone(defaultSettingsOfProvider), + modelSelectionOfFeature: { 'Ctrl+L': null, 'Ctrl+K': null, 'Autocomplete': null }, + _modelsList: _computeModelsList(defaultSettingsOfProvider), + } + return d +} + + +export const IVoidSettingsService = createDecorator('VoidSettingsService'); +class VoidSettingsService extends Disposable implements IVoidSettingsService { + _serviceBrand: undefined; + + private readonly _onDidChangeState = new Emitter(); + readonly onDidChangeState: Event = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes + + state: VoidSettingsState; + + constructor( + @IStorageService private readonly _storageService: IStorageService, + @IEncryptionService private readonly _encryptionService: IEncryptionService, + // 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() + + // read and update the actual state immediately + this._readState().then(s => { + this.state = s + this._onDidChangeState.fire() + }) + } + + private async _readState(): Promise { + 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, + } + } + + // if changed models or enabled a provider, recompute models list + const modelsListChanged = settingName === 'models' || settingName === 'enabled' + const newModelsList = modelsListChanged ? _computeModelsList(newSettingsOfProvider) : this.state._modelsList + + const newState: VoidSettingsState = { + modelSelectionOfFeature: newModelSelectionOfFeature, + settingsOfProvider: newSettingsOfProvider, + _modelsList: 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.value, currentSelection)) + + if (selnIdx === -1) { + if (newModelsList.length !== 0) + this.setModelSelectionOfFeature(featureName, newModelsList[0].value, { doNotApplyEffects: true }) + else + this.setModelSelectionOfFeature(featureName, null, { doNotApplyEffects: true }) + } + } + } + + await this._storeState() + this._onDidChangeState.fire() + } + + + setModelSelectionOfFeature: SetModelSelectionOfFeature = async (featureName, newVal, options) => { + const newState: VoidSettingsState = { + ...this.state, + modelSelectionOfFeature: { + ...this.state.modelSelectionOfFeature, + [featureName]: newVal + } + } + + this.state = newState + + if (options?.doNotApplyEffects) + return + console.log('NEW STATE II', JSON.stringify(newState, null, 2)) + + await this._storeState() + this._onDidChangeState.fire() + } + + + +} + + +registerSingleton(IVoidSettingsService, VoidSettingsService, InstantiationType.Eager); diff --git a/src/vs/platform/void/common/voidConfigTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts similarity index 91% rename from src/vs/platform/void/common/voidConfigTypes.ts rename to src/vs/platform/void/common/voidSettingsTypes.ts index 5415af4b8..810e9651c 100644 --- a/src/vs/platform/void/common/voidConfigTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -76,12 +76,12 @@ export const anthropicMaxPossibleTokens = (modelName: string) => { } -export const dummyModelData = { - anthropic: ['claude 3.5'], - openAI: ['gpt 4o'], - ollama: ['llama 3.2', 'codestral'], - openRouter: ['qwen 2.5'], -} +// export const dummyModelData = { +// anthropic: ['claude 3.5'], +// openAI: ['gpt 4o'], +// ollama: ['llama 3.2', 'codestral'], +// openRouter: ['qwen 2.5'], +// } @@ -234,7 +234,7 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName // used when waiting and for a type reference -export const defaultVoidProviderState: SettingsOfProvider = { +export const defaultSettingsOfProvider: SettingsOfProvider = { anthropic: { ...voidProviderDefaults.anthropic, ...voidInitModelOptions.anthropic, @@ -273,21 +273,17 @@ export const defaultVoidProviderState: SettingsOfProvider = { } +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 type ModelSelectionOfFeature = { - 'Ctrl+L': { - providerName: ProviderName, - modelName: string, - } | null, - 'Ctrl+K': { - providerName: ProviderName, - modelName: string, - } | null, - 'Autocomplete': { - providerName: ProviderName, - modelName: string, - } | null, + 'Ctrl+L': ModelSelection | null, + 'Ctrl+K': ModelSelection | null, + 'Autocomplete': ModelSelection | null, } export type FeatureName = keyof ModelSelectionOfFeature export const featureNames = ['Ctrl+L', 'Ctrl+K', 'Autocomplete'] as const diff --git a/src/vs/platform/void/electron-main/llmMessage/anthropic.ts b/src/vs/platform/void/electron-main/llmMessage/anthropic.ts index 964047b26..19a4ddef2 100644 --- a/src/vs/platform/void/electron-main/llmMessage/anthropic.ts +++ b/src/vs/platform/void/electron-main/llmMessage/anthropic.ts @@ -5,7 +5,7 @@ import Anthropic from '@anthropic-ai/sdk'; import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js'; -import { anthropicMaxPossibleTokens } from '../../common/voidConfigTypes.js'; +import { anthropicMaxPossibleTokens } from '../../common/voidSettingsTypes.js'; // Anthropic type LLMMessageAnthropic = { diff --git a/src/vs/workbench/contrib/void/browser/.gitignore b/src/vs/workbench/contrib/void/browser/.gitignore deleted file mode 100644 index 897c5fc64..000000000 --- a/src/vs/workbench/contrib/void/browser/.gitignore +++ /dev/null @@ -1 +0,0 @@ -void-imports/ diff --git a/src/vs/workbench/contrib/void/browser/registerAutocomplete.ts b/src/vs/workbench/contrib/void/browser/autocompleteService.ts similarity index 100% rename from src/vs/workbench/contrib/void/browser/registerAutocomplete.ts rename to src/vs/workbench/contrib/void/browser/autocompleteService.ts diff --git a/src/vs/workbench/contrib/void/browser/findDiffs.ts b/src/vs/workbench/contrib/void/browser/helpers/findDiffs.ts similarity index 99% rename from src/vs/workbench/contrib/void/browser/findDiffs.ts rename to src/vs/workbench/contrib/void/browser/helpers/findDiffs.ts index 60643e329..8586a3caf 100644 --- a/src/vs/workbench/contrib/void/browser/findDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/helpers/findDiffs.ts @@ -3,7 +3,7 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import { diffLines } from './react/out/diff/index.js' +import { diffLines } from '../react/out/diff/index.js' export type ComputedDiff = { type: 'edit'; diff --git a/src/vs/workbench/contrib/void/browser/getCmdKey.ts b/src/vs/workbench/contrib/void/browser/helpers/getCmdKey.ts similarity index 88% rename from src/vs/workbench/contrib/void/browser/getCmdKey.ts rename to src/vs/workbench/contrib/void/browser/helpers/getCmdKey.ts index d21540685..fe520e42f 100644 --- a/src/vs/workbench/contrib/void/browser/getCmdKey.ts +++ b/src/vs/workbench/contrib/void/browser/helpers/getCmdKey.ts @@ -3,7 +3,7 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import { isMacintosh } from '../../../../base/common/platform.js'; +import { isMacintosh } from '../../../../../base/common/platform.js'; // import { OperatingSystem, OS } from '../../../../base/common/platform.js'; // OS === OperatingSystem.Macintosh diff --git a/src/vs/workbench/contrib/void/browser/helpers/reactServicesHelper.ts b/src/vs/workbench/contrib/void/browser/helpers/reactServicesHelper.ts new file mode 100644 index 000000000..3c043344b --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/helpers/reactServicesHelper.ts @@ -0,0 +1,51 @@ +import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; +import { IModelService } from '../../../../../editor/common/services/model.js'; +import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; +import { IContextViewService, IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { IHoverService } from '../../../../../platform/hover/browser/hover.js'; +import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; +import { ILLMMessageService } from '../../../../../platform/void/common/llmMessageService.js'; +import { IRefreshModelService } from '../../../../../platform/void/common/refreshModelService.js'; +import { IVoidSettingsService } from '../../../../../platform/void/common/voidSettingsService.js'; +import { IInlineDiffsService } from '../inlineDiffsService.js'; +import { ISidebarStateService } from '../sidebarStateService.js'; +import { IThreadHistoryService } from '../threadHistoryService.js'; + +export type ReactServicesType = { + sidebarStateService: ISidebarStateService; + settingsStateService: IVoidSettingsService; + threadsStateService: IThreadHistoryService; + fileService: IFileService; + modelService: IModelService; + inlineDiffService: IInlineDiffsService; + llmMessageService: ILLMMessageService; + clipboardService: IClipboardService; + refreshModelService: IRefreshModelService; + + themeService: IThemeService, + hoverService: IHoverService, + + contextViewService: IContextViewService; + contextMenuService: IContextMenuService; +} + + +export const getReactServices = (accessor: ServicesAccessor): ReactServicesType => { + return { + settingsStateService: accessor.get(IVoidSettingsService), + sidebarStateService: accessor.get(ISidebarStateService), + threadsStateService: accessor.get(IThreadHistoryService), + fileService: accessor.get(IFileService), + modelService: accessor.get(IModelService), + inlineDiffService: accessor.get(IInlineDiffsService), + llmMessageService: accessor.get(ILLMMessageService), + clipboardService: accessor.get(IClipboardService), + themeService: accessor.get(IThemeService), + hoverService: accessor.get(IHoverService), + refreshModelService: accessor.get(IRefreshModelService), + contextViewService: accessor.get(IContextViewService), + contextMenuService: accessor.get(IContextMenuService), + } +} + diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts similarity index 99% rename from src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts rename to src/vs/workbench/contrib/void/browser/inlineDiffsService.ts index cb4ae8588..5d6c0c494 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts @@ -11,9 +11,8 @@ import { ICodeEditor, IOverlayWidget, IViewZone } from '../../../../editor/brows // import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; // import { throttle } from '../../../../base/common/decorators.js'; -// import { IVoidConfigStateService } from './registerConfig.js'; import { writeFileWithDiffInstructions } from './prompt/systemPrompts.js'; -import { ComputedDiff, findDiffs } from './findDiffs.js'; +import { ComputedDiff, findDiffs } from './helpers/findDiffs.js'; import { EndOfLinePreference, ITextModel } from '../../../../editor/common/model.js'; import { IRange } from '../../../../editor/common/core/range.js'; import { registerColor } from '../../../../platform/theme/common/colorUtils.js'; @@ -144,7 +143,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { constructor( // @IHistoryService private readonly _historyService: IHistoryService, // history service is the history of pressing alt left/right - // @IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService, @ICodeEditorService private readonly _editorService: ICodeEditorService, @IModelService private readonly _modelService: IModelService, @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, // undoRedo service is the history of pressing ctrl+z diff --git a/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts b/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts index 17372ec9f..e23ac6f49 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/stringifySelections.ts @@ -3,7 +3,7 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import { CodeSelection } from '../registerThreads.js'; +import { CodeSelection } from '../threadHistoryService.js'; export const stringifySelections = (selections: CodeSelection[]) => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 92e44671a..b83d9cebc 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -6,10 +6,10 @@ import React, { FormEvent, Fragment, useCallback, useEffect, useRef, useState } from 'react'; -import { useConfigState, useService, useSidebarState, useThreadsState } from '../util/services.js'; +import { useSettingsState, useService, useSidebarState, useThreadsState } from '../util/services.js'; import { generateDiffInstructions } from '../../../prompt/systemPrompts.js'; import { userInstructionsStr } from '../../../prompt/stringifySelections.js'; -import { ChatMessage, CodeSelection, CodeStagingSelection } from '../../../registerThreads.js'; +import { ChatMessage, CodeSelection, CodeStagingSelection } from '../../../threadHistoryService.js'; import { BlockCode } from '../markdown/BlockCode.js'; import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js'; @@ -19,7 +19,7 @@ import { EndOfLinePreference } from '../../../../../../../editor/common/model.js import { IDisposable } from '../../../../../../../base/common/lifecycle.js'; import { ErrorDisplay } from './ErrorDisplay.js'; import { OnError, ServiceSendLLMMessageParams } from '../../../../../../../platform/void/common/llmMessageTypes.js'; -import { getCmdKey } from '../../../getCmdKey.js' +import { getCmdKey } from '../../../helpers/getCmdKey.js' import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; import { VoidInputBox } from './inputs.js'; import { ModelSelectionOfFeature } from '../void-settings-tsx/ModelSelectionSettings.js'; @@ -277,9 +277,6 @@ export const SidebarChat = () => { return () => disposables.forEach(d => d.dispose()) }, [sidebarStateService, inputBoxRef]) - // config state - const voidConfigState = useConfigState() - // threads state const threadsState = useThreadsState() const threadsStateService = useService('threadsStateService') diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx index 56524b601..b4de1b6f8 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx @@ -6,7 +6,7 @@ import React, { useEffect, useState } from 'react'; import * as ReactDOM from 'react-dom/client' import { _registerServices } from './services.js'; -import { ReactServicesType } from '../../../reactServices.js'; +import { ReactServicesType } from '../../../helpers/reactServicesHelper.js'; export const mountFnGenerator = (Component: React.FC) => (rootElement: HTMLElement, services: ReactServicesType) => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx index 25fb80f3e..70219c0b1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx @@ -4,35 +4,38 @@ *--------------------------------------------------------------------------------------------*/ import { useState, useEffect } from 'react' -import { VoidSidebarState } from '../../../registerSidebar.js' -import { ThreadsState } from '../../../registerThreads.js' -import { SettingsOfProvider } from '../../../../../../../platform/void/common/voidConfigTypes.js' +import { ThreadsState } from '../../../threadHistoryService.js' +import { SettingsOfProvider } from '../../../../../../../platform/void/common/voidSettingsTypes.js' import { RefreshModelState } from '../../../../../../../platform/void/common/refreshModelService.js' import { IDisposable } from '../../../../../../../base/common/lifecycle.js' -import { ReactServicesType } from '../../../reactServices.js' +import { ReactServicesType } from '../../../helpers/reactServicesHelper.js' +import { VoidSidebarState } from '../../../sidebarStateService.js' +import { VoidSettingsState } from '../../../../../../../platform/void/common/voidSettingsService.js' // normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes let services: ReactServicesType -// even if React hasn't mounted yet, these variables are always updated to the latest state: +// even if React hasn't mounted yet, the variables are always updated to the latest state. +// React listens by adding a setState function to these listeners. let sidebarState: VoidSidebarState -let threadsState: ThreadsState -let settingsOfProvider: SettingsOfProvider -let refreshModelState: RefreshModelState - -// React listens by adding a setState function to these: const sidebarStateListeners: Set<(s: VoidSidebarState) => void> = new Set() + +let threadsState: ThreadsState const threadsStateListeners: Set<(s: ThreadsState) => void> = new Set() -const settingsOfProviderListeners: Set<(s: SettingsOfProvider) => void> = new Set() + +let settingsState: VoidSettingsState +const settingsStateListeners: Set<(s: VoidSettingsState) => void> = new Set() + +let refreshModelState: RefreshModelState const refreshModelStateListeners: Set<(s: RefreshModelState) => void> = new Set() + + // must call this before you can use any of the hooks below // this should only be called ONCE! this is the only place you don't need to dispose onDidChange. If you use state.onDidChange anywhere else, make sure to dispose it! - let wasCalled = false - export const _registerServices = (services_: ReactServicesType) => { const disposables: IDisposable[] = [] @@ -41,7 +44,7 @@ export const _registerServices = (services_: ReactServicesType) => { wasCalled = true services = services_ - const { sidebarStateService, configStateService, threadsStateService, refreshModelService } = services + const { sidebarStateService, settingsStateService, threadsStateService, refreshModelService } = services sidebarState = sidebarStateService.state disposables.push( @@ -59,11 +62,11 @@ export const _registerServices = (services_: ReactServicesType) => { }) ) - settingsOfProvider = configStateService.state.settingsOfProvider + settingsState = settingsStateService.state disposables.push( - configStateService.onDidChangeState(() => { - settingsOfProvider = configStateService.state.settingsOfProvider - settingsOfProviderListeners.forEach(l => l(settingsOfProvider)) + settingsStateService.onDidChangeState(() => { + settingsState = settingsStateService.state + settingsStateListeners.forEach(l => l(settingsState)) }) ) @@ -99,12 +102,12 @@ export const useSidebarState = () => { return s } -export const useConfigState = () => { - const [s, ss] = useState(settingsOfProvider) +export const useSettingsState = () => { + const [s, ss] = useState(settingsState) useEffect(() => { - ss(settingsOfProvider) - settingsOfProviderListeners.add(ss) - return () => { settingsOfProviderListeners.delete(ss) } + ss(settingsState) + settingsStateListeners.add(ss) + return () => { settingsStateListeners.delete(ss) } }, [ss]) return s } diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx index 2e95d1edb..beefbc3c7 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx @@ -4,66 +4,44 @@ *--------------------------------------------------------------------------------------------*/ import { useCallback, useEffect, useRef, useState } from 'react' -import { FeatureName, featureNames, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js' -import { dummyModelData } from '../../../../../../../platform/void/common/voidConfigTypes.js' -import { useConfigState, useRefreshModelState, useService } from '../util/services.js' +import { FeatureName, featureNames, ModelSelection, modelSelectionsEqual, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidSettingsTypes.js' +import { useSettingsState, useRefreshModelState, useService } from '../util/services.js' import { VoidSelectBox } from '../sidebar-tsx/inputs.js' import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js' export const ModelSelectionOfFeature = ({ featureName }: { featureName: FeatureName }) => { - const voidConfigService = useService('configStateService') - const voidConfigState = useConfigState() - - const modelOptions: { text: string, value: [string, string] }[] = [] - - for (const providerName of providerNames) { - const providerConfig = voidConfigState[providerName] - if (providerConfig.enabled !== 'true') continue - providerConfig.models?.forEach(model => { - modelOptions.push({ text: `${model} (${providerName})`, value: [providerName, model] }) - }) - } - - - const isDummy = modelOptions.length === 0 - if (isDummy) { - for (const [providerName, models] of Object.entries(dummyModelData)) { - for (let model of models) { - modelOptions.push({ text: `${model} (${providerName})`, value: ['dummy', 'dummy'] }) - } - } - } + const voidSettingsService = useService('settingsStateService') + const settingsState = useSettingsState() let weChangedText = false - return <>

{featureName}

{ { - if (isDummy) return // don't set state to the dummy value + options={settingsState._modelsList} + onChangeSelection={useCallback((newVal: ModelSelection) => { if (weChangedText) return - voidConfigService.setModelSelectionOfFeature(featureName, { providerName: newVal[0] as ProviderName, modelName: newVal[1] }) - }, [voidConfigService, featureName, isDummy])} + voidSettingsService.setModelSelectionOfFeature(featureName, newVal) + }, [voidSettingsService, featureName])} // we are responsible for setting the initial state here. always sync instance when state changes. onCreateInstance={useCallback((instance: SelectBox) => { const syncInstance = () => { - const settingsAtProvider = voidConfigService.state.modelSelectionOfFeature[featureName] - const index = modelOptions.findIndex(v => v.value[0] === settingsAtProvider?.providerName && v.value[1] === settingsAtProvider?.modelName) - if (index !== -1) { + const modelsListRef = voidSettingsService.state._modelsList // as a ref + const settingsAtProvider = voidSettingsService.state.modelSelectionOfFeature[featureName] + const selectionIdx = settingsAtProvider === null ? -1 : modelsListRef.findIndex(v => modelSelectionsEqual(v.value, settingsAtProvider)) + if (selectionIdx !== -1) { weChangedText = true - instance.select(index) + instance.select(selectionIdx) weChangedText = false } } syncInstance() - const disposable = voidConfigService.onDidChangeState(syncInstance) + const disposable = voidSettingsService.onDidChangeState(syncInstance) return [disposable] - }, [voidConfigService, modelOptions, featureName])} + }, [voidSettingsService, featureName])} />} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx index 33a1b95ca..0b91bf93d 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react' -import { titleOfProviderName, displayInfoOfSettingName, ProviderName, providerNames, featureNames, SettingsOfProvider, SettingName, defaultVoidProviderState } from '../../../../../../../platform/void/common/voidConfigTypes.js' +import { titleOfProviderName, displayInfoOfSettingName, ProviderName, providerNames, featureNames, SettingsOfProvider, SettingName, defaultSettingsOfProvider } from '../../../../../../../platform/void/common/voidSettingsTypes.js' import { VoidInputBox } from '../sidebar-tsx/inputs.js' -import { useConfigState, useService } from '../util/services.js' +import { useSettingsState, useService } from '../util/services.js' import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js' import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' @@ -14,41 +14,34 @@ import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' const Setting = ({ providerName, settingName }: { providerName: ProviderName, settingName: SettingName }) => { const { title, type, placeholder } = displayInfoOfSettingName(providerName, settingName) - const voidConfigService = useService('configStateService') + const voidSettingsService = useService('settingsStateService') - let weChangedText = false + let weChangedTextRef = false return <> { - if (weChangedText) return - - voidConfigService.setSettingOfProvider(providerName, settingName, newVal) - // if we just disabeld this provider, we should unselect all models that use it - if (settingName === 'enabled' && newVal !== 'true') { - for (let featureName of featureNames) { - if (voidConfigService.state.modelSelectionOfFeature[featureName]?.providerName === providerName) - voidConfigService.setModelSelectionOfFeature(featureName, null) - } - } - }, [voidConfigService, providerName, settingName])} + if (weChangedTextRef) return + voidSettingsService.setSettingOfProvider(providerName, settingName, newVal) + }, [voidSettingsService, providerName, settingName])} // we are responsible for setting the initial value. always sync the instance whenever there's a change to state. onCreateInstance={useCallback((instance: InputBox) => { const syncInstance = () => { - const settingsAtProvider = voidConfigService.state.settingsOfProvider[providerName]; + const settingsAtProvider = voidSettingsService.state.settingsOfProvider[providerName]; const stateVal = settingsAtProvider[settingName as keyof typeof settingsAtProvider] - weChangedText = true + // console.log('SYNCING TO', providerName, settingName, stateVal) + weChangedTextRef = true instance.value = stateVal as string - weChangedText = false + weChangedTextRef = false } syncInstance() - const disposable = voidConfigService.onDidChangeState(syncInstance) + const disposable = voidSettingsService.onDidChangeState(syncInstance) return [disposable] - }, [voidConfigService, providerName, settingName])} + }, [voidSettingsService, providerName, settingName])} multiline={false} /> @@ -57,8 +50,8 @@ const Setting = ({ providerName, settingName }: { providerName: ProviderName, se const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => { - const voidConfigState = useConfigState() - const { models, ...others } = voidConfigState[providerName] + const voidSettingsState = useSettingsState() + const { models, ...others } = voidSettingsState.settingsOfProvider[providerName] return <>

{titleOfProviderName(providerName)}

@@ -77,7 +70,5 @@ export const VoidProviderSettings = () => { {providerNames.map(providerName => )} - - } diff --git a/src/vs/workbench/contrib/void/browser/reactServices.ts b/src/vs/workbench/contrib/void/browser/reactServices.ts deleted file mode 100644 index e465ca931..000000000 --- a/src/vs/workbench/contrib/void/browser/reactServices.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; -import { IContextViewService, IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { IHoverService } from '../../../../platform/hover/browser/hover.js'; -import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js'; -import { IRefreshModelService } from '../../../../platform/void/common/refreshModelService.js'; -import { IVoidConfigStateService } from '../../../../platform/void/common/voidConfigService.js'; -import { IInlineDiffsService } from './registerInlineDiffs.js'; -import { IVoidSidebarStateService } from './registerSidebar.js'; -import { IThreadHistoryService } from './registerThreads.js'; - -export type ReactServicesType = { - sidebarStateService: IVoidSidebarStateService; - configStateService: IVoidConfigStateService; - threadsStateService: IThreadHistoryService; - fileService: IFileService; - modelService: IModelService; - inlineDiffService: IInlineDiffsService; - llmMessageService: ILLMMessageService; - clipboardService: IClipboardService; - refreshModelService: IRefreshModelService; - - themeService: IThemeService, - hoverService: IHoverService, - - contextViewService: IContextViewService; - contextMenuService: IContextMenuService; -} - - -export const getReactServices = (accessor: ServicesAccessor): ReactServicesType => { - return { - configStateService: accessor.get(IVoidConfigStateService), - sidebarStateService: accessor.get(IVoidSidebarStateService), - threadsStateService: accessor.get(IThreadHistoryService), - fileService: accessor.get(IFileService), - modelService: accessor.get(IModelService), - inlineDiffService: accessor.get(IInlineDiffsService), - llmMessageService: accessor.get(ILLMMessageService), - clipboardService: accessor.get(IClipboardService), - themeService: accessor.get(IThemeService), - hoverService: accessor.get(IHoverService), - refreshModelService: accessor.get(IRefreshModelService), - contextViewService: accessor.get(IContextViewService), - contextMenuService: accessor.get(IContextMenuService), - } -} - diff --git a/src/vs/workbench/contrib/void/browser/registerActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts similarity index 92% rename from src/vs/workbench/contrib/void/browser/registerActions.ts rename to src/vs/workbench/contrib/void/browser/sidebarActions.ts index 16bb76004..bc80596d2 100644 --- a/src/vs/workbench/contrib/void/browser/registerActions.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts @@ -11,16 +11,16 @@ import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; -import { CodeStagingSelection, IThreadHistoryService } from './registerThreads.js'; +import { CodeStagingSelection, IThreadHistoryService } from './threadHistoryService.js'; // import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { IRange } from '../../../../editor/common/core/range.js'; import { ITextModel } from '../../../../editor/common/model.js'; -import { IVoidSidebarStateService, VOID_VIEW_ID } from './registerSidebar.js'; +import { VOID_VIEW_ID } from './sidebarViewPane.js'; import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; -// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; +import { ISidebarStateService } from './sidebarStateService.js'; // ---------- Register commands and keybindings ---------- @@ -61,7 +61,7 @@ registerAction2(class extends Action2 { if (!model) return - const stateService = accessor.get(IVoidSidebarStateService) + const stateService = accessor.get(ISidebarStateService) const metricsService = accessor.get(IMetricsService) metricsService.capture('Chat Navigation', { type: 'Ctrl+L' }) @@ -126,7 +126,7 @@ registerAction2(class extends Action2 { }); } async run(accessor: ServicesAccessor): Promise { - const stateService = accessor.get(IVoidSidebarStateService) + const stateService = accessor.get(ISidebarStateService) const metricsService = accessor.get(IMetricsService) metricsService.capture('Chat Navigation', { type: 'New Chat' }) @@ -149,7 +149,7 @@ registerAction2(class extends Action2 { }); } async run(accessor: ServicesAccessor): Promise { - const stateService = accessor.get(IVoidSidebarStateService) + const stateService = accessor.get(ISidebarStateService) const metricsService = accessor.get(IMetricsService) metricsService.capture('Chat Navigation', { type: 'History' }) @@ -159,7 +159,6 @@ registerAction2(class extends Action2 { } }) -// // Settings (API config) menu button // registerAction2(class extends Action2 { // constructor() { // super({ diff --git a/src/vs/workbench/contrib/void/browser/sidebarStateService.ts b/src/vs/workbench/contrib/void/browser/sidebarStateService.ts new file mode 100644 index 000000000..0cab1180d --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/sidebarStateService.ts @@ -0,0 +1,84 @@ +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 { IViewsService } from '../../../services/views/common/viewsService.js'; +import { VOID_VIEW_CONTAINER_ID, VOID_VIEW_ID } from './sidebarViewPane.js'; + + +// service that manages sidebar's state +export type VoidSidebarState = { + isHistoryOpen: boolean; + currentTab: 'chat'; +} + +export interface ISidebarStateService { + readonly _serviceBrand: undefined; + + readonly state: VoidSidebarState; // readonly to the user + setState(newState: Partial): void; + onDidChangeState: Event; + + onDidFocusChat: Event; + onDidBlurChat: Event; + fireFocusChat(): void; + fireBlurChat(): void; + + openSidebarView(): void; +} + +export const ISidebarStateService = createDecorator('voidSidebarStateService'); +class VoidSidebarStateService extends Disposable implements ISidebarStateService { + _serviceBrand: undefined; + + static readonly ID = 'voidSidebarStateService'; + + private readonly _onDidChangeState = new Emitter(); + readonly onDidChangeState: Event = this._onDidChangeState.event; + + private readonly _onFocusChat = new Emitter(); + readonly onDidFocusChat: Event = this._onFocusChat.event; + + private readonly _onBlurChat = new Emitter(); + readonly onDidBlurChat: Event = this._onBlurChat.event; + + + // state + state: VoidSidebarState + + constructor( + @IViewsService private readonly _viewsService: IViewsService, + ) { + super() + + // initial state + this.state = { isHistoryOpen: false, currentTab: 'chat', } + } + + + setState(newState: Partial) { + // make sure view is open if the tab changes + if ('currentTab' in newState) { + this.openSidebarView() + } + + this.state = { ...this.state, ...newState } + this._onDidChangeState.fire() + } + + fireFocusChat() { + this._onFocusChat.fire() + } + + fireBlurChat() { + this._onBlurChat.fire() + } + + openSidebarView() { + this._viewsService.openViewContainer(VOID_VIEW_CONTAINER_ID); + this._viewsService.openView(VOID_VIEW_ID); + } + +} + +registerSingleton(ISidebarStateService, VoidSidebarStateService, InstantiationType.Eager); diff --git a/src/vs/workbench/contrib/void/browser/registerSidebar.ts b/src/vs/workbench/contrib/void/browser/sidebarViewPane.ts similarity index 67% rename from src/vs/workbench/contrib/void/browser/registerSidebar.ts rename to src/vs/workbench/contrib/void/browser/sidebarViewPane.ts index 6bed1eee9..8b778474e 100644 --- a/src/vs/workbench/contrib/void/browser/registerSidebar.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarViewPane.ts @@ -24,9 +24,8 @@ import { SyncDescriptor } from '../../../../platform/instantiation/common/descri import { IViewPaneOptions, ViewPane } from '../../../browser/parts/views/viewPane.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; @@ -37,18 +36,11 @@ import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { mountSidebar } from './react/out/sidebar-tsx/index.js'; -import { IViewsService } from '../../../services/views/common/viewsService.js'; -import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { getReactServices } from './reactServices.js'; +import { getReactServices } from './helpers/reactServicesHelper.js'; // compare against search.contribution.ts and debug.contribution.ts, scm.contribution.ts (source control) -export type VoidSidebarState = { - isHistoryOpen: boolean; - currentTab: 'chat'; -} - // ---------- Define viewpane ---------- class VoidSidebarViewPane extends ViewPane { @@ -132,77 +124,3 @@ viewsRegistry.registerViews([{ // }, }], viewContainer); - - -// ---------- Register service that manages sidebar's state ---------- - -export interface IVoidSidebarStateService { - readonly _serviceBrand: undefined; - - readonly state: VoidSidebarState; // readonly to the user - setState(newState: Partial): void; - onDidChangeState: Event; - - onDidFocusChat: Event; - onDidBlurChat: Event; - fireFocusChat(): void; - fireBlurChat(): void; - - openSidebarView(): void; -} - -export const IVoidSidebarStateService = createDecorator('voidSidebarStateService'); -class VoidSidebarStateService extends Disposable implements IVoidSidebarStateService { - _serviceBrand: undefined; - - static readonly ID = 'voidSidebarStateService'; - - private readonly _onDidChangeState = new Emitter(); - readonly onDidChangeState: Event = this._onDidChangeState.event; - - private readonly _onFocusChat = new Emitter(); - readonly onDidFocusChat: Event = this._onFocusChat.event; - - private readonly _onBlurChat = new Emitter(); - readonly onDidBlurChat: Event = this._onBlurChat.event; - - - // state - state: VoidSidebarState - - constructor( - @IViewsService private readonly _viewsService: IViewsService, - ) { - super() - - // initial state - this.state = { isHistoryOpen: false, currentTab: 'chat', } - } - - - setState(newState: Partial) { - // make sure view is open if the tab changes - if ('currentTab' in newState) { - this.openSidebarView() - } - - this.state = { ...this.state, ...newState } - this._onDidChangeState.fire() - } - - fireFocusChat() { - this._onFocusChat.fire() - } - - fireBlurChat() { - this._onBlurChat.fire() - } - - openSidebarView() { - this._viewsService.openViewContainer(VOID_VIEW_CONTAINER_ID); - this._viewsService.openView(VOID_VIEW_ID); - } - -} - -registerSingleton(IVoidSidebarStateService, VoidSidebarStateService, InstantiationType.Eager); diff --git a/src/vs/workbench/contrib/void/browser/registerThreads.ts b/src/vs/workbench/contrib/void/browser/threadHistoryService.ts similarity index 98% rename from src/vs/workbench/contrib/void/browser/registerThreads.ts rename to src/vs/workbench/contrib/void/browser/threadHistoryService.ts index d92e8942e..9d704c656 100644 --- a/src/vs/workbench/contrib/void/browser/registerThreads.ts +++ b/src/vs/workbench/contrib/void/browser/threadHistoryService.ts @@ -10,7 +10,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { URI } from '../../../../base/common/uri.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { IAutocompleteService } from './registerAutocomplete.js'; +import { IAutocompleteService } from './autocompleteService.js'; import { IRange } from '../../../../editor/common/core/range.js'; export type CodeSelection = { @@ -80,7 +80,7 @@ const newThreadObject = () => { } } -const THREAD_STORAGE_KEY = 'void.threadsHistory' +const THREAD_STORAGE_KEY = 'void.threadHistory' export interface IThreadHistoryService { readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/void/browser/void.contribution.ts b/src/vs/workbench/contrib/void/browser/void.contribution.ts index dbb6f0ab0..ee12a345a 100644 --- a/src/vs/workbench/contrib/void/browser/void.contribution.ts +++ b/src/vs/workbench/contrib/void/browser/void.contribution.ts @@ -3,23 +3,23 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -// register keybinds -import './registerActions.js' // register inline diffs -import './registerInlineDiffs.js' +import './inlineDiffsService.js' -// register Sidebar chat -import './registerSidebar.js' +// register Sidebar pane, state, actions (keybinds, menus) +import './sidebarActions.js' +import './sidebarViewPane.js' +import './sidebarStateService.js' // register Thread History -import './registerThreads.js' +import './threadHistoryService.js' // register Autocomplete -import './registerAutocomplete.js' +import './autocompleteService.js' // register css import './media/void.css' // settings pane -import './voidSettingsEditorPane.js' +import './voidSettingsPane.js' diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsEditorPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts similarity index 84% rename from src/vs/workbench/contrib/void/browser/voidSettingsEditorPane.ts rename to src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index cb5f47325..3bf9f685b 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsEditorPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -15,10 +15,8 @@ import { IStorageService } from '../../../../platform/storage/common/storage.js' import { Dimension } from '../../../../base/browser/dom.js'; import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; -import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; -import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; -import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { URI } from '../../../../base/common/uri.js'; @@ -26,12 +24,13 @@ import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextke import { mountVoidSettings } from './react/out/void-settings-tsx/index.js' -import { getReactServices } from './reactServices.js'; +import { getReactServices } from './helpers/reactServicesHelper.js'; +import { Codicon } from '../../../../base/common/codicons.js'; // refer to preferences.contribution.ts keybindings editor -export class VoidEditorInput extends EditorInput { +class VoidEditorInput extends EditorInput { static readonly ID: string = 'workbench.input.void.settings'; @@ -93,17 +92,25 @@ Registry.as(EditorExtensions.EditorPane).registerEditorPane ); -// Register the action +// Register the gear registerAction2(class extends Action2 { constructor() { super({ - id: 'workbench.action.openVoidEditor', - title: 'Open Void Settings', - keybinding: { - when: ContextKeyExpr.true(), - primary: KeyMod.CtrlCmd | KeyCode.KeyE, - weight: KeybindingWeight.WorkbenchContrib - } + id: 'workbench.action.openVoidSettings', + title: nls.localize2('voidSettings', "Void: Settings"), + f1: true, + icon: Codicon.gear, + menu: [ + { + id: MenuId.LayoutControlMenuSubmenu, + group: 'z_end', + }, + { + id: MenuId.LayoutControlMenu, + when: ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both'), + group: 'z_end' + } + ] }); } async run(accessor: ServicesAccessor): Promise { @@ -112,5 +119,4 @@ registerAction2(class extends Action2 { const input = instantiationService.createInstance(VoidEditorInput); await editorService.openEditor(input); } -}); - +})