diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index 7c8b99c8..689888cb 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -177,11 +177,6 @@ export type SettingName = UnionOfKeys -type DisplayInfo = { - title: string, - type: string, - placeholder: string, -} export const titleOfProviderName = (providerName: ProviderName) => { if (providerName === 'anthropic') @@ -202,11 +197,14 @@ export const titleOfProviderName = (providerName: ProviderName) => { throw new Error(`descOfProviderName: Unknown provider name: "${providerName}"`) } +type DisplayInfo = { + title: string, + placeholder: string, +} export const displayInfoOfSettingName = (providerName: ProviderName, settingName: SettingName): DisplayInfo => { if (settingName === 'apiKey') { return { title: 'API Key', - type: 'string', placeholder: providerName === 'anthropic' ? 'sk-ant-key...' : // sk-ant-api03-key providerName === 'openAI' ? 'sk-proj-key...' : providerName === 'openRouter' ? 'sk-or-key...' : // sk-or-v1-key @@ -221,7 +219,6 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName title: providerName === 'ollama' ? 'Your Ollama endpoint' : providerName === 'openAICompatible' ? 'baseURL' // (do not include /chat/completions) : '(never)', - type: 'string', placeholder: providerName === 'ollama' ? voidProviderDefaults.ollama.endpoint : providerName === 'openAICompatible' ? 'https://my-website.com/v1' : '(never)', @@ -229,15 +226,13 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName } else if (settingName === 'enabled') { return { - title: 'Enabled?', - type: 'boolean', + title: '(never)', placeholder: '(never)', } } else if (settingName === 'models') { return { - title: 'Available Models', - type: '(never)', + title: '(never)', placeholder: '(never)', } } diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx index 4d177c88..f8f78d00 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx @@ -8,7 +8,7 @@ import { mountFnGenerator } from '../util/mountFnGenerator.js' // import { SidebarSettings } from './SidebarSettings.js'; -import { useSidebarState } from '../util/services.js'; +import { useIsDark, useSidebarState } from '../util/services.js'; // import { SidebarThreadSelector } from './SidebarThreadSelector.js'; // import { SidebarChat } from './SidebarChat.js'; @@ -17,12 +17,12 @@ import { SidebarThreadSelector } from './SidebarThreadSelector.js'; import { SidebarChat } from './SidebarChat.js'; import ErrorBoundary from './ErrorBoundary.js'; -export const Sidebar = () => { +export const Sidebar = ({ className }: { className: string }) => { const sidebarState = useSidebarState() const { isHistoryOpen, currentTab: tab } = sidebarState - // className='@@void-scope' - return
+ const isDark = useIsDark() + return
{/* { diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index e8ab57ed..924f75a6 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -21,9 +21,8 @@ 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, VoidScrollableElt } from '../util/inputs.js'; +import { VoidInputBox } 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 }) => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx index 2097773f..769056ed 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx @@ -3,7 +3,7 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import React, { useCallback, useEffect, useRef } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useService } from './services.js'; import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; import { defaultInputBoxStyles, defaultSelectBoxStyles } from '../../../../../../../platform/theme/browser/defaultStyles.js'; @@ -145,21 +145,32 @@ export const VoidSelectBox = ({ onChangeSelection, onCreateInstance, selectB }; -export const VoidScrollableElt = ({ options, children }: { options: ScrollableElementCreationOptions, children: React.ReactNode }) => { +// export const VoidScrollableElt = ({ options, children }: { options: ScrollableElementCreationOptions, children: React.ReactNode }) => { +// const instanceRef = useRef(null); +// const [childrenPortal, setChildrenPortal] = useState(null) - return { - return [container, options] as const; - }, [options])} - onCreateInstance={useCallback(() => { return [] }, [])} - dispose={useCallback((instance: DomScrollableElement) => { - console.log('calling dispose!!!!') - // instance.dispose(); - // instance.getDomNode().remove() - }, [])} - >abcdefg -} +// return <> +// { +// return [container, options] as const; +// }, [options])} +// onCreateInstance={useCallback((instance: DomScrollableElement) => { +// instanceRef.current = instance; +// setChildrenPortal(createPortal(children, instance.getDomNode())) +// return [] +// }, [setChildrenPortal, children])} +// dispose={useCallback((instance: DomScrollableElement) => { +// console.log('calling dispose!!!!') +// // instance.dispose(); +// // instance.getDomNode().remove() +// }, [])} +// >{children} + +// {childrenPortal} + +// +// } // export const VoidSelectBox = ({ onChangeSelection, initVal, selectBoxRef, options }: { // initVal: T; diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx index b4de1b6f..d67932dd 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx @@ -9,7 +9,7 @@ import { _registerServices } from './services.js'; import { ReactServicesType } from '../../../helpers/reactServicesHelper.js'; -export const mountFnGenerator = (Component: React.FC) => (rootElement: HTMLElement, services: ReactServicesType) => { +export const mountFnGenerator = (Component: (params: any) => React.ReactNode) => (rootElement: HTMLElement, services: ReactServicesType) => { if (typeof document === 'undefined') { console.error('index.tsx error: document was undefined') return @@ -17,8 +17,9 @@ export const mountFnGenerator = (Component: React.FC) => (rootElement: HTMLEleme const disposables = _registerServices(services) + const root = ReactDOM.createRoot(rootElement) - root.render(); + root.render(); // tailwind dark theme indicator return disposables } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx index 0b5b85f4..250363ce 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx @@ -11,6 +11,7 @@ import { IDisposable } from '../../../../../../../base/common/lifecycle.js' import { ReactServicesType } from '../../../helpers/reactServicesHelper.js' import { VoidSidebarState } from '../../../sidebarStateService.js' import { VoidSettingsState } from '../../../../../../../platform/void/common/voidSettingsService.js' +import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js' // normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes @@ -31,7 +32,8 @@ const settingsStateListeners: Set<(s: VoidSettingsState) => void> = new Set() let refreshModelState: RefreshModelState const refreshModelStateListeners: Set<(s: RefreshModelState) => void> = new Set() - +let colorThemeState: ColorScheme +const colorThemeStateListeners: Set<(s: ColorScheme) => void> = new Set() // must call this before you can use any of the hooks below // this should only be called ONCE! this is the only place you don't need to dispose onDidChange. If you use state.onDidChange anywhere else, make sure to dispose it! @@ -48,7 +50,7 @@ export const _registerServices = (services_: ReactServicesType) => { wasCalled = true services = services_ - const { sidebarStateService, settingsStateService, threadsStateService, refreshModelService } = services + const { sidebarStateService, settingsStateService, threadsStateService, refreshModelService, themeService } = services sidebarState = sidebarStateService.state disposables.push( @@ -82,6 +84,14 @@ export const _registerServices = (services_: ReactServicesType) => { }) ) + colorThemeState = themeService.getColorTheme().type + disposables.push( + themeService.onDidColorThemeChange(theme => { + colorThemeState = theme.type + colorThemeStateListeners.forEach(l => l(colorThemeState)) + }) + ) + return disposables } @@ -139,3 +149,18 @@ export const useRefreshModelState = () => { + + +export const useIsDark = () => { + const [s, ss] = useState(colorThemeState) + useEffect(() => { + ss(colorThemeState) + colorThemeStateListeners.add(ss) + return () => { colorThemeStateListeners.delete(ss) } + }, [ss]) + + // s is the theme, return isDark instead of s + const isDark = s === ColorScheme.DARK || s === ColorScheme.HIGH_CONTRAST_DARK + return isDark + +} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx index d4eaa679..6c5648de 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -3,7 +3,7 @@ import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox import { ProviderName, SettingName, displayInfoOfSettingName, titleOfProviderName, providerNames, ModelInfo } from '../../../../../../../platform/void/common/voidSettingsTypes.js' import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' import { VoidInputBox } from '../util/inputs.js' -import { useRefreshModelState, useService, useSettingsState } from '../util/services.js' +import { useIsDark, useRefreshModelState, useService, useSettingsState } from '../util/services.js' @@ -44,7 +44,7 @@ export const ModelMenu = () => { {modelDump.map(m => { const { isHidden, isDefault, modelName, providerName } = m - return
+ return
{modelName} {isDefault ? '' : '(custom)'} {providerName} { settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? 'hidden' : '✅'} @@ -59,7 +59,7 @@ export const ModelMenu = () => { const ProviderSetting = ({ providerName, settingName }: { providerName: ProviderName, settingName: SettingName }) => { - const { title, type, placeholder } = displayInfoOfSettingName(providerName, settingName) + const { title, placeholder } = displayInfoOfSettingName(providerName, settingName) const voidSettingsService = useService('settingsStateService') @@ -110,7 +110,6 @@ const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) = export const VoidProviderSettings = () => { - return <> {providerNames.map(providerName => @@ -123,7 +122,8 @@ export const VoidProviderSettings = () => { // full settings export const Settings = () => { - return
+ const isDark = useIsDark() + return
diff --git a/src/vs/workbench/contrib/void/browser/react/tailwind.config.js b/src/vs/workbench/contrib/void/browser/react/tailwind.config.js index eb3f965f..8a41657b 100644 --- a/src/vs/workbench/contrib/void/browser/react/tailwind.config.js +++ b/src/vs/workbench/contrib/void/browser/react/tailwind.config.js @@ -5,6 +5,7 @@ /** @type {import('tailwindcss').Config} */ module.exports = { + darkMode: 'selector', // '{prefix-}dark' className is used to identify `dark:` content: ['./src2/**/*.{jsx,tsx}'], // uses these files to decide how to transform the css file theme: { extend: { diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index 73b42cc0..6d522842 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -76,6 +76,7 @@ class VoidSettingsPane extends EditorPane { // parent.style.overflow = 'auto' parent.style.userSelect = 'text' + // gets set immediately this.instantiationService.invokeFunction(accessor => { const services = getReactServices(accessor) @@ -91,6 +92,9 @@ class VoidSettingsPane extends EditorPane { container.style.width = `${dimension.width}px`; container.style.height = `${dimension.height}px`; } + + override get minimumWidth() { return 512 } + }