mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
react updates
This commit is contained in:
parent
dfdf29fb36
commit
c485d2c478
9 changed files with 164 additions and 148 deletions
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -136,3 +136,6 @@ export const useRefreshModelState = () => {
|
|||
}, [ss])
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
</>
|
||||
}
|
||||
|
|
@ -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} />
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue