react updates

This commit is contained in:
Andrew Pareles 2024-12-16 17:43:07 -08:00
parent dfdf29fb36
commit c485d2c478
9 changed files with 164 additions and 148 deletions

View file

@ -21,8 +21,9 @@ import { ErrorDisplay } from './ErrorDisplay.js';
import { OnError, ServiceSendLLMMessageParams } from '../../../../../../../platform/void/common/llmMessageTypes.js';
import { getCmdKey } from '../../../helpers/getCmdKey.js'
import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
import { VoidInputBox } from './inputs.js';
import { VoidInputBox, VoidScrollableElt } from '../util/inputs.js';
import { ModelDropdown } from '../void-settings-tsx/ModelDropdown.js';
import { ScrollbarVisibility } from '../../../../../../../base/common/scrollable.js';
const IconX = ({ size, className = '' }: { size: number, className?: string }) => {
@ -121,6 +122,7 @@ const ScrollToBottomContainer = ({ children, className, style }: { children: Rea
return (
<div
// options={{ vertical: ScrollbarVisibility.Auto, horizontal: ScrollbarVisibility.Auto }}
ref={divRef}
onScroll={onScroll}
className={className}

View file

@ -4,20 +4,23 @@
*--------------------------------------------------------------------------------------------*/
import React, { useCallback, useEffect, useRef } from 'react';
import { useService } from '../util/services.js';
import { useService } from './services.js';
import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
import { defaultInputBoxStyles, defaultSelectBoxStyles } from '../../../../../../../platform/theme/browser/defaultStyles.js';
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js';
import { IDisposable } from '../../../../../../../base/common/lifecycle.js';
import { DomScrollableElement } from '../../../../../../../base/browser/ui/scrollbar/scrollableElement.js';
import { ScrollableElementCreationOptions } from '../../../../../../../base/browser/ui/scrollbar/scrollableElementOptions.js';
export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, propsFn, dispose, onCreateInstance }
export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, propsFn, dispose, onCreateInstance, children }
: {
ctor: { new(...params: CtorParams): Instance },
propsFn: (container: HTMLDivElement) => CtorParams,
onCreateInstance: (instance: Instance) => IDisposable[],
dispose: (instance: Instance) => void,
children?: React.ReactNode,
}
) => {
const containerRef = useRef<HTMLDivElement | null>(null);
@ -31,7 +34,7 @@ export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, prop
}
}, [ctor, propsFn, dispose, onCreateInstance, containerRef])
return <div ref={containerRef} className='w-full' />
return <div ref={containerRef} className='w-full'>{children}</div>
}
@ -142,6 +145,21 @@ export const VoidSelectBox = <T,>({ onChangeSelection, onCreateInstance, selectB
};
export const VoidScrollableElt = ({ options, children }: { options: ScrollableElementCreationOptions, children: React.ReactNode }) => {
return <WidgetComponent
ctor={DomScrollableElement}
propsFn={useCallback((container) => {
return [container, options] as const;
}, [options])}
onCreateInstance={useCallback(() => { return [] }, [])}
dispose={useCallback((instance: DomScrollableElement) => {
console.log('calling dispose!!!!')
// instance.dispose();
// instance.getDomNode().remove()
}, [])}
>abcdefg</WidgetComponent>
}
// export const VoidSelectBox = <T,>({ onChangeSelection, initVal, selectBoxRef, options }: {
// initVal: T;

View file

@ -136,3 +136,6 @@ export const useRefreshModelState = () => {
}, [ss])
return s
}

View file

@ -6,7 +6,7 @@
import { useCallback, useEffect, useRef, useState } from 'react'
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 { VoidSelectBox } from '../util/inputs.js'
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'

View file

