mirror of
https://github.com/voideditor/void
synced 2026-05-24 01:48:25 +00:00
services work in react
This commit is contained in:
parent
289da6bd6d
commit
9fc3e413a9
8 changed files with 285 additions and 255 deletions
|
|
@ -1,46 +1,18 @@
|
|||
import React, { useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { mountFnGenerator } from '../util/mountFnGenerator'
|
||||
import { VIEWPANE_FILTER_ACTION } from '../../../../../browser/parts/views/viewPane.js'
|
||||
import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation'
|
||||
|
||||
import { SidebarSettings } from './SidebarSettings.js';
|
||||
import { useServices } from '../util/contextForServices.js';
|
||||
import { IVoidSidebarStateService, VoidSidebarState } from '../../registerSidebar.js';
|
||||
// import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
// import { SidebarChat } from './SidebarChat.js';
|
||||
// import { SidebarSettings } from './SidebarSettings.js';
|
||||
console.log('!!filteraction', VIEWPANE_FILTER_ACTION)
|
||||
const Sidebar = ({ accessor }: { accessor: ServicesAccessor }) => {
|
||||
|
||||
// const chatInputRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
|
||||
const [tab, setTab] = useState<'threadSelector' | 'chat' | 'settings'>('chat')
|
||||
|
||||
// // if they pressed the + to add a new chat
|
||||
// useOnVSCodeMessage('startNewThread', (m) => {
|
||||
// setTab('chat');
|
||||
// chatInputRef.current?.focus();
|
||||
// })
|
||||
|
||||
// // ctrl+l should switch back to chat
|
||||
// useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// setTab('chat');
|
||||
// chatInputRef.current?.focus();
|
||||
// })
|
||||
|
||||
// // if they toggled thread selector
|
||||
// useOnVSCodeMessage('toggleThreadSelector', (m) => {
|
||||
// if (tab === 'threadSelector') {
|
||||
// setTab('chat')
|
||||
// chatInputRef.current?.blur();
|
||||
// } else
|
||||
// setTab('threadSelector')
|
||||
// })
|
||||
|
||||
// // if they toggled settings
|
||||
// useOnVSCodeMessage('toggleSettings', (m) => {
|
||||
// if (tab === 'settings') {
|
||||
// setTab('chat')
|
||||
// chatInputRef.current?.blur();
|
||||
// } else
|
||||
// setTab('settings')
|
||||
// })
|
||||
const Sidebar = () => {
|
||||
// state should come from sidebarStateService
|
||||
const { sidebarStateService } = useServices()
|
||||
const [sidebarState, setSideBarState] = useState<VoidSidebarState>(sidebarStateService.state)
|
||||
const { isHistoryOpen, currentTab: tab } = sidebarState
|
||||
useEffect(() => { sidebarStateService.onDidChangeState(() => setSideBarState(sidebarStateService.state)) }, [sidebarStateService])
|
||||
|
||||
return <>
|
||||
<div className={`flex flex-col h-screen w-full`}>
|
||||
|
|
@ -48,20 +20,19 @@ const Sidebar = ({ accessor }: { accessor: ServicesAccessor }) => {
|
|||
<span onClick={() => {
|
||||
const tabs = ['chat', 'settings', 'threadSelector']
|
||||
const index = tabs.indexOf(tab)
|
||||
setTab(tabs[(index + 1) % tabs.length] as any)
|
||||
sidebarStateService.setState({ currentTab: tabs[(index + 1) % tabs.length] as any })
|
||||
}}>clickme {tab}</span>
|
||||
|
||||
<div className={`mb-2 h-[30vh] ${tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
hi
|
||||
<div className={`mb-2 h-[30vh] ${isHistoryOpen ? '' : 'hidden'}`}>
|
||||
{/* <SidebarThreadSelector onClose={() => setTab('chat')} /> */}
|
||||
</div>
|
||||
|
||||
<div className={`${tab !== 'chat' && tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
<div className={`${tab === 'chat' ? '' : 'hidden'}`}>
|
||||
{/* <SidebarChat chatInputRef={chatInputRef} /> */}
|
||||
</div>
|
||||
|
||||
<div className={`${tab !== 'settings' ? 'hidden' : ''}`}>
|
||||
{/* <SidebarSettings /> */}
|
||||
<div className={`${tab === 'settings' ? '' : 'hidden'}`}>
|
||||
<SidebarSettings />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
// import React, { FormEvent, useCallback, useRef, useState } from "react";
|
||||
|
||||
|
||||
// sidebarStateService.onDidFocusChat(() => {})
|
||||
// sidebarStateService.onDidBlurChat(() => {})
|
||||
|
||||
|
||||
|
||||
// import MarkdownRender from "../../sidebar/markdown/!MarkdownRender";
|
||||
// import BlockCode from "../../sidebar/markdown/!BlockCode";
|
||||
// import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation';
|
||||
|
|
|
|||
|
|
@ -1,105 +1,119 @@
|
|||
// import React, { useState } from "react";
|
||||
// import { configFields, useVoidConfig, VoidConfigField } from "../util/contextForConfig";
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useServices } from '../util/contextForServices.js';
|
||||
import { IVoidConfigStateService, nonDefaultConfigFields, PartialVoidConfig, VoidConfig, VoidConfigField, VoidConfigInfo, SetFieldFnType, ConfigState } from '../../../browser/registerConfig.js';
|
||||
|
||||
|
||||
// const SettingOfFieldAndParam = ({ field, param }: { field: VoidConfigField, param: string }) => {
|
||||
// const { voidConfig, partialVoidConfig, voidConfigInfo, setConfigParam } = useVoidConfig()
|
||||
// const { enumArr, defaultVal, description } = voidConfigInfo[field][param]
|
||||
// const val = partialVoidConfig[field]?.[param] ?? defaultVal // current value of this item
|
||||
const SettingOfFieldAndParam = ({ field, param, configState, configStateService }:
|
||||
{ field: VoidConfigField; param: string; configState: NonNullable<ConfigState>; configStateService: IVoidConfigStateService }) => {
|
||||
|
||||
// const updateState = (newValue: string) => { setConfigParam(field, param, newValue) }
|
||||
|
||||
// const resetButton = <button
|
||||
// disabled={val === defaultVal}
|
||||
// title={val === defaultVal ? 'This is the default value.' : `Revert value to '${defaultVal}'?`}
|
||||
// className='group btn btn-sm disabled:opacity-75 disabled:cursor-default'
|
||||
// onClick={() => updateState(defaultVal)}
|
||||
// >
|
||||
// <svg
|
||||
// className='size-5 group-disabled:stroke-current group-disabled:fill-current group-hover:stroke-red-600 group-hover:fill-red-600 duration-200'
|
||||
// fill="currentColor" strokeWidth="0" viewBox="0 0 16 16" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path fillRule="evenodd" clipRule="evenodd" d="M3.5 2v3.5L4 6h3.5V5H4.979l.941-.941a3.552 3.552 0 1 1 5.023 5.023L5.746 14.28l.72.72 5.198-5.198A4.57 4.57 0 0 0 5.2 3.339l-.7.7V2h-1z"></path>
|
||||
// </svg>
|
||||
// </button>
|
||||
|
||||
// const inputElement = enumArr === undefined ?
|
||||
// // string
|
||||
// (<input
|
||||
// className='input p-1 w-full'
|
||||
// type="text"
|
||||
// value={val}
|
||||
// onChange={(e) => updateState(e.target.value)}
|
||||
// />)
|
||||
// :
|
||||
// // enum
|
||||
// (<select
|
||||
// className='dropdown p-1 w-full'
|
||||
// value={val}
|
||||
// onChange={(e) => updateState(e.target.value)}
|
||||
// >
|
||||
// {enumArr.map((option) => (
|
||||
// <option key={option} value={option}>
|
||||
// {option}
|
||||
// </option>
|
||||
// ))}
|
||||
// </select>)
|
||||
|
||||
// return <div>
|
||||
// <label className='hidden'>{param}</label>
|
||||
// <span>{description}</span>
|
||||
// <div className='flex items-center'>
|
||||
// {inputElement}
|
||||
// {resetButton}
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
|
||||
// export const SidebarSettings = () => {
|
||||
|
||||
// const { voidConfig, voidConfigInfo } = useVoidConfig()
|
||||
|
||||
// const current_field = voidConfig.default['whichApi'] as VoidConfigField
|
||||
const { partialVoidConfig } = configState
|
||||
|
||||
|
||||
// return (
|
||||
// <div className='space-y-4 py-2 overflow-y-auto'>
|
||||
const { enumArr, defaultVal, description } = configStateService.voidConfigInfo[field][param]
|
||||
const val = partialVoidConfig[field]?.[param] ?? defaultVal // current value of this item
|
||||
|
||||
// {/* choose the field */}
|
||||
// <div className='outline-vscode-input-bg'>
|
||||
// <SettingOfFieldAndParam
|
||||
// field='default'
|
||||
// param='whichApi'
|
||||
// />
|
||||
// <SettingOfFieldAndParam
|
||||
// field='default'
|
||||
// param='maxTokens'
|
||||
// />
|
||||
// </div>
|
||||
const updateState = (newValue: string) => { configStateService.setField(field, param, newValue) }
|
||||
|
||||
// <hr />
|
||||
const resetButton = <button
|
||||
disabled={val === defaultVal}
|
||||
title={val === defaultVal ? 'This is the default value.' : `Revert value to '${defaultVal}'?`}
|
||||
className='group btn btn-sm disabled:opacity-75 disabled:cursor-default'
|
||||
onClick={() => updateState(defaultVal)}
|
||||
>
|
||||
<svg
|
||||
className='size-5 group-disabled:stroke-current group-disabled:fill-current group-hover:stroke-red-600 group-hover:fill-red-600 duration-200'
|
||||
fill='currentColor' strokeWidth='0' viewBox='0 0 16 16' height='200px' width='200px' xmlns='http://www.w3.org/2000/svg'><path fillRule='evenodd' clipRule='evenodd' d='M3.5 2v3.5L4 6h3.5V5H4.979l.941-.941a3.552 3.552 0 1 1 5.023 5.023L5.746 14.28l.72.72 5.198-5.198A4.57 4.57 0 0 0 5.2 3.339l-.7.7V2h-1z'></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
// {/* render all fields, but hide the ones not visible for fast tab switching */}
|
||||
// {configFields.map(field => {
|
||||
// return <div
|
||||
// key={field}
|
||||
// className={`flex flex-col gap-y-2 ${field !== current_field ? 'hidden' : ''}`}
|
||||
// >
|
||||
// {Object.keys(voidConfigInfo[field]).map((param) => (
|
||||
// <SettingOfFieldAndParam
|
||||
// key={param}
|
||||
// field={field}
|
||||
// param={param}
|
||||
// />
|
||||
// ))}
|
||||
// </div>
|
||||
// })}
|
||||
const inputElement = enumArr === undefined ?
|
||||
// string
|
||||
(<input
|
||||
className='input p-1 w-full'
|
||||
type='text'
|
||||
value={val}
|
||||
onChange={(e) => updateState(e.target.value)}
|
||||
/>)
|
||||
:
|
||||
// enum
|
||||
(<select
|
||||
className='dropdown p-1 w-full'
|
||||
value={val}
|
||||
onChange={(e) => updateState(e.target.value)}
|
||||
>
|
||||
{enumArr.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>)
|
||||
|
||||
// {/* Remove this after 10/21/24, this is just to give developers a heads up about the recent change */}
|
||||
// <div className='pt-20'>
|
||||
// {`We recently updated Settings. To copy your old Void settings over, press Ctrl+Shift+P, `}
|
||||
// {`type 'Open User Settings (JSON)',`}
|
||||
// {` and look for 'void.'. `}
|
||||
// </div>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
return <div>
|
||||
<label className='hidden'>{param}</label>
|
||||
<span>{description}</span>
|
||||
<div className='flex items-center'>
|
||||
{inputElement}
|
||||
{resetButton}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
export const SidebarSettings = () => {
|
||||
// track the config state using React state so visual updates happen
|
||||
const { configStateService } = useServices()
|
||||
const [configState, setConfigState] = useState<ConfigState>(configStateService.state)
|
||||
const { voidConfig } = configState
|
||||
useEffect(() => { configStateService.onDidChangeState(() => setConfigState(configStateService.state)) }, [configStateService])
|
||||
|
||||
const current_field = voidConfig.default['whichApi'] as VoidConfigField
|
||||
|
||||
return (
|
||||
<div className='space-y-4 py-2 overflow-y-auto'>
|
||||
|
||||
{/* choose the field */}
|
||||
<div className='outline-vscode-input-bg'>
|
||||
<SettingOfFieldAndParam
|
||||
configState={configState}
|
||||
configStateService={configStateService}
|
||||
field='default'
|
||||
param='whichApi'
|
||||
/>
|
||||
<SettingOfFieldAndParam
|
||||
configState={configState}
|
||||
configStateService={configStateService}
|
||||
field='default'
|
||||
param='maxTokens'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
{/* render all fields, but hide the ones not visible for fast tab switching */}
|
||||
{nonDefaultConfigFields.map(field => {
|
||||
return <div
|
||||
key={field}
|
||||
className={`flex flex-col gap-y-2 ${field !== current_field ? 'hidden' : ''}`}
|
||||
>
|
||||
{Object.keys(configStateService.voidConfigInfo[field]).map((param) => (
|
||||
<SettingOfFieldAndParam
|
||||
key={param}
|
||||
configState={configState}
|
||||
configStateService={configStateService}
|
||||
field={field}
|
||||
param={param}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
})}
|
||||
|
||||
{/* Remove this after 10/21/24, this is just to give developers a heads up about the recent change */}
|
||||
<div className='pt-20'>
|
||||
{`We recently updated Settings. To copy your old Void settings over, press Ctrl+Shift+P, `}
|
||||
{`type 'Open User Settings (JSON)',`}
|
||||
{` and look for 'void.'. `}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
import React, { createContext, useContext } from 'react'
|
||||
import { ReactServicesType } from '../../registerSidebar.js';
|
||||
|
||||
const AccessorContext = createContext<ReactServicesType | undefined>(undefined)
|
||||
|
||||
export const AccessorProvider = ({ children, services }: { children: React.ReactNode; services: ReactServicesType }) => {
|
||||
return <AccessorContext.Provider value={services}>
|
||||
{children}
|
||||
</AccessorContext.Provider>
|
||||
}
|
||||
|
||||
export const useServices = (): ReactServicesType => {
|
||||
const context = useContext(AccessorContext)
|
||||
if (context === undefined) {
|
||||
throw new Error('useAccessor must be used within an AccessorProvider')
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,43 +1,18 @@
|
|||
import React from 'react';
|
||||
import * as ReactDOM from 'react-dom/client'
|
||||
import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation';
|
||||
// import { initPosthog, identifyUser } from './posthog';
|
||||
|
||||
// const ListenersAndTracking = () => {
|
||||
// // initialize posthog
|
||||
// useEffect(() => {
|
||||
// initPosthog()
|
||||
// }, [])
|
||||
|
||||
// // // when we get the deviceid, identify the user
|
||||
// // useEffect(() => {
|
||||
// // getVSCodeAPI().postMessage({ type: 'getDeviceId' });
|
||||
// // awaitVSCodeResponse('deviceId').then((m => {
|
||||
// // identifyUser(m.deviceId)
|
||||
// // }))
|
||||
// // }, [])
|
||||
|
||||
// // // Receive messages from the VSCode extension
|
||||
// // useEffect(() => {
|
||||
// // const listener = (event: MessageEvent) => {
|
||||
// // const m = event.data as MessageToSidebar;
|
||||
// // onMessageFromVSCode(m)
|
||||
// // }
|
||||
// // window.addEventListener('message', listener);
|
||||
// // return () => window.removeEventListener('message', listener)
|
||||
// // }, [])
|
||||
|
||||
// return null
|
||||
// }
|
||||
import { AccessorProvider } from './contextForServices';
|
||||
import { ReactServicesType } from '../../registerSidebar';
|
||||
|
||||
|
||||
|
||||
|
||||
export const mountFnGenerator = (Component: React.FC<{ accessor: ServicesAccessor }>) => (rootElement: HTMLElement, accessor: ServicesAccessor) => {
|
||||
export const mountFnGenerator = (Component: React.FC) => (rootElement: HTMLElement, services: ReactServicesType) => {
|
||||
if (typeof document === 'undefined') {
|
||||
console.error('index.tsx error: document was undefined')
|
||||
return
|
||||
}
|
||||
const root = ReactDOM.createRoot(rootElement)
|
||||
root.render(<Component accessor={accessor} />);
|
||||
root.render(
|
||||
<AccessorProvider services={services}>
|
||||
<Component />
|
||||
</AccessorProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ export const nonDefaultConfigFields = [
|
|||
const voidConfigInfo: Record<
|
||||
typeof nonDefaultConfigFields[number] | 'default', {
|
||||
[prop: string]: {
|
||||
description: string,
|
||||
enumArr?: readonly string[] | undefined,
|
||||
defaultVal: string,
|
||||
},
|
||||
description: string;
|
||||
enumArr?: readonly string[] | undefined;
|
||||
defaultVal: string;
|
||||
};
|
||||
}
|
||||
> = {
|
||||
default: {
|
||||
|
|
@ -186,7 +186,7 @@ const voidConfigInfo: Record<
|
|||
|
||||
|
||||
// this is the type that comes with metadata like desc, default val, etc
|
||||
type VoidConfigInfo = typeof voidConfigInfo
|
||||
export type VoidConfigInfo = typeof voidConfigInfo
|
||||
export type VoidConfigField = keyof typeof voidConfigInfo // typeof configFields[number]
|
||||
|
||||
// this is the type that specifies the user's actual config
|
||||
|
|
@ -203,27 +203,48 @@ export type VoidConfig = {
|
|||
}
|
||||
|
||||
|
||||
const VOID_CONFIG_KEY = 'void.partialVoidConfig'
|
||||
|
||||
type setFieldType = <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => Promise<void>;
|
||||
|
||||
export interface IVoidSettingsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
onDidChange: Event<void>;
|
||||
getPartialVoidConfig(): Promise<PartialVoidConfig>;
|
||||
getVoidConfig(): Promise<VoidConfig>;
|
||||
setField: setFieldType;
|
||||
const getVoidConfig = (partialVoidConfig: PartialVoidConfig): VoidConfig => {
|
||||
const config = {} as PartialVoidConfig
|
||||
for (const field of [...nonDefaultConfigFields, 'default'] as const) {
|
||||
config[field] = {}
|
||||
for (const prop in voidConfigInfo[field]) {
|
||||
config[field][prop] = partialVoidConfig[field]?.[prop]?.trim() || voidConfigInfo[field][prop].defaultVal
|
||||
}
|
||||
}
|
||||
return config as VoidConfig
|
||||
}
|
||||
|
||||
export const IVoidSettingsService = createDecorator<IVoidSettingsService>('voidSettingsService');
|
||||
class VoidSettingsService extends Disposable implements IVoidSettingsService {
|
||||
|
||||
const VOID_CONFIG_KEY = 'void.partialVoidConfig'
|
||||
|
||||
export type SetFieldFnType = <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => Promise<void>;
|
||||
|
||||
export type ConfigState = {
|
||||
partialVoidConfig: PartialVoidConfig;
|
||||
voidConfig: VoidConfig;
|
||||
}
|
||||
|
||||
export interface IVoidConfigStateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
readonly state: ConfigState;
|
||||
readonly voidConfigInfo: VoidConfigInfo;
|
||||
onDidChangeState: Event<void>;
|
||||
setField: SetFieldFnType;
|
||||
}
|
||||
|
||||
export const IVoidConfigStateService = createDecorator<IVoidConfigStateService>('VoidConfigStateService');
|
||||
class VoidConfigStateService extends Disposable implements IVoidConfigStateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChange = new Emitter<void>();
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
|
||||
|
||||
state: ConfigState;
|
||||
|
||||
voidConfigInfo: VoidConfigInfo = voidConfigInfo;
|
||||
|
||||
|
||||
async getPartialVoidConfig(): Promise<PartialVoidConfig> {
|
||||
private async _readPartialVoidConfig(): Promise<PartialVoidConfig> {
|
||||
const encryptedPartialConfig = this._storageService.get(VOID_CONFIG_KEY, StorageScope.APPLICATION)
|
||||
|
||||
if (!encryptedPartialConfig)
|
||||
|
|
@ -234,28 +255,15 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
|
|||
}
|
||||
|
||||
|
||||
async getVoidConfig(): Promise<VoidConfig> {
|
||||
const partialVoidConfig = await this.getPartialVoidConfig()
|
||||
const config = {} as PartialVoidConfig
|
||||
for (let field of [...nonDefaultConfigFields, 'default'] as const) {
|
||||
config[field] = {}
|
||||
for (let prop in voidConfigInfo[field]) {
|
||||
config[field][prop] = partialVoidConfig[field]?.[prop]?.trim() || voidConfigInfo[field][prop].defaultVal
|
||||
}
|
||||
}
|
||||
return config as VoidConfig
|
||||
}
|
||||
|
||||
|
||||
private async storePartialVoidConfig(partialVoidConfig: PartialVoidConfig) {
|
||||
private async _storePartialVoidConfig(partialVoidConfig: PartialVoidConfig) {
|
||||
const encryptedPartialConfigStr = await this._encryptionService.encrypt(JSON.stringify(partialVoidConfig))
|
||||
this._storageService.store(VOID_CONFIG_KEY, encryptedPartialConfigStr, StorageScope.APPLICATION, StorageTarget.USER)
|
||||
}
|
||||
|
||||
|
||||
// Set field on PartialVoidConfig
|
||||
setField: setFieldType = async <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => {
|
||||
const partialVoidConfig = await this.getPartialVoidConfig()
|
||||
setField: SetFieldFnType = async <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => {
|
||||
const partialVoidConfig = await this._readPartialVoidConfig()
|
||||
|
||||
const newPartialConfig: PartialVoidConfig = {
|
||||
...partialVoidConfig,
|
||||
|
|
@ -264,19 +272,41 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
|
|||
[param]: newVal
|
||||
}
|
||||
}
|
||||
await this.storePartialVoidConfig(newPartialConfig)
|
||||
this._onDidChange.fire()
|
||||
await this._storePartialVoidConfig(newPartialConfig)
|
||||
this._setState(newPartialConfig)
|
||||
}
|
||||
|
||||
// internal function to update state, should be called every time state changes
|
||||
private async _setState(partialVoidConfig: PartialVoidConfig) {
|
||||
this.state = {
|
||||
partialVoidConfig: partialVoidConfig,
|
||||
voidConfig: getVoidConfig(partialVoidConfig),
|
||||
}
|
||||
this._onDidChangeState.fire()
|
||||
}
|
||||
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IEncryptionService private readonly _encryptionService: IEncryptionService,
|
||||
// @ISecretStorageService private readonly _secretStorageService: ISecretStorageService, // could have used this, but it's clearer the way it is (+ slightly different eg StorageTarget.USER)
|
||||
// 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, just treat partialVoidConfig like it's empty
|
||||
this.state = {
|
||||
partialVoidConfig: {},
|
||||
voidConfig: getVoidConfig({}),
|
||||
}
|
||||
|
||||
// read and update the actual state immediately
|
||||
this._readPartialVoidConfig().then(partialVoidConfig => {
|
||||
this._setState(partialVoidConfig)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IVoidSettingsService, VoidSettingsService, InstantiationType.Eager);
|
||||
registerSingleton(IVoidConfigStateService, VoidConfigStateService, InstantiationType.Eager);
|
||||
|
|
@ -36,9 +36,10 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi
|
|||
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
|
||||
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
|
||||
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
|
||||
// import { IVoidSettingsService } from './registerSettings.js';
|
||||
// import { IVoidConfigService } from './registerSettings.js';
|
||||
// import { IEditorService } from '../../../services/editor/common/editorService.js';
|
||||
import mountFn from './react/out/Sidebar.js';
|
||||
import { IVoidConfigStateService } from './registerConfig.js';
|
||||
// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
||||
|
||||
// const mountFn = (...params: any) => { }
|
||||
|
|
@ -47,11 +48,16 @@ import mountFn from './react/out/Sidebar.js';
|
|||
// compare against search.contribution.ts and https://app.greptile.com/chat/w1nsmt3lauwzculipycpn?repo=github%3Amain%3Amicrosoft%2Fvscode
|
||||
// and debug.contribution.ts, scm.contribution.ts (source control)
|
||||
|
||||
type VoidSidebarState = {
|
||||
export type VoidSidebarState = {
|
||||
isHistoryOpen: boolean;
|
||||
currentTab: 'chat' | 'settings';
|
||||
}
|
||||
|
||||
export type ReactServicesType = {
|
||||
sidebarStateService: IVoidSidebarStateService;
|
||||
configStateService: IVoidConfigStateService;
|
||||
threadHistoryService: IThreadHistoryService;
|
||||
}
|
||||
|
||||
// ---------- Define viewpane ----------
|
||||
|
||||
|
|
@ -89,10 +95,13 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
dom.append(parent, root);
|
||||
|
||||
// gets set immediately
|
||||
let accessor_: ServicesAccessor = null as unknown as ServicesAccessor
|
||||
this.instantiationService.invokeFunction(accessor => { accessor_ = accessor });
|
||||
|
||||
mountFn(root, accessor_);
|
||||
this.instantiationService.invokeFunction(accessor => {
|
||||
mountFn(root, {
|
||||
configStateService: accessor.get(IVoidConfigStateService),
|
||||
sidebarStateService: accessor.get(IVoidSidebarStateService),
|
||||
threadHistoryService: accessor.get(IThreadHistoryService),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -184,37 +193,53 @@ viewsRegistry.registerViews([{
|
|||
|
||||
// ---------- Register service that manages sidebar's state ----------
|
||||
|
||||
interface IVoidSidebarStateService {
|
||||
export interface IVoidSidebarStateService {
|
||||
readonly _serviceBrand: undefined;
|
||||
setState(newState: Partial<VoidSidebarState>): void;
|
||||
state: VoidSidebarState;
|
||||
focusChat(): void;
|
||||
blurChat(): void;
|
||||
|
||||
onDidChange: Event<void>;
|
||||
onFocusChat: Event<void>;
|
||||
onBlurChat: Event<void>;
|
||||
state: VoidSidebarState;
|
||||
setState(newState: Partial<VoidSidebarState>): void;
|
||||
onDidChangeState: Event<void>;
|
||||
|
||||
onDidFocusChat: Event<void>;
|
||||
onDidBlurChat: Event<void>;
|
||||
fireFocusChat(): void;
|
||||
fireBlurChat(): void;
|
||||
}
|
||||
|
||||
|
||||
const IVoidSidebarStateService = createDecorator<IVoidSidebarStateService>('voidSidebarStateService');
|
||||
export const IVoidSidebarStateService = createDecorator<IVoidSidebarStateService>('voidSidebarStateService');
|
||||
class VoidSidebarStateService extends Disposable implements IVoidSidebarStateService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChange = new Emitter<void>();
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
|
||||
|
||||
private readonly _onFocusChat = new Emitter<void>();
|
||||
readonly onFocusChat: Event<void> = this._onFocusChat.event;
|
||||
readonly onDidFocusChat: Event<void> = this._onFocusChat.event;
|
||||
|
||||
private readonly _onBlurChat = new Emitter<void>();
|
||||
readonly onBlurChat: Event<void> = this._onBlurChat.event;
|
||||
readonly onDidBlurChat: Event<void> = this._onBlurChat.event;
|
||||
|
||||
|
||||
// state
|
||||
state: VoidSidebarState = {
|
||||
isHistoryOpen: false,
|
||||
currentTab: 'chat',
|
||||
state: VoidSidebarState
|
||||
|
||||
|
||||
setState(newState: Partial<VoidSidebarState>) {
|
||||
// make sure view is open if the tab changes
|
||||
if ('currentTab' in newState)
|
||||
this._viewsService.openView(SIDEBAR_VIEW_ID);
|
||||
|
||||
this.state = { ...this.state, ...newState }
|
||||
this._onDidChangeState.fire()
|
||||
}
|
||||
|
||||
fireFocusChat() {
|
||||
this._onFocusChat.fire()
|
||||
}
|
||||
|
||||
fireBlurChat() {
|
||||
this._onBlurChat.fire()
|
||||
}
|
||||
|
||||
constructor(
|
||||
|
|
@ -223,23 +248,13 @@ class VoidSidebarStateService extends Disposable implements IVoidSidebarStateSer
|
|||
super()
|
||||
// auto open the view on mount (if it bothers you this is here, this is technically just initializing the state of the view)
|
||||
this._viewsService.openView(SIDEBAR_VIEW_ID);
|
||||
}
|
||||
|
||||
setState(newState: Partial<VoidSidebarState>) {
|
||||
// make sure view is open if the tab changes
|
||||
if ('currentTab' in newState)
|
||||
this._viewsService.openView(SIDEBAR_VIEW_ID);
|
||||
// initial state
|
||||
this.state = {
|
||||
isHistoryOpen: false,
|
||||
currentTab: 'chat',
|
||||
}
|
||||
|
||||
this.state = { ...this.state, ...newState }
|
||||
this._onDidChange.fire()
|
||||
}
|
||||
|
||||
focusChat() {
|
||||
this._onFocusChat.fire()
|
||||
}
|
||||
|
||||
blurChat() {
|
||||
this._onBlurChat.fire()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -258,7 +273,7 @@ registerAction2(class extends Action2 {
|
|||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: false, currentTab: 'chat' })
|
||||
stateService.focusChat()
|
||||
stateService.fireFocusChat()
|
||||
|
||||
// const selection = accessor.get(IEditorService).activeTextEditorControl?.getSelection()
|
||||
|
||||
|
|
@ -292,7 +307,7 @@ registerAction2(class extends Action2 {
|
|||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: false, currentTab: 'chat' })
|
||||
stateService.focusChat()
|
||||
stateService.fireFocusChat()
|
||||
|
||||
const historyService = accessor.get(IThreadHistoryService)
|
||||
historyService.startNewThread()
|
||||
|
|
@ -312,7 +327,7 @@ registerAction2(class extends Action2 {
|
|||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: !stateService.state.isHistoryOpen })
|
||||
stateService.blurChat()
|
||||
stateService.fireBlurChat()
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -329,6 +344,6 @@ registerAction2(class extends Action2 {
|
|||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: false, currentTab: 'settings' })
|
||||
stateService.blurChat()
|
||||
stateService.fireBlurChat()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// register Settings
|
||||
import './registerSettings.js'
|
||||
import './registerConfig.js'
|
||||
|
||||
// register Sidebar chat
|
||||
import './registerSidebar.js'
|
||||
|
|
|
|||
Loading…
Reference in a new issue