/*--------------------------------------------------------------------------------------------- * 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);