settings styles

This commit is contained in:
Andrew Pareles 2024-12-17 01:33:33 -08:00
parent 597c04f63b
commit 7752278720
4 changed files with 48 additions and 30 deletions

View file

@ -498,10 +498,10 @@ export const SidebarChat = () => {
{/* bottom row */}
<div
className='flex flex-row justify-between items-end'
className='flex flex-row justify-between items-end gap-1'
>
{/* submit options */}
<div>
<div className='w-[250px]'>
<ModelDropdown featureName='Ctrl+L' />
</div>

View file

@ -5,7 +5,7 @@
import React, { useCallback, useEffect, useRef } from 'react';
import { useService } from '../util/services.js';
import { , InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
import { IInputBoxStyles, 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';
@ -14,13 +14,14 @@ import { ScrollableElementCreationOptions } from '../../../../../../../base/brow
export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, propsFn, dispose, onCreateInstance, children }
export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, propsFn, dispose, onCreateInstance, children, className }
: {
ctor: { new(...params: CtorParams): Instance },
propsFn: (container: HTMLDivElement) => CtorParams,
onCreateInstance: (instance: Instance) => IDisposable[],
dispose: (instance: Instance) => void,
children?: React.ReactNode,
className?: string
}
) => {
const containerRef = useRef<HTMLDivElement | null>(null);
@ -34,7 +35,7 @@ export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, prop
}
}, [ctor, propsFn, dispose, onCreateInstance, containerRef])
return <div ref={containerRef} className='w-full'>{children}</div>
return <div ref={containerRef} className={className === undefined ? `w-full` : className}>{children}</div>
}
@ -105,6 +106,7 @@ export const VoidSelectBox = <T,>({ onChangeSelection, onCreateInstance, selectB
let containerRef = useRef<HTMLDivElement | null>(null);
return <WidgetComponent
className='text-ellipsis whitespace-nowrap pr-6'
ctor={SelectBox}
propsFn={useCallback((container) => {
containerRef.current = container

View file

@ -4,6 +4,7 @@ import { ProviderName, SettingName, displayInfoOfSettingName, titleOfProviderNam
import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'
import { VoidInputBox, VoidSelectBox } from '../util/inputs.js'
import { useIsDark, useRefreshModelState, useService, useSettingsState } from '../util/services.js'
import { X } from 'lucide-react'
@ -18,10 +19,10 @@ const RefreshableModels = () => {
if (!settingsState.settingsOfProvider.ollama.enabled)
return null
return <>
return <div>
<button onClick={() => refreshModelService.refreshOllamaModels()}>refresh Ollama built-in models</button>
{refreshModelState === 'loading' ? 'loading...' : 'good!'}
</>
</div>
}
@ -110,16 +111,16 @@ export const ModelDump = () => {
const settingsState = useSettingsState()
// a dump of all the enabled providers' models
const modelDump: (ModelInfo & { providerName: ProviderName })[] = []
const modelDump: (ModelInfo & { providerName: ProviderName, providerEnabled: boolean })[] = []
for (let providerName of providerNames) {
const providerSettings = settingsState.settingsOfProvider[providerName]
if (!providerSettings.enabled) continue
modelDump.push(...providerSettings.models.map(model => ({ ...model, providerName })))
// if (!providerSettings.enabled) continue
modelDump.push(...providerSettings.models.map(model => ({ ...model, providerName, providerEnabled: providerSettings.enabled })))
}
return <div className=''>
{modelDump.map(m => {
const { isHidden, isDefault, modelName, providerName } = m
const { isHidden, isDefault, modelName, providerName, providerEnabled } = m
return <div key={`${modelName}${providerName}`} className='flex items-center justify-between gap-4 hover:bg-black/10 dark:hover:bg-gray-200/10 py-1 px-3 rounded-sm overflow-hidden cursor-default'>
{/* left part is width:full */}
@ -129,9 +130,9 @@ export const ModelDump = () => {
{/* right part is anything that fits */}
<div className='w-fit flex items-center gap-4'>
<span className='opacity-50 whitespace-nowrap'>{isDefault ? '' : '(custom model)'}</span>
<div onClick={() => { settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? '❌' : '✅'}</div>
<div className='w-10'>
{isDefault ? null : <button onClick={() => { settingsStateService.deleteModel(providerName, modelName) }}>x</button>}
<button disabled={!providerEnabled} onClick={() => { settingsStateService.toggleModelHidden(providerName, modelName) }}>{(!providerEnabled || isHidden) ? '❌' : '✅'}</button>
<div className='w-5 flex items-center justify-center'>
{isDefault ? null : <button onClick={() => { settingsStateService.deleteModel(providerName, modelName) }}><X className='size-4' /></button>}
</div>
</div>
</div>
@ -190,7 +191,7 @@ const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) =
<div className='flex items-center gap-4'>
<h3 className='text-xl'>{titleOfProviderName(providerName)}</h3>
<span onClick={() => { voidSettingsService.setSettingOfProvider(providerName, 'enabled', !enabled) }}>{enabled ? '✅' : '❌'}</span>
<button onClick={() => { voidSettingsService.setSettingOfProvider(providerName, 'enabled', !enabled) }}>{enabled ? '✅' : '❌'}</button>
</div>
{/* settings besides models (e.g. api key) */}
{Object.keys(others).map((sName, i) => {
@ -248,7 +249,7 @@ export const Settings = () => {
<div className='w-full overflow-y-auto'>
<div className={`${tab !== 'models' ? 'hidden' : ''}`}>
<h2 className={`text-3xl`}>Models</h2>
<h2 className={`text-3xl mb-2`}>Models</h2>
<ErrorBoundary>
<ModelDump />
<AddModelButton />
@ -257,7 +258,7 @@ export const Settings = () => {
</div>
<div className={`${tab !== 'providers' ? 'hidden' : ''}`}>
<h2 className={`text-3xl`} onClick={() => { setTab('providers') }}>Providers</h2>
<h2 className={`text-3xl mb-2`} onClick={() => { setTab('providers') }}>Providers</h2>
<ErrorBoundary>
<VoidProviderSettings />
</ErrorBoundary>

View file

@ -27,6 +27,7 @@ import { mountVoidSettings } from './react/out/void-settings-tsx/index.js'
import { getReactServices } from './helpers/reactServicesHelper.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { IDisposable } from '../../../../base/common/lifecycle.js';
import { DomScrollableElement } from '../../../../base/browser/ui/scrollbar/scrollableElement.js';
// refer to preferences.contribution.ts keybindings editor
@ -62,6 +63,8 @@ class VoidSettingsInput extends EditorInput {
class VoidSettingsPane extends EditorPane {
static readonly ID = 'workbench.test.myCustomPane';
private _scrollbar: DomScrollableElement | undefined;
constructor(
group: IEditorGroup,
@ITelemetryService telemetryService: ITelemetryService,
@ -73,31 +76,43 @@ class VoidSettingsPane extends EditorPane {
}
protected createEditor(parent: HTMLElement): void {
// parent.style.overflow = 'auto'
parent.style.userSelect = 'text'
parent.style.height = '100%';
parent.style.width = '100%';
const scrollableContent = document.createElement('div');
scrollableContent.style.height = '100%';
scrollableContent.style.width = '100%';
// gets set immediately
this._scrollbar = this._register(new DomScrollableElement(scrollableContent, {}));
parent.appendChild(this._scrollbar.getDomNode());
this._scrollbar.scanDomNode();
// Mount React into the scrollable content
this.instantiationService.invokeFunction(accessor => {
const services = getReactServices(accessor)
const disposables: IDisposable[] | undefined = mountVoidSettings(parent, services);
disposables?.forEach(d => this._register(d))
})
const services = getReactServices(accessor);
const disposables: IDisposable[] | undefined = mountVoidSettings(scrollableContent, services);
setTimeout(() => { // this is a complete hack and I don't really understand how scrollbar works here
this._scrollbar?.scanDomNode();
}, 1000)
disposables?.forEach(d => this._register(d));
});
}
layout(dimension: Dimension): void {
const container = this.getContainer();
if (!container) return;
if (!this._scrollbar) return;
this._scrollbar.getDomNode().style.height = `${dimension.height}px`;
this._scrollbar.getDomNode().style.width = `${dimension.width}px`;
this._scrollbar.scanDomNode();
container.style.width = `${dimension.width}px`;
container.style.height = `${dimension.height}px`;
}
override get minimumWidth() { return 512 }
override get minimumWidth() { return 700 }
}
// register Settings pane
Registry.as<IEditorPaneRegistry>(EditorExtensions.EditorPane).registerEditorPane(
EditorPaneDescriptor.create(VoidSettingsPane, VoidSettingsPane.ID, nls.localize('VoidSettingsPane', "Void Settings Pane")),