@ -1,58 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
import { ModelInfo, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidSettingsTypes.js'
import { useRefreshModelState, useService, useSettingsState } from '../util/services.js'
const Refreshables = () => {
const settingsState = useSettingsState()
const refreshModelState = useRefreshModelState()
const refreshModelService = useService('refreshModelService')
if (settingsState.settingsOfProvider.ollama.enabled !== 'true')
return null
return <>
<button onClick={() => refreshModelService.refreshOllamaModels()}>refresh Ollama built-in models</button>
{refreshModelState === 'loading' ? 'loading...' : 'good!'}
</>
}
export const ModelMenu = () => {
const settingsStateService = useService('settingsStateService')
const settingsState = useSettingsState()
// a dump of all the enabled providers' models
const models: (ModelInfo & { providerName: ProviderName })[] = []
for (let providerName of providerNames) {
const providerSettings = settingsState.settingsOfProvider[providerName]
if (providerSettings.enabled !== 'true') continue
models.push(...providerSettings.models.map(model => ({ ...model, providerName })))
}
return <>
{models.map(m => {
const { isHidden, isDefault, modelName, providerName } = m
return <div key={`${modelName}${providerName}`} className='flex items-center justify-between gap-4'>
<span>{modelName} {isDefault ? '' : '(custom)'}</span>
<span>{providerName}</span>
<span onClick={() => { settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? 'hidden' : '✅'}</span>
</div>
})}
<Refreshables />
</>
}

View file

@ -1,74 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPL 3.0 License.
*--------------------------------------------------------------------------------------------*/
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { titleOfProviderName, displayInfoOfSettingName, ProviderName, providerNames, featureNames, SettingsOfProvider, SettingName, defaultSettingsOfProvider } from '../../../../../../../platform/void/common/voidSettingsTypes.js'
import { VoidInputBox } from '../sidebar-tsx/inputs.js'
import { useSettingsState, useService } from '../util/services.js'
import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'
const Setting = ({ providerName, settingName }: { providerName: ProviderName, settingName: SettingName }) => {
const { title, type, placeholder } = displayInfoOfSettingName(providerName, settingName)
const voidSettingsService = useService('settingsStateService')
let weChangedTextRef = false
return <><ErrorBoundary>
<label>{title}</label>
<VoidInputBox
placeholder={placeholder}
onChangeText={useCallback((newVal) => {
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 = voidSettingsService.state.settingsOfProvider[providerName];
const stateVal = settingsAtProvider[settingName as keyof typeof settingsAtProvider]
// console.log('SYNCING TO', providerName, settingName, stateVal)
weChangedTextRef = true
instance.value = stateVal as string
weChangedTextRef = false
}
syncInstance()
const disposable = voidSettingsService.onDidChangeState(syncInstance)
return [disposable]
}, [voidSettingsService, providerName, settingName])}
multiline={false}
/>
</ErrorBoundary></>
}
const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => {
const voidSettingsState = useSettingsState()
const { models, ...others } = voidSettingsState.settingsOfProvider[providerName]
return <>
<h1 className='text-xl'>{titleOfProviderName(providerName)}</h1>
{/* settings besides models (e.g. api key) */}
{Object.keys(others).map((sName, i) => {
const settingName = sName as keyof typeof others
return <Setting key={settingName} providerName={providerName} settingName={settingName} />
})}
</>
}
export const VoidProviderSettings = () => {
return <>
{providerNames.map(providerName =>
<SettingsForProvider key={providerName} providerName={providerName} />
)}
</>
}

View file

@ -1,17 +1,142 @@
import React, { useCallback } from 'react'
import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'
import { ProviderName, SettingName, displayInfoOfSettingName, titleOfProviderName, providerNames, ModelInfo } from '../../../../../../../platform/void/common/voidSettingsTypes.js'
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'
import { ModelMenu } from './ModelSettings.js'
import { VoidProviderSettings } from './ProviderSettings.js'
import { VoidInputBox } from '../util/inputs.js'
import { useRefreshModelState, useService, useSettingsState } from '../util/services.js'
// models
const RefreshableModels = () => {
const settingsState = useSettingsState()
const refreshModelState = useRefreshModelState()
const refreshModelService = useService('refreshModelService')
if (settingsState.settingsOfProvider.ollama.enabled !== 'true')
return null
return <>
<button onClick={() => refreshModelService.refreshOllamaModels()}>refresh Ollama built-in models</button>
{refreshModelState === 'loading' ? 'loading...' : 'good!'}
</>
}
export const ModelMenu = () => {
const settingsStateService = useService('settingsStateService')
const settingsState = useSettingsState()
// a dump of all the enabled providers' models
const modelDump: (ModelInfo & { providerName: ProviderName })[] = []
for (let providerName of providerNames) {
const providerSettings = settingsState.settingsOfProvider[providerName]
if (providerSettings.enabled !== 'true') continue
modelDump.push(...providerSettings.models.map(model => ({ ...model, providerName })))
}
return <>
{modelDump.map(m => {
const { isHidden, isDefault, modelName, providerName } = m
return <div key={`${modelName}${providerName}`} className='flex items-center justify-between gap-4'>
<span>{modelName} {isDefault ? '' : '(custom)'}</span>
<span>{providerName}</span>
<span onClick={() => { settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? 'hidden' : '✅'}</span>
</div>
})}
</>
}
// providers
const ProviderSetting = ({ providerName, settingName }: { providerName: ProviderName, settingName: SettingName }) => {
const { title, type, placeholder } = displayInfoOfSettingName(providerName, settingName)
const voidSettingsService = useService('settingsStateService')
let weChangedTextRef = false
return <><ErrorBoundary>
<label>{title}</label>
<VoidInputBox
placeholder={placeholder}
onChangeText={useCallback((newVal) => {
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 = voidSettingsService.state.settingsOfProvider[providerName];
const stateVal = settingsAtProvider[settingName as keyof typeof settingsAtProvider]
// console.log('SYNCING TO', providerName, settingName, stateVal)
weChangedTextRef = true
instance.value = stateVal as string
weChangedTextRef = false
}
syncInstance()
const disposable = voidSettingsService.onDidChangeState(syncInstance)
return [disposable]
}, [voidSettingsService, providerName, settingName])}
multiline={false}
/>
</ErrorBoundary></>
}
const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => {
const voidSettingsState = useSettingsState()
const { models, ...others } = voidSettingsState.settingsOfProvider[providerName]
return <>
<h1 className='text-xl'>{titleOfProviderName(providerName)}</h1>
{/* settings besides models (e.g. api key) */}
{Object.keys(others).map((sName, i) => {
const settingName = sName as keyof typeof others
return <ProviderSetting key={settingName} providerName={providerName} settingName={settingName} />
})}
</>
}
export const VoidProviderSettings = () => {
return <>
{providerNames.map(providerName =>
<SettingsForProvider key={providerName} providerName={providerName} />
)}
</>
}
// full settings
export const Settings = () => {
return <div className='@@void-scope w-full h-full'>
<ErrorBoundary>
<ModelMenu />
</ErrorBoundary>
return <div className='@@void-scope'>
<div className='w-full h-full'>
<ErrorBoundary>
<VoidProviderSettings />
</ErrorBoundary>
<div className='max-w-3xl mx-auto'>
<ErrorBoundary>
<ModelMenu />
<RefreshableModels />
</ErrorBoundary>
</div>
<ErrorBoundary>
<VoidProviderSettings />
</ErrorBoundary>
</div>
</div>
}

View file

@ -71,7 +71,7 @@ class SidebarViewPane extends ViewPane {
protected override renderBody(parent: HTMLElement): void {
super.renderBody(parent);
parent.style.overflow = 'auto'
// parent.style.overflow = 'auto'
parent.style.userSelect = 'text'
// gets set immediately

View file

@ -73,7 +73,7 @@ class VoidSettingsPane extends EditorPane {
}
protected createEditor(parent: HTMLElement): void {
parent.style.overflow = 'auto'
// parent.style.overflow = 'auto'
parent.style.userSelect = 'text'
// gets set immediately