From f21ef61677d272c17d0978f48a0e7beac87bd031 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 14 Dec 2024 21:19:49 -0800 Subject: [PATCH 01/21] .js fix --- CONTRIBUTING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 144b7130..b6ba189a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ There are a few ways to contribute: - 💡 Make suggestions in our [Discord](https://discord.gg/RSNjgaugJs). - ⭐️ If you want to build your AI tool into Void, feel free to get in touch! It's very easy to extend Void, and the UX you create will be much more natural than a VSCode Extension. -Most of Void's code lives in `src/vs/workbench/contrib/void/browser/` and `src/vs/platform/void/`. +Most of Void's code lives in `src/vs/workbench/contrib/void/browser/` and `src/vs/platform/void/`. @@ -75,6 +75,7 @@ Alternatively, if you want to build Void from the terminal, instead of pressing - Make sure you follow the prerequisite steps. - Make sure you have the same NodeJS version as `.nvmrc`. - If you make any React changes, you must re-run `npm run buildreact` and re-build. +- If you get `"TypeError: Failed to fetch dynamically imported module: vscode-file://vscode-app/.../workbench.desktop.main.js", source: file:///.../bootstrap-window.js`, make sure all imports end with `.js`. - If you have any questions, feel free to [submit an issue](https://github.com/voideditor/void/issues/new). For building questions, you can also refer to VSCode's full [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) page. @@ -101,7 +102,7 @@ We don't usually recommend bundling. Instead, you should probably just build. If # Guidelines -We're always glad to talk about new ideas, help you get set up, and make sure your changes align with our vision for the project! Feel free to shoot Mat or Andrew a message, or start chatting with us in the `#contributing` channel of our [Discord](https://discord.gg/RSNjgaugJs). +We're always glad to talk about new ideas, help you get set up, and make sure your changes align with our vision for the project! Feel free to shoot Mat or Andrew a message, or start chatting with us in the `#contributing` channel of our [Discord](https://discord.gg/RSNjgaugJs). ## Submitting a Pull Request From 7dda4bdc08de604769ba4f542cd81fcbc06af5b3 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 15 Dec 2024 00:30:21 -0800 Subject: [PATCH 02/21] add editor pane --- .../void/browser/void.contribution.ts | 15 +++ .../platform/void/common/void.contribution.ts | 11 -- .../contrib/void/browser/findDiffs.ts | 2 +- .../{util/diffLines.tsx => diff/index.tsx} | 0 .../browser/react/src/sidebar-tsx/Sidebar.tsx | 20 +-- .../react/src/sidebar-tsx/SidebarChat.tsx | 4 +- .../browser/react/src/sidebar-tsx/index.tsx | 6 + .../browser/react/src/sidebar-tsx/inputs.tsx | 8 +- .../react/src/util/mountFnGenerator.tsx | 2 +- .../void/browser/react/src/util/services.tsx | 3 +- .../ModelSelectionSettings.tsx | 2 +- .../VoidProviderSettings.tsx | 4 +- .../src/void-settings-tsx/VoidSettings.tsx | 13 ++ .../react/src/void-settings-tsx/index.tsx | 6 + .../contrib/void/browser/react/tsup.config.js | 5 +- .../contrib/void/browser/reactServices.ts | 51 ++++++++ .../contrib/void/browser/registerActions.ts | 36 +++--- .../contrib/void/browser/registerSidebar.ts | 73 +++-------- .../contrib/void/browser/void.contribution.ts | 3 + .../void/browser/voidSettingsEditorPane.ts | 116 ++++++++++++++++++ src/vs/workbench/workbench.common.main.ts | 2 +- 21 files changed, 265 insertions(+), 117 deletions(-) create mode 100644 src/vs/platform/void/browser/void.contribution.ts delete mode 100644 src/vs/platform/void/common/void.contribution.ts rename src/vs/workbench/contrib/void/browser/react/src/{util/diffLines.tsx => diff/index.tsx} (100%) create mode 100644 src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/index.tsx rename src/vs/workbench/contrib/void/browser/react/src/{sidebar-tsx => void-settings-tsx}/ModelSelectionSettings.tsx (98%) rename src/vs/workbench/contrib/void/browser/react/src/{sidebar-tsx => void-settings-tsx}/VoidProviderSettings.tsx (96%) create mode 100644 src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidSettings.tsx create mode 100644 src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/index.tsx create mode 100644 src/vs/workbench/contrib/void/browser/reactServices.ts create mode 100644 src/vs/workbench/contrib/void/browser/voidSettingsEditorPane.ts diff --git a/src/vs/platform/void/browser/void.contribution.ts b/src/vs/platform/void/browser/void.contribution.ts new file mode 100644 index 00000000..5209680d --- /dev/null +++ b/src/vs/platform/void/browser/void.contribution.ts @@ -0,0 +1,15 @@ + + +// ---------- common ---------- + +// llmMessage +import '../common/llmMessageService.js' + +// voidConfig +import '../common/voidConfigService.js' + +// refreshModel +import '../common/refreshModelService.js' + +// metrics +import '../common/metricsService.js' diff --git a/src/vs/platform/void/common/void.contribution.ts b/src/vs/platform/void/common/void.contribution.ts deleted file mode 100644 index 2e12fb9c..00000000 --- a/src/vs/platform/void/common/void.contribution.ts +++ /dev/null @@ -1,11 +0,0 @@ -// llmMessage -import './llmMessageService.js' - -// voidConfig -import './voidConfigService.js' - -// refreshModel -import './refreshModelService.js' - -// metrics -import './metricsService.js' diff --git a/src/vs/workbench/contrib/void/browser/findDiffs.ts b/src/vs/workbench/contrib/void/browser/findDiffs.ts index 32b893e4..60643e32 100644 --- a/src/vs/workbench/contrib/void/browser/findDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/findDiffs.ts @@ -3,7 +3,7 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import { diffLines } from './react/out/util/diffLines.js' +import { diffLines } from './react/out/diff/index.js' export type ComputedDiff = { type: 'edit'; diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/diffLines.tsx b/src/vs/workbench/contrib/void/browser/react/src/diff/index.tsx similarity index 100% rename from src/vs/workbench/contrib/void/browser/react/src/util/diffLines.tsx rename to src/vs/workbench/contrib/void/browser/react/src/diff/index.tsx 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 d605e2d8..e4714679 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 @@ -15,11 +15,11 @@ import { useSidebarState } from '../util/services.js'; import '../styles.css' import { SidebarThreadSelector } from './SidebarThreadSelector.js'; import { SidebarChat } from './SidebarChat.js'; -import { ModelSelectionSettings } from './ModelSelectionSettings.js'; -import { VoidProviderSettings } from './VoidProviderSettings.js'; +import { ModelSelectionSettings } from '../void-settings-tsx/ModelSelectionSettings.js'; +import { VoidProviderSettings } from '../void-settings-tsx/VoidProviderSettings.js'; import ErrorBoundary from './ErrorBoundary.js'; -const Sidebar = () => { +export const Sidebar = () => { const sidebarState = useSidebarState() const { isHistoryOpen, currentTab: tab } = sidebarState @@ -43,16 +43,6 @@ const Sidebar = () => { - - - - - - -
- - -
@@ -61,7 +51,3 @@ const Sidebar = () => { } - -const mountFn = mountFnGenerator(Sidebar) -export default mountFn - 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 f45c0e9c..13ee1b6c 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 @@ -22,7 +22,7 @@ import { OnError, ServiceSendLLMMessageParams } from '../../../../../../../platf import { getCmdKey } from '../../../getCmdKey.js' import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; import { VoidInputBox } from './inputs.js'; -import { ModelSelectionOfFeature } from './ModelSelectionSettings.js'; +import { ModelSelectionOfFeature } from '../void-settings-tsx/ModelSelectionSettings.js'; const IconX = ({ size, className = '' }: { size: number, className?: string }) => { @@ -202,7 +202,7 @@ const ChatBubble = ({ chatMessage }: { } return
-
+
{chatbubbleContents}
diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/index.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/index.tsx new file mode 100644 index 00000000..a174f0ad --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/index.tsx @@ -0,0 +1,6 @@ +import { mountFnGenerator } from '../util/mountFnGenerator.js' +import { Sidebar } from './Sidebar.js' + +export const mountSidebar = mountFnGenerator(Sidebar) + + diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/inputs.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/inputs.tsx index 896762fe..017ed131 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/inputs.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/inputs.tsx @@ -5,9 +5,9 @@ import React, { useCallback, useEffect, useRef } from 'react'; import { useService } from '../util/services.js'; -import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; -import { defaultCheckboxStyles, defaultInputBoxStyles, defaultSelectBoxStyles, defaultToggleStyles } from '../../../../../../../platform/theme/browser/defaultStyles.js'; -import { SelectBox, unthemedSelectBoxStyles } from '../../../../../../../base/browser/ui/selectBox/selectBox.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'; @@ -125,7 +125,7 @@ export const VoidSelectBox = ({ onChangeSelection, onCreateInstance, selectB instance.render(containerRef.current) disposables.push( - instance.onDidSelect(e => { onChangeSelection(options[e.index].value ); }) + instance.onDidSelect(e => { onChangeSelection(options[e.index].value); }) ) if (onCreateInstance) { 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 d6b24f8c..56524b60 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 @@ -5,8 +5,8 @@ import React, { useEffect, useState } from 'react'; import * as ReactDOM from 'react-dom/client' -import { ReactServicesType, VoidSidebarState } from '../../../registerSidebar.js'; import { _registerServices } from './services.js'; +import { ReactServicesType } from '../../../reactServices.js'; export const mountFnGenerator = (Component: React.FC) => (rootElement: HTMLElement, services: ReactServicesType) => { 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 975f5be6..25fb80f3 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 @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { useState, useEffect } from 'react' -import { VoidSidebarState, ReactServicesType } from '../../../registerSidebar.js' +import { VoidSidebarState } from '../../../registerSidebar.js' import { ThreadsState } from '../../../registerThreads.js' import { SettingsOfProvider } from '../../../../../../../platform/void/common/voidConfigTypes.js' import { RefreshModelState } from '../../../../../../../platform/void/common/refreshModelService.js' import { IDisposable } from '../../../../../../../base/common/lifecycle.js' +import { ReactServicesType } from '../../../reactServices.js' // normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ModelSelectionSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx similarity index 98% rename from src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ModelSelectionSettings.tsx rename to src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx index c7f61fdf..e13f0aea 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ModelSelectionSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx @@ -7,7 +7,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { FeatureName, featureNames, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js' import { dummyModelData } from '../../../../../../../platform/void/common/voidConfigModelDefaults.js' import { useConfigState, useRefreshModelState, useService } from '../util/services.js' -import { VoidSelectBox } from './inputs.js' +import { VoidSelectBox } from '../sidebar-tsx/inputs.js' import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js' diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/VoidProviderSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx similarity index 96% rename from src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/VoidProviderSettings.tsx rename to src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx index f85cc74e..33a1b95c 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/VoidProviderSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx @@ -5,10 +5,10 @@ import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react' import { titleOfProviderName, displayInfoOfSettingName, ProviderName, providerNames, featureNames, SettingsOfProvider, SettingName, defaultVoidProviderState } from '../../../../../../../platform/void/common/voidConfigTypes.js' -import { VoidInputBox } from './inputs.js' +import { VoidInputBox } from '../sidebar-tsx/inputs.js' import { useConfigState, useService } from '../util/services.js' import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js' -import ErrorBoundary from './ErrorBoundary.js' +import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' const Setting = ({ providerName, settingName }: { providerName: ProviderName, settingName: SettingName }) => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidSettings.tsx new file mode 100644 index 00000000..ecf60499 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidSettings.tsx @@ -0,0 +1,13 @@ +import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' +import { VoidProviderSettings } from './VoidProviderSettings.js' + + +export const VoidSettings = () => { + return <> + + + + +} + + diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/index.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/index.tsx new file mode 100644 index 00000000..ee89ffab --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/index.tsx @@ -0,0 +1,6 @@ +import { mountFnGenerator } from '../util/mountFnGenerator.js' +import { VoidSettings } from './VoidSettings.js' + +export const mountVoidSettings = mountFnGenerator(VoidSettings) + + diff --git a/src/vs/workbench/contrib/void/browser/react/tsup.config.js b/src/vs/workbench/contrib/void/browser/react/tsup.config.js index c190a71e..5e0df9c0 100644 --- a/src/vs/workbench/contrib/void/browser/react/tsup.config.js +++ b/src/vs/workbench/contrib/void/browser/react/tsup.config.js @@ -7,8 +7,9 @@ import { defineConfig } from 'tsup' export default defineConfig({ entry: [ - './src2/sidebar-tsx/Sidebar.tsx', - './src2/util/diffLines.tsx', + './src2/sidebar-tsx/index.tsx', + './src2/void-settings-tsx/index.tsx', + './src2/diff/index.tsx', ], outDir: './out', format: ['esm'], diff --git a/src/vs/workbench/contrib/void/browser/reactServices.ts b/src/vs/workbench/contrib/void/browser/reactServices.ts new file mode 100644 index 00000000..e465ca93 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/reactServices.ts @@ -0,0 +1,51 @@ +import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; +import { IModelService } from '../../../../editor/common/services/model.js'; +import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; +import { IContextViewService, IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js'; +import { IRefreshModelService } from '../../../../platform/void/common/refreshModelService.js'; +import { IVoidConfigStateService } from '../../../../platform/void/common/voidConfigService.js'; +import { IInlineDiffsService } from './registerInlineDiffs.js'; +import { IVoidSidebarStateService } from './registerSidebar.js'; +import { IThreadHistoryService } from './registerThreads.js'; + +export type ReactServicesType = { + sidebarStateService: IVoidSidebarStateService; + configStateService: IVoidConfigStateService; + threadsStateService: IThreadHistoryService; + fileService: IFileService; + modelService: IModelService; + inlineDiffService: IInlineDiffsService; + llmMessageService: ILLMMessageService; + clipboardService: IClipboardService; + refreshModelService: IRefreshModelService; + + themeService: IThemeService, + hoverService: IHoverService, + + contextViewService: IContextViewService; + contextMenuService: IContextMenuService; +} + + +export const getReactServices = (accessor: ServicesAccessor): ReactServicesType => { + return { + configStateService: accessor.get(IVoidConfigStateService), + sidebarStateService: accessor.get(IVoidSidebarStateService), + threadsStateService: accessor.get(IThreadHistoryService), + fileService: accessor.get(IFileService), + modelService: accessor.get(IModelService), + inlineDiffService: accessor.get(IInlineDiffsService), + llmMessageService: accessor.get(ILLMMessageService), + clipboardService: accessor.get(IClipboardService), + themeService: accessor.get(IThemeService), + hoverService: accessor.get(IHoverService), + refreshModelService: accessor.get(IRefreshModelService), + contextViewService: accessor.get(IContextViewService), + contextMenuService: accessor.get(IContextMenuService), + } +} + diff --git a/src/vs/workbench/contrib/void/browser/registerActions.ts b/src/vs/workbench/contrib/void/browser/registerActions.ts index d46f6bb4..ee6fcdef 100644 --- a/src/vs/workbench/contrib/void/browser/registerActions.ts +++ b/src/vs/workbench/contrib/void/browser/registerActions.ts @@ -150,23 +150,23 @@ registerAction2(class extends Action2 { } }) -// Settings (API config) menu button -registerAction2(class extends Action2 { - constructor() { - super({ - id: 'void.viewSettings', - title: 'Void Settings', - icon: { id: 'settings-gear' }, - menu: [{ id: MenuId.ViewTitle, group: 'navigation', when: ContextKeyExpr.equals('view', VOID_VIEW_ID), }] - }); - } - async run(accessor: ServicesAccessor): Promise { - const stateService = accessor.get(IVoidSidebarStateService) - const metricsService = accessor.get(IMetricsService) +// // Settings (API config) menu button +// registerAction2(class extends Action2 { +// constructor() { +// super({ +// id: 'void.viewSettings', +// title: 'Void Settings', +// icon: { id: 'settings-gear' }, +// menu: [{ id: MenuId.ViewTitle, group: 'navigation', when: ContextKeyExpr.equals('view', VOID_VIEW_ID), }] +// }); +// } +// async run(accessor: ServicesAccessor): Promise { +// const stateService = accessor.get(IVoidSidebarStateService) +// const metricsService = accessor.get(IMetricsService) - metricsService.capture('Chat Navigation', { type: 'Settings' }) +// metricsService.capture('Chat Navigation', { type: 'Settings' }) - stateService.setState({ isHistoryOpen: false, currentTab: stateService.state.currentTab === 'settings' ? 'chat' : 'settings' }) - stateService.fireBlurChat() - } -}) +// stateService.setState({ isHistoryOpen: false, currentTab: stateService.state.currentTab === 'settings' ? 'chat' : 'settings' }) +// stateService.fireBlurChat() +// } +// }) diff --git a/src/vs/workbench/contrib/void/browser/registerSidebar.ts b/src/vs/workbench/contrib/void/browser/registerSidebar.ts index 2c7104cf..6bed1eee 100644 --- a/src/vs/workbench/contrib/void/browser/registerSidebar.ts +++ b/src/vs/workbench/contrib/void/browser/registerSidebar.ts @@ -18,7 +18,7 @@ import * as nls from '../../../../nls.js'; import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; -import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; +// import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { IViewPaneOptions, ViewPane } from '../../../browser/parts/views/viewPane.js'; @@ -27,51 +27,26 @@ import { IContextKeyService } from '../../../../platform/contextkey/common/conte import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { IThreadHistoryService } from './registerThreads.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; +import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; 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 mountFn from './react/out/sidebar-tsx/Sidebar.js'; +import { mountSidebar } from './react/out/sidebar-tsx/index.js'; -import { IVoidConfigStateService } from '../../../../platform/void/common/voidConfigService.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { IInlineDiffsService } from './registerInlineDiffs.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js'; -import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { IRefreshModelService } from '../../../../platform/void/common/refreshModelService.js'; +import { getReactServices } from './reactServices.js'; // compare against search.contribution.ts and debug.contribution.ts, scm.contribution.ts (source control) export type VoidSidebarState = { isHistoryOpen: boolean; - currentTab: 'chat' | 'settings'; -} - -export type ReactServicesType = { - sidebarStateService: IVoidSidebarStateService; - configStateService: IVoidConfigStateService; - threadsStateService: IThreadHistoryService; - fileService: IFileService; - modelService: IModelService; - inlineDiffService: IInlineDiffsService; - llmMessageService: ILLMMessageService; - clipboardService: IClipboardService; - refreshModelService: IRefreshModelService; - - themeService: IThemeService, - hoverService: IHoverService, - - contextViewService: IContextViewService; - contextMenuService: IContextMenuService; + currentTab: 'chat'; } // ---------- Define viewpane ---------- @@ -104,24 +79,10 @@ class VoidSidebarViewPane extends ViewPane { // gets set immediately this.instantiationService.invokeFunction(accessor => { - const services: ReactServicesType = { - configStateService: accessor.get(IVoidConfigStateService), - sidebarStateService: accessor.get(IVoidSidebarStateService), - threadsStateService: accessor.get(IThreadHistoryService), - fileService: accessor.get(IFileService), - modelService: accessor.get(IModelService), - inlineDiffService: accessor.get(IInlineDiffsService), - llmMessageService: accessor.get(ILLMMessageService), - clipboardService: accessor.get(IClipboardService), - themeService: accessor.get(IThemeService), - hoverService: accessor.get(IHoverService), - refreshModelService: accessor.get(IRefreshModelService), - contextViewService: accessor.get(IContextViewService), - contextMenuService: accessor.get(IContextMenuService), - } + const services = getReactServices(accessor) // mount react - const disposables: IDisposable[] | undefined = mountFn(parent, services); + const disposables: IDisposable[] | undefined = mountSidebar(parent, services); disposables?.forEach(d => this._register(d)) }); } @@ -143,12 +104,12 @@ export const VOID_VIEW_ID = VOID_VIEW_CONTAINER_ID // simplicity const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const viewContainer = viewContainerRegistry.registerViewContainer({ id: VOID_VIEW_CONTAINER_ID, - title: nls.localize2('void', 'Void Chat'), // this is used to say "Void" (Ctrl + L) + title: nls.localize2('void chat', 'Void Chat'), // this is used to say "Void" (Ctrl + L) ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [VOID_VIEW_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }]), hideIfEmpty: false, // icon: voidViewIcon, order: 1, -}, ViewContainerLocation.AuxiliaryBar, { doNotRegisterOpenCommand: true, }); +}, ViewContainerLocation.AuxiliaryBar, { doNotRegisterOpenCommand: true, isDefault: true }); @@ -158,17 +119,17 @@ viewsRegistry.registerViews([{ id: VOID_VIEW_ID, hideByDefault: false, // start open // containerIcon: voidViewIcon, - name: nls.localize2('void chat', "Chat"), // this says ... : CHAT + name: nls.localize2('chat', 'Chat'), // this says ... : CHAT ctorDescriptor: new SyncDescriptor(VoidSidebarViewPane), canToggleVisibility: false, canMoveView: true, - openCommandActionDescriptor: { - id: viewContainer.id, - keybindings: { - primary: KeyMod.CtrlCmd | KeyCode.KeyL, - }, - order: 1 - }, + // openCommandActionDescriptor: { + // id: viewContainer.id, + // keybindings: { + // primary: KeyMod.CtrlCmd | KeyCode.KeyL, + // }, + // order: 1 + // }, }], viewContainer); diff --git a/src/vs/workbench/contrib/void/browser/void.contribution.ts b/src/vs/workbench/contrib/void/browser/void.contribution.ts index 2dde5f70..dbb6f0ab 100644 --- a/src/vs/workbench/contrib/void/browser/void.contribution.ts +++ b/src/vs/workbench/contrib/void/browser/void.contribution.ts @@ -20,3 +20,6 @@ import './registerAutocomplete.js' // register css import './media/void.css' + +// settings pane +import './voidSettingsEditorPane.js' diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsEditorPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsEditorPane.ts new file mode 100644 index 00000000..cb5f4732 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/voidSettingsEditorPane.ts @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPL 3.0 License. + *--------------------------------------------------------------------------------------------*/ + +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { EditorInput } from '../../../common/editor/editorInput.js'; +import * as nls from '../../../../nls.js'; +import { EditorExtensions } from '../../../common/editor.js'; +import { EditorPane } from '../../../browser/parts/editor/editorPane.js'; +import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { IStorageService } from '../../../../platform/storage/common/storage.js'; +import { Dimension } from '../../../../base/browser/dom.js'; +import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; +import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; +import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; +import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { URI } from '../../../../base/common/uri.js'; +import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; + + +import { mountVoidSettings } from './react/out/void-settings-tsx/index.js' +import { getReactServices } from './reactServices.js'; + + +// refer to preferences.contribution.ts keybindings editor + +export class VoidEditorInput extends EditorInput { + + static readonly ID: string = 'workbench.input.void.settings'; + + readonly resource = URI.from({ + scheme: 'void-editor-settings', + path: 'void-settings' // Give it a unique path + }); + + constructor() { + super(); + } + + override get typeId(): string { + return VoidEditorInput.ID; + } + + override getName(): string { + return nls.localize('voidSettingsInputsName', "Void Settings"); + } + +} + + +class MyCustomPane extends EditorPane { + static readonly ID = 'workbench.test.myCustomPane'; + + constructor( + group: IEditorGroup, + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(MyCustomPane.ID, group, telemetryService, themeService, storageService); + } + + protected createEditor(container: HTMLElement): void { + + this.instantiationService.invokeFunction(accessor => { + const services = getReactServices(accessor) + mountVoidSettings(container, services); + }) + } + + layout(dimension: Dimension): void { + const container = this.getContainer(); + if (!container) return; + + container.style.width = `${dimension.width}px`; + container.style.height = `${dimension.height}px`; + } +} + + + +Registry.as(EditorExtensions.EditorPane).registerEditorPane( + EditorPaneDescriptor.create(MyCustomPane, MyCustomPane.ID, nls.localize('MyCustomPane', "CustomPane")), + [new SyncDescriptor(VoidEditorInput)] +); + + +// Register the action +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.openVoidEditor', + title: 'Open Void Settings', + keybinding: { + when: ContextKeyExpr.true(), + primary: KeyMod.CtrlCmd | KeyCode.KeyE, + weight: KeybindingWeight.WorkbenchContrib + } + }); + } + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const instantiationService = accessor.get(IInstantiationService); + const input = instantiationService.createInstance(VoidEditorInput); + await editorService.openEditor(input); + } +}); + diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index e1ea851c..36bd6ad4 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -17,7 +17,7 @@ import './browser/workbench.contribution.js'; //#region --- Void // Void added this: import './contrib/void/browser/void.contribution.js'; -import '../platform/void/common/void.contribution.js'; +import '../platform/void/browser/void.contribution.js'; //#endregion From 0589a2a5ece6d88c4c891251d244c3172bc898b0 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 15 Dec 2024 01:11:07 -0800 Subject: [PATCH 03/21] remove maxtokens --- .../void/common/voidConfigModelDefaults.ts | 58 ------------ .../platform/void/common/voidConfigTypes.ts | 94 +++++++++++++++---- .../electron-main/llmMessage/anthropic.ts | 7 +- .../void/electron-main/llmMessage/groq.ts | 3 +- .../void/electron-main/llmMessage/ollama.ts | 3 +- .../void/electron-main/llmMessage/openai.ts | 8 +- .../void/electron-main/llmMessage/util.ts | 14 --- .../ModelSelectionSettings.tsx | 2 +- 8 files changed, 88 insertions(+), 101 deletions(-) delete mode 100644 src/vs/platform/void/common/voidConfigModelDefaults.ts delete mode 100644 src/vs/platform/void/electron-main/llmMessage/util.ts diff --git a/src/vs/platform/void/common/voidConfigModelDefaults.ts b/src/vs/platform/void/common/voidConfigModelDefaults.ts deleted file mode 100644 index 8d2fc0f2..00000000 --- a/src/vs/platform/void/common/voidConfigModelDefaults.ts +++ /dev/null @@ -1,58 +0,0 @@ - - -export const defaultAnthropicModels = [ - 'claude-3-5-sonnet-20240620', - // 'claude-3-opus-20240229', - // 'claude-3-sonnet-20240229', - // 'claude-3-haiku-20240307' -] - - -export const defaultOpenAIModels = [ - 'o1-preview', - 'o1-mini', - 'gpt-4o', - 'gpt-4o-mini', - // 'gpt-4o-2024-05-13', - // 'gpt-4o-2024-08-06', - // 'gpt-4o-mini-2024-07-18', - // 'gpt-4-turbo', - // 'gpt-4-turbo-2024-04-09', - // 'gpt-4-turbo-preview', - // 'gpt-4-0125-preview', - // 'gpt-4-1106-preview', - // 'gpt-4', - // 'gpt-4-0613', - // 'gpt-3.5-turbo-0125', - // 'gpt-3.5-turbo', - // 'gpt-3.5-turbo-1106', -] - - - -export const defaultGroqModels = [ - "mixtral-8x7b-32768", - "llama2-70b-4096", - "gemma-7b-it" -] - - -export const defaultGeminiModels = [ - 'gemini-1.5-flash', - 'gemini-1.5-pro', - 'gemini-1.5-flash-8b', - 'gemini-1.0-pro' -] - - - - - -export const dummyModelData = { - anthropic: ['claude 3.5'], - openAI: ['gpt 4o'], - ollama: ['llama 3.2', 'codestral'], - openRouter: ['qwen 2.5'], -} - - diff --git a/src/vs/platform/void/common/voidConfigTypes.ts b/src/vs/platform/void/common/voidConfigTypes.ts index cdd3e18f..5415af4b 100644 --- a/src/vs/platform/void/common/voidConfigTypes.ts +++ b/src/vs/platform/void/common/voidConfigTypes.ts @@ -4,7 +4,84 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import { defaultAnthropicModels, defaultGeminiModels, defaultGroqModels, defaultOpenAIModels } from './voidConfigModelDefaults.js' + +// https://docs.anthropic.com/en/docs/about-claude/models +export const defaultAnthropicModels = [ + 'claude-3-5-sonnet-20241022', + 'claude-3-5-haiku-20241022', + 'claude-3-opus-20240229', + 'claude-3-sonnet-20240229', + // 'claude-3-haiku-20240307', +] + + +export const defaultOpenAIModels = [ + 'o1-preview', + 'o1-mini', + 'gpt-4o', + 'gpt-4o-mini', + // 'gpt-4o-2024-05-13', + // 'gpt-4o-2024-08-06', + // 'gpt-4o-mini-2024-07-18', + // 'gpt-4-turbo', + // 'gpt-4-turbo-2024-04-09', + // 'gpt-4-turbo-preview', + // 'gpt-4-0125-preview', + // 'gpt-4-1106-preview', + // 'gpt-4', + // 'gpt-4-0613', + // 'gpt-3.5-turbo-0125', + // 'gpt-3.5-turbo', + // 'gpt-3.5-turbo-1106', +] + + + +export const defaultGroqModels = [ + "mixtral-8x7b-32768", + "llama2-70b-4096", + "gemma-7b-it" +] + + +export const defaultGeminiModels = [ + 'gemini-1.5-flash', + 'gemini-1.5-pro', + 'gemini-1.5-flash-8b', + 'gemini-1.0-pro' +] + + + +// export const parseMaxTokensStr = (maxTokensStr: string) => { +// // parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN +// const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr) +// if (Number.isNaN(int)) +// return undefined +// return int +// } + + + + +export const anthropicMaxPossibleTokens = (modelName: string) => { + if (modelName === 'claude-3-5-sonnet-20241022' + || modelName === 'claude-3-5-haiku-20241022') + return 8192 + if (modelName === 'claude-3-opus-20240229' + || modelName === 'claude-3-sonnet-20240229' + || modelName === 'claude-3-haiku-20240307') + return 4096 + return 1024 // return a reasonably small number if they're using a different model +} + + +export const dummyModelData = { + anthropic: ['claude 3.5'], + openAI: ['gpt 4o'], + ollama: ['llama 3.2', 'codestral'], + openRouter: ['qwen 2.5'], +} @@ -74,7 +151,6 @@ export type SettingsOfProvider = { & { enabled: string, // 'true' | 'false' - maxTokens: string, models: string[], // if null, user can type in any string as a model }) @@ -137,13 +213,6 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName : '(never)', } } - else if (settingName === 'maxTokens') { - return { - title: 'Max Tokens', - type: 'number', - placeholder: '1024', - } - } else if (settingName === 'enabled') { return { title: 'Enabled?', @@ -170,43 +239,36 @@ export const defaultVoidProviderState: SettingsOfProvider = { ...voidProviderDefaults.anthropic, ...voidInitModelOptions.anthropic, enabled: 'false', - maxTokens: '', }, openAI: { ...voidProviderDefaults.openAI, ...voidInitModelOptions.openAI, enabled: 'false', - maxTokens: '', }, ollama: { ...voidProviderDefaults.ollama, ...voidInitModelOptions.ollama, enabled: 'false', - maxTokens: '', }, openRouter: { ...voidProviderDefaults.openRouter, ...voidInitModelOptions.openRouter, enabled: 'false', - maxTokens: '', }, openAICompatible: { ...voidProviderDefaults.openAICompatible, ...voidInitModelOptions.openAICompatible, enabled: 'false', - maxTokens: '', }, gemini: { ...voidProviderDefaults.gemini, ...voidInitModelOptions.gemini, enabled: 'false', - maxTokens: '', }, groq: { ...voidProviderDefaults.groq, ...voidInitModelOptions.groq, enabled: 'false', - maxTokens: '', } } diff --git a/src/vs/platform/void/electron-main/llmMessage/anthropic.ts b/src/vs/platform/void/electron-main/llmMessage/anthropic.ts index 86282282..964047b2 100644 --- a/src/vs/platform/void/electron-main/llmMessage/anthropic.ts +++ b/src/vs/platform/void/electron-main/llmMessage/anthropic.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import Anthropic from '@anthropic-ai/sdk'; -import { parseMaxTokensStr } from './util.js'; import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js'; -import { displayInfoOfSettingName } from '../../common/voidConfigTypes.js'; +import { anthropicMaxPossibleTokens } from '../../common/voidConfigTypes.js'; // Anthropic type LLMMessageAnthropic = { @@ -17,9 +16,9 @@ export const sendAnthropicMsg: _InternalSendLLMMessageFnType = ({ messages, onTe const thisConfig = settingsOfProvider.anthropic - const maxTokens = parseMaxTokensStr(thisConfig.maxTokens) + const maxTokens = anthropicMaxPossibleTokens(modelName) if (maxTokens === undefined) { - onError({ message: `Please set a value for ${displayInfoOfSettingName('anthropic', 'maxTokens').title}.`, fullError: null }) + onError({ message: `Please set a value for Max Tokens.`, fullError: null }) return } diff --git a/src/vs/platform/void/electron-main/llmMessage/groq.ts b/src/vs/platform/void/electron-main/llmMessage/groq.ts index 5ab59211..1b5918a7 100644 --- a/src/vs/platform/void/electron-main/llmMessage/groq.ts +++ b/src/vs/platform/void/electron-main/llmMessage/groq.ts @@ -5,7 +5,6 @@ import Groq from 'groq-sdk'; import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js'; -import { parseMaxTokensStr } from './util.js'; // Groq export const sendGroqMsg: _InternalSendLLMMessageFnType = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => { @@ -24,7 +23,7 @@ export const sendGroqMsg: _InternalSendLLMMessageFnType = async ({ messages, onT model: modelName, stream: true, temperature: 0.7, - max_tokens: parseMaxTokensStr(thisConfig.maxTokens), + // max_tokens: parseMaxTokensStr(thisConfig.maxTokens), }) .then(async response => { _setAborter(() => response.controller.abort()) diff --git a/src/vs/platform/void/electron-main/llmMessage/ollama.ts b/src/vs/platform/void/electron-main/llmMessage/ollama.ts index ba04e117..03cf29dc 100644 --- a/src/vs/platform/void/electron-main/llmMessage/ollama.ts +++ b/src/vs/platform/void/electron-main/llmMessage/ollama.ts @@ -5,7 +5,6 @@ import { Ollama } from 'ollama'; import { _InternalOllamaListFnType, _InternalSendLLMMessageFnType, ModelResponse } from '../../common/llmMessageTypes.js'; -import { parseMaxTokensStr } from './util.js'; export const ollamaList: _InternalOllamaListFnType = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider }) => { @@ -49,7 +48,7 @@ export const sendOllamaMsg: _InternalSendLLMMessageFnType = ({ messages, onText, model: modelName, messages: messages, stream: true, - options: { num_predict: parseMaxTokensStr(thisConfig.maxTokens) } // this is max_tokens + // options: { num_predict: parseMaxTokensStr(thisConfig.maxTokens) } // this is max_tokens }) .then(async stream => { _setAborter(() => stream.abort()) diff --git a/src/vs/platform/void/electron-main/llmMessage/openai.ts b/src/vs/platform/void/electron-main/llmMessage/openai.ts index 3b8c3645..03eb7761 100644 --- a/src/vs/platform/void/electron-main/llmMessage/openai.ts +++ b/src/vs/platform/void/electron-main/llmMessage/openai.ts @@ -5,7 +5,7 @@ import OpenAI from 'openai'; import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js'; -import { parseMaxTokensStr } from './util.js'; +// import { parseMaxTokensStr } from './util.js'; // OpenAI, OpenRouter, OpenAICompatible @@ -20,7 +20,7 @@ export const sendOpenAIMsg: _InternalSendLLMMessageFnType = ({ messages, onText, if (providerName === 'openAI') { const thisConfig = settingsOfProvider.openAI openai = new OpenAI({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true }); - options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } + options = { model: modelName, messages: messages, stream: true, /*max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens)*/ } } else if (providerName === 'openRouter') { const thisConfig = settingsOfProvider.openRouter @@ -31,12 +31,12 @@ export const sendOpenAIMsg: _InternalSendLLMMessageFnType = ({ messages, onText, 'X-Title': 'Void Editor', // Optional. Shows in rankings on openrouter.ai. }, }); - options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } + options = { model: modelName, messages: messages, stream: true, /*max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens)*/ } } else if (providerName === 'openAICompatible') { const thisConfig = settingsOfProvider.openAICompatible openai = new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true }) - options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } + options = { model: modelName, messages: messages, stream: true, /*max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens)*/ } } else { console.error(`sendOpenAIMsg: invalid providerName: ${providerName}`) diff --git a/src/vs/platform/void/electron-main/llmMessage/util.ts b/src/vs/platform/void/electron-main/llmMessage/util.ts deleted file mode 100644 index 988e4706..00000000 --- a/src/vs/platform/void/electron-main/llmMessage/util.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Glass Devtools, Inc. All rights reserved. - * Void Editor additions licensed under the AGPL 3.0 License. - *--------------------------------------------------------------------------------------------*/ - -export const parseMaxTokensStr = (maxTokensStr: string) => { - // parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN - const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr) - if (Number.isNaN(int)) - return undefined - return int -} - - diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx index e13f0aea..2e95d1ed 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx @@ -5,7 +5,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { FeatureName, featureNames, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js' -import { dummyModelData } from '../../../../../../../platform/void/common/voidConfigModelDefaults.js' +import { dummyModelData } from '../../../../../../../platform/void/common/voidConfigTypes.js' import { useConfigState, useRefreshModelState, useService } from '../util/services.js' import { VoidSelectBox } from '../sidebar-tsx/inputs.js' import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js' From a42ff2d59a4cfa51762993a86bd8e30c3b559ee4 Mon Sep 17 00:00:00 2001 From: mp Date: Sun, 15 Dec 2024 01:30:44 -0800 Subject: [PATCH 04/21] sidebar + ux --- .../browser/react/src/sidebar-tsx/Sidebar.tsx | 18 +++- .../react/src/sidebar-tsx/SidebarChat.tsx | 92 +++++++++++++++---- .../void/browser/react/tailwind.config.js | 49 +++++----- .../contrib/void/browser/registerActions.ts | 13 ++- .../contrib/void/browser/registerThreads.ts | 12 ++- 5 files changed, 129 insertions(+), 55 deletions(-) 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 e4714679..7725b16a 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 @@ -24,8 +24,8 @@ export const Sidebar = () => { const { isHistoryOpen, currentTab: tab } = sidebarState // className='@@void-scope' - return
-
+ return
+
{/* { const tabs = ['chat', 'settings', 'threadSelector'] @@ -33,18 +33,28 @@ export const Sidebar = () => { sidebarStateService.setState({ currentTab: tabs[(index + 1) % tabs.length] as any }) }}>clickme {tab} */} -
+
-
+
+ + {/* + + */}
+ {/*
+ + + +
*/} +
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 13ee1b6c..92e44671 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 @@ -86,6 +86,52 @@ const IconSquare = ({ size, className = '' }: { size: number, className?: string }; +const ScrollToBottomContainer = ({ children, height, className }: { children: React.ReactNode, height: React.CSSProperties['height'], className?: string }) => { + const [isAtBottom, setIsAtBottom] = useState(true); // Start at bottom + const divRef = useRef(null); + + const scrollToBottom = () => { + if (divRef.current) { + divRef.current.scrollTop = divRef.current.scrollHeight; + } + }; + + const onScroll = () => { + const div = divRef.current; + if (!div) return; + + const isBottom = Math.abs( + div.scrollHeight - div.clientHeight - div.scrollTop + ) < 1; + + setIsAtBottom(isBottom); + }; + + // When children change (new messages added) + useEffect(() => { + if (isAtBottom) { + scrollToBottom(); + } + }, [children, isAtBottom]); // Dependency on children to detect new messages + + // Initial scroll to bottom + useEffect(() => { + scrollToBottom(); + }, []); + + return ( +
+ {children} +
+ ); +}; + + // read files from VSCode const VSReadFile = async (modelService: IModelService, uri: URI): Promise => { const model = modelService.getModel(uri) @@ -112,7 +158,7 @@ export const SelectedFiles = ( return ( !!selections && selections.length !== 0 && ( -
+
{selections.map((selection, i) => ( {/* selected file summary */} @@ -122,6 +168,7 @@ export const SelectedFiles = ( select-none bg-vscode-badge-bg border border-vscode-button-border rounded-md w-fit h-fit min-w-[80px] p-1 + text-left `} onClick={() => { setSelectionIsOpened(s => { @@ -145,9 +192,11 @@ export const SelectedFiles = ( {/* X button */} {type === 'staging' && // hoveredIdx === i { + onClick={(e) => { + e.stopPropagation(); if (type !== 'staging') return; setStaging([...selections.slice(0, i), ...selections.slice(i + 1)]) + setSelectionIsOpened(o => [...o.slice(0, i), ...o.slice(i + 1)]) }} > @@ -252,7 +301,6 @@ export const SidebarChat = () => { const isDisabled = !instructions.trim() const formRef = useRef(null) - const onSubmit = async (e: FormEvent) => { e.preventDefault() @@ -359,7 +407,10 @@ export const SidebarChat = () => { const previousMessages = currentThread?.messages ?? [] return <> -
+ {/* previous messages */} {previousMessages.map((message, i) => @@ -367,23 +418,22 @@ export const SidebarChat = () => { {/* message stream */} -
+ {/* input box */}
0 ? 'absolute bottom-0' : ''}`} >
{ if (e.key === 'Enter' && !e.shiftKey) { onSubmit(e) @@ -434,19 +484,21 @@ export const SidebarChat = () => { {isLoading ? // stop button : // submit button (up arrow)
} From 718b8cc61b951beff1342c2c5a3eaac4622e50d0 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 15 Dec 2024 21:10:15 -0800 Subject: [PATCH 07/21] add void settings in lower left --- .../platform/void/common/voidSettingsService.ts | 2 +- .../ModelSelectionSettings.tsx | 17 +++++++---------- .../contrib/void/browser/voidSettingsPane.ts | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/void/common/voidSettingsService.ts b/src/vs/platform/void/common/voidSettingsService.ts index 6309213b..dbaf0837 100644 --- a/src/vs/platform/void/common/voidSettingsService.ts +++ b/src/vs/platform/void/common/voidSettingsService.ts @@ -29,7 +29,7 @@ type SetModelSelectionOfFeature = ( -type ModelOption = { text: string, value: ModelSelection } +export type ModelOption = { text: string, value: ModelSelection } diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx index beefbc3c..bc16aeaa 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx @@ -17,13 +17,13 @@ export const ModelSelectionOfFeature = ({ featureName }: { featureName: FeatureN let weChangedText = false return <> -

{featureName}

- { - { if (weChangedText) return - voidSettingsService.setModelSelectionOfFeature(featureName, newVal) }, [voidSettingsService, featureName])} // we are responsible for setting the initial state here. always sync instance when state changes. @@ -32,18 +32,15 @@ export const ModelSelectionOfFeature = ({ featureName }: { featureName: FeatureN const modelsListRef = voidSettingsService.state._modelsList // as a ref const settingsAtProvider = voidSettingsService.state.modelSelectionOfFeature[featureName] const selectionIdx = settingsAtProvider === null ? -1 : modelsListRef.findIndex(v => modelSelectionsEqual(v.value, settingsAtProvider)) - if (selectionIdx !== -1) { - weChangedText = true - instance.select(selectionIdx) - weChangedText = false - } + weChangedText = true + instance.select(selectionIdx === -1 ? 0 : selectionIdx) + weChangedText = false } syncInstance() const disposable = voidSettingsService.onDidChangeState(syncInstance) return [disposable] }, [voidSettingsService, featureName])} />} - } diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index 3bf9f685..7ece62e6 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -15,7 +15,7 @@ import { IStorageService } from '../../../../platform/storage/common/storage.js' import { Dimension } from '../../../../base/browser/dom.js'; import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; -import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; @@ -92,11 +92,12 @@ Registry.as(EditorExtensions.EditorPane).registerEditorPane ); +const OPEN_VOID_SETTINGS_ID = 'workbench.action.openVoidSettings' // Register the gear registerAction2(class extends Action2 { constructor() { super({ - id: 'workbench.action.openVoidSettings', + id: OPEN_VOID_SETTINGS_ID, title: nls.localize2('voidSettings', "Void: Settings"), f1: true, icon: Codicon.gear, @@ -120,3 +121,14 @@ registerAction2(class extends Action2 { await editorService.openEditor(input); } }) + + + +MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '0_command', + command: { + id: OPEN_VOID_SETTINGS_ID, + title: nls.localize('voidSettings', "Void Settings") + }, + order: 1 +}); From 8403eed8ca73ae5ca5ef0352e9571e919b9578b1 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 15 Dec 2024 21:42:31 -0800 Subject: [PATCH 08/21] fix hook --- .../ModelSelectionSettings.tsx | 55 ++++++++++--------- .../contrib/void/browser/voidSettingsPane.ts | 2 +- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx index bc16aeaa..6c8c53c1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx @@ -10,37 +10,40 @@ import { VoidSelectBox } from '../sidebar-tsx/inputs.js' import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js' -export const ModelSelectionOfFeature = ({ featureName }: { featureName: FeatureName }) => { - +const SelectBoxOfFeature = ({ featureName }: { featureName: FeatureName }) => { const voidSettingsService = useService('settingsStateService') const settingsState = useSettingsState() let weChangedText = false - return <> - {settingsState._modelsList.length === 0 ? - 'Please add a provider!' - : { - if (weChangedText) return - voidSettingsService.setModelSelectionOfFeature(featureName, newVal) - }, [voidSettingsService, featureName])} - // we are responsible for setting the initial state here. always sync instance when state changes. - onCreateInstance={useCallback((instance: SelectBox) => { - const syncInstance = () => { - const modelsListRef = voidSettingsService.state._modelsList // as a ref - const settingsAtProvider = voidSettingsService.state.modelSelectionOfFeature[featureName] - const selectionIdx = settingsAtProvider === null ? -1 : modelsListRef.findIndex(v => modelSelectionsEqual(v.value, settingsAtProvider)) - weChangedText = true - instance.select(selectionIdx === -1 ? 0 : selectionIdx) - weChangedText = false - } - syncInstance() - const disposable = voidSettingsService.onDidChangeState(syncInstance) - return [disposable] - }, [voidSettingsService, featureName])} - />} + return { + if (weChangedText) return + voidSettingsService.setModelSelectionOfFeature(featureName, newVal) + }, [voidSettingsService, featureName])} + // we are responsible for setting the initial state here. always sync instance when state changes. + onCreateInstance={useCallback((instance: SelectBox) => { + const syncInstance = () => { + const modelsListRef = voidSettingsService.state._modelsList // as a ref + const settingsAtProvider = voidSettingsService.state.modelSelectionOfFeature[featureName] + const selectionIdx = settingsAtProvider === null ? -1 : modelsListRef.findIndex(v => modelSelectionsEqual(v.value, settingsAtProvider)) + weChangedText = true + instance.select(selectionIdx === -1 ? 0 : selectionIdx) + weChangedText = false + } + syncInstance() + const disposable = voidSettingsService.onDidChangeState(syncInstance) + return [disposable] + }, [voidSettingsService, featureName])} + /> +} + + +export const ModelSelectionOfFeature = ({ featureName }: { featureName: FeatureName }) => { + const settingsState = useSettingsState() + return <> + {settingsState._modelsList.length === 0 ? 'Please add a provider!' : } } diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index 7ece62e6..f09b5e63 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -123,7 +123,7 @@ registerAction2(class extends Action2 { }) - +// add to settings gear on bottom left MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '0_command', command: { From f973f8d4e794d159b5ad1e548b2efef06becff35 Mon Sep 17 00:00:00 2001 From: mp Date: Sun, 15 Dec 2024 22:24:12 -0800 Subject: [PATCH 09/21] fix selection range bug --- .../react/src/sidebar-tsx/SidebarChat.tsx | 18 ++++++++++----- .../contrib/void/browser/sidebarActions.ts | 22 ++++++++++--------- 2 files changed, 25 insertions(+), 15 deletions(-) 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 8226db82..591886bc 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 @@ -297,8 +297,6 @@ export const SidebarChat = () => { const isDisabled = !instructions.trim() const [formHeight, setFormHeight] = useState(0) const [sidebarHeight, setSidebarHeight] = useState(0) - const formRef = useCallback((node: HTMLFormElement | null) => { if (node) { setFormHeight(node.clientHeight); } }, [setFormHeight]); - const sidebarRef = useCallback((node: HTMLDivElement | null) => { if (node) { setSidebarHeight(node.clientHeight); } }, [setSidebarHeight]); const onChangeText = useCallback((newStr: string) => { setInstructions(newStr) }, [setInstructions]) @@ -406,8 +404,14 @@ export const SidebarChat = () => { const previousMessages = currentThread?.messages ?? [] + + + + const [_test, _setTest] = useState([]) + + return
{ if (ref) { setSidebarHeight(ref.clientHeight); } }} className={`w-full h-full`} > { {/* message stream */} -
{`text`}
+ + {_test.map((_, i) =>
div {i}
)} +
{`totalHeight: ${sidebarHeight - formHeight - 30}`}
+
{`sidebarHeight: ${sidebarHeight}`}
+
{`formHeight: ${formHeight}`}
@@ -430,7 +438,7 @@ export const SidebarChat = () => { className={`right-0 left-0 m-2 z-[999] ${previousMessages.length > 0 ? 'absolute bottom-0' : ''}`} >
{ if (ref) { setFormHeight(ref.clientHeight); } }} className={` flex flex-col gap-2 p-2 relative input text-left shrink-0 transition-all duration-200 diff --git a/src/vs/workbench/contrib/void/browser/sidebarActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts index bc80596d..06f86b8e 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarActions.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts @@ -73,20 +73,12 @@ registerAction2(class extends Action2 { accessor.get(IEditorService).activeTextEditorControl?.getSelection() ) - // add selection - const threadHistoryService = accessor.get(IThreadHistoryService) - const currentStaging = threadHistoryService.state._currentStagingSelections - const currentStagingEltIdx = currentStaging?.findIndex(s => - s.fileURI.fsPath === model.uri.fsPath - && s.range?.startLineNumber === selectionRange?.startLineNumber - && s.range?.endLineNumber === selectionRange?.endLineNumber - ) if (selectionRange) { const selectionStr = getContentInRange(model, selectionRange) - const selection: CodeStagingSelection = selectionStr === null ? { + const selection: CodeStagingSelection = selectionStr === null || selectionRange.startLineNumber > selectionRange.endLineNumber ? { type: 'File', fileURI: model.uri, selectionStr: null, @@ -98,7 +90,16 @@ registerAction2(class extends Action2 { range: selectionRange, } - // overwrite selections that match with this one (compares by `fileURI` and line numbers in `range`) + // add selection to staging + const threadHistoryService = accessor.get(IThreadHistoryService) + const currentStaging = threadHistoryService.state._currentStagingSelections + const currentStagingEltIdx = currentStaging?.findIndex(s => + s.fileURI.fsPath === model.uri.fsPath + && s.range?.startLineNumber === selection.range?.startLineNumber + && s.range?.endLineNumber === selection.range?.endLineNumber + ) + + // if matches with existing selection, overwrite if (currentStagingEltIdx !== undefined && currentStagingEltIdx !== -1) { threadHistoryService.setStaging([ ...currentStaging!.slice(0, currentStagingEltIdx), @@ -106,6 +107,7 @@ registerAction2(class extends Action2 { ...currentStaging!.slice(currentStagingEltIdx + 1, Infinity) ]) } + // if no match, add else { threadHistoryService.setStaging([...(currentStaging ?? []), selection]) } From fc187e0028c7ef74e0fd326bc764310880ac11ee Mon Sep 17 00:00:00 2001 From: mp Date: Sun, 15 Dec 2024 23:01:24 -0800 Subject: [PATCH 10/21] sidebar scrolllock --- .../void/browser/react/src/sidebar-tsx/Sidebar.tsx | 2 +- .../void/browser/react/src/sidebar-tsx/SidebarChat.tsx | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) 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 7725b16a..399feff8 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 @@ -24,7 +24,7 @@ export const Sidebar = () => { const { isHistoryOpen, currentTab: tab } = sidebarState // className='@@void-scope' - return
+ 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 591886bc..abb47d23 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 @@ -58,8 +58,8 @@ const IconArrowUp = ({ size, className = '' }: { size: number, className?: strin > @@ -405,11 +405,8 @@ export const SidebarChat = () => { const previousMessages = currentThread?.messages ?? [] - - const [_test, _setTest] = useState([]) - return
{ if (ref) { setSidebarHeight(ref.clientHeight); } }} className={`w-full h-full`} @@ -429,6 +426,7 @@ export const SidebarChat = () => {
{`totalHeight: ${sidebarHeight - formHeight - 30}`}
{`sidebarHeight: ${sidebarHeight}`}
{`formHeight: ${formHeight}`}
+ From 78a48bae138a0b9422c6f030faa1fc92c8aa15e1 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 16 Dec 2024 00:17:48 -0800 Subject: [PATCH 11/21] add model selection --- .../void/common/refreshModelService.ts | 2 +- .../void/common/voidSettingsService.ts | 73 +++++++++++++++---- .../platform/void/common/voidSettingsTypes.ts | 52 ++++++++----- .../browser/react/src/sidebar-tsx/Sidebar.tsx | 2 - .../react/src/sidebar-tsx/SidebarChat.tsx | 4 +- ...electionSettings.tsx => ModelDropdown.tsx} | 41 +++-------- .../src/void-settings-tsx/ModelSettings.tsx | 58 +++++++++++++++ ...viderSettings.tsx => ProviderSettings.tsx} | 0 .../react/src/void-settings-tsx/Settings.tsx | 17 +++++ .../src/void-settings-tsx/VoidSettings.tsx | 13 ---- .../react/src/void-settings-tsx/index.tsx | 4 +- .../contrib/void/browser/voidSettingsPane.ts | 23 +++--- 12 files changed, 199 insertions(+), 90 deletions(-) rename src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/{ModelSelectionSettings.tsx => ModelDropdown.tsx} (67%) create mode 100644 src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSettings.tsx rename src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/{VoidProviderSettings.tsx => ProviderSettings.tsx} (100%) create mode 100644 src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx delete mode 100644 src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidSettings.tsx diff --git a/src/vs/platform/void/common/refreshModelService.ts b/src/vs/platform/void/common/refreshModelService.ts index f1f0c575..b9b44e83 100644 --- a/src/vs/platform/void/common/refreshModelService.ts +++ b/src/vs/platform/void/common/refreshModelService.ts @@ -85,7 +85,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ this.llmMessageService.ollamaList({ onSuccess: ({ models }) => { - this.voidSettingsService.setSettingOfProvider('ollama', 'models', models.map(model => model.name)) + this.voidSettingsService.setDefaultModels('ollama', models.map(model => model.name)) this._setState('done') }, onError: ({ error }) => { diff --git a/src/vs/platform/void/common/voidSettingsService.ts b/src/vs/platform/void/common/voidSettingsService.ts index dbaf0837..a11e3f60 100644 --- a/src/vs/platform/void/common/voidSettingsService.ts +++ b/src/vs/platform/void/common/voidSettingsService.ts @@ -10,10 +10,10 @@ import { IEncryptionService } from '../../encryption/common/encryptionService.js import { registerSingleton, InstantiationType } from '../../instantiation/common/extensions.js'; import { createDecorator } from '../../instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js'; -import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames } from './voidSettingsTypes.js'; +import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, modelInfoOfDefaultNames, ModelInfo } from './voidSettingsTypes.js'; -const STORAGE_KEY = 'void.voidConfigStateIV' +const STORAGE_KEY = 'void.voidSettings' type SetSettingOfProviderFn = ( providerName: ProviderName, @@ -37,7 +37,7 @@ export type VoidSettingsState = { readonly settingsOfProvider: SettingsOfProvider; // optionsOfProvider readonly modelSelectionOfFeature: ModelSelectionOfFeature; // stateOfFeature - readonly _modelsList: ModelOption[] // computed based on the two above items + readonly _modelOptions: ModelOption[] // computed based on the two above items } @@ -48,19 +48,25 @@ export interface IVoidSettingsService { onDidChangeState: Event; setSettingOfProvider: SetSettingOfProviderFn; setModelSelectionOfFeature: SetModelSelectionOfFeature; + + setDefaultModels(providerName: ProviderName, modelNames: string[]): void; + toggleModelHidden(providerName: ProviderName, modelName: string): void; + addModel(providerName: ProviderName, modelName: string): void; + deleteModel(providerName: ProviderName, modelName: string): boolean; } -let _computeModelsList = (settingsOfProvider: SettingsOfProvider) => { - let modelsList: ModelOption[] = [] +let _computeModelOptions = (settingsOfProvider: SettingsOfProvider) => { + let modelOptions: ModelOption[] = [] for (const providerName of providerNames) { const providerConfig = settingsOfProvider[providerName] if (providerConfig.enabled !== 'true') continue - providerConfig.models?.forEach(modelName => { - modelsList.push({ text: `${modelName} (${providerName})`, value: { providerName, modelName } }) - }) + for (const { modelName, isHidden } of providerConfig.models) { + if (isHidden) continue + modelOptions.push({ text: `${modelName} (${providerName})`, value: { providerName, modelName } }) + } } - return modelsList + return modelOptions } @@ -68,7 +74,7 @@ const defaultState = () => { const d: VoidSettingsState = { settingsOfProvider: deepClone(defaultSettingsOfProvider), modelSelectionOfFeature: { 'Ctrl+L': null, 'Ctrl+K': null, 'Autocomplete': null }, - _modelsList: _computeModelsList(defaultSettingsOfProvider), + _modelOptions: _computeModelOptions(defaultSettingsOfProvider), // computed } return d } @@ -132,12 +138,12 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { // if changed models or enabled a provider, recompute models list const modelsListChanged = settingName === 'models' || settingName === 'enabled' - const newModelsList = modelsListChanged ? _computeModelsList(newSettingsOfProvider) : this.state._modelsList + const newModelsList = modelsListChanged ? _computeModelOptions(newSettingsOfProvider) : this.state._modelOptions const newState: VoidSettingsState = { modelSelectionOfFeature: newModelSelectionOfFeature, settingsOfProvider: newSettingsOfProvider, - _modelsList: newModelsList, + _modelOptions: newModelsList, } // this must go above this.setanythingelse() @@ -177,7 +183,6 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { if (options?.doNotApplyEffects) return - console.log('NEW STATE II', JSON.stringify(newState, null, 2)) await this._storeState() this._onDidChangeState.fire() @@ -185,6 +190,48 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { + setDefaultModels(providerName: ProviderName, newDefaultModelNames: string[]) { + const { models } = this.state.settingsOfProvider[providerName] + const newDefaultModels = modelInfoOfDefaultNames(newDefaultModelNames) + const newModels = [ + ...newDefaultModels, + ...models.filter(m => !m.isDefault), // keep any non-default models + ] + this.setSettingOfProvider(providerName, 'models', newModels) + } + toggleModelHidden(providerName: ProviderName, modelName: string) { + const { models } = this.state.settingsOfProvider[providerName] + const modelIdx = models.findIndex(m => m.modelName === modelName) + if (modelIdx === -1) return + const newModels: ModelInfo[] = [ + ...models.slice(0, modelIdx), + { ...models[modelIdx], isHidden: !models[modelIdx].isHidden }, + ...models.slice(modelIdx + 1, Infinity) + ] + this.setSettingOfProvider(providerName, 'models', newModels) + } + addModel(providerName: ProviderName, modelName: string) { + const { models } = this.state.settingsOfProvider[providerName] + const existingIdx = models.findIndex(m => m.modelName === modelName) + if (existingIdx !== -1) return // if exists, do nothing + const newModels = [ + ...models, + { modelName, isDefault: false, isHidden: false } + ] + this.setSettingOfProvider(providerName, 'models', newModels) + } + deleteModel(providerName: ProviderName, modelName: string): boolean { + const { models } = this.state.settingsOfProvider[providerName] + const delIdx = models.findIndex(m => m.modelName === modelName) + if (delIdx === -1) return false + const newModels = [ + ...models.slice(0, delIdx), // delete the idx + ...models.slice(delIdx + 1, Infinity) + ] + this.setSettingOfProvider(providerName, 'models', newModels) + return true + } + } diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index 810e9651..7c8b99c8 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -5,17 +5,30 @@ *--------------------------------------------------------------------------------------------*/ +export type ModelInfo = { + modelName: string, + isDefault: boolean, // whether or not it's a default for its provider + isHidden: boolean, // whether or not the user is hiding it +} + + +export const modelInfoOfDefaultNames = (modelNames: string[]): ModelInfo[] => { + const isHidden = modelNames.length >= 10 // hide all models if there are a ton of them, and make user enable them individually + return modelNames.map((modelName, i) => ({ modelName, isDefault: true, isHidden })) +} + // https://docs.anthropic.com/en/docs/about-claude/models -export const defaultAnthropicModels = [ +export const defaultAnthropicModels = modelInfoOfDefaultNames([ 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229', // 'claude-3-haiku-20240307', -] +]) -export const defaultOpenAIModels = [ +// https://platform.openai.com/docs/models/gp +export const defaultOpenAIModels = modelInfoOfDefaultNames([ 'o1-preview', 'o1-mini', 'gpt-4o', @@ -33,23 +46,24 @@ export const defaultOpenAIModels = [ // 'gpt-3.5-turbo-0125', // 'gpt-3.5-turbo', // 'gpt-3.5-turbo-1106', -] +]) -export const defaultGroqModels = [ +// https://console.groq.com/docs/models +export const defaultGroqModels = modelInfoOfDefaultNames([ "mixtral-8x7b-32768", "llama2-70b-4096", "gemma-7b-it" -] +]) -export const defaultGeminiModels = [ +export const defaultGeminiModels = modelInfoOfDefaultNames([ 'gemini-1.5-flash', 'gemini-1.5-pro', 'gemini-1.5-flash-8b', 'gemini-1.0-pro' -] +]) @@ -152,7 +166,7 @@ export type SettingsOfProvider = { { enabled: string, // 'true' | 'false' - models: string[], // if null, user can type in any string as a model + models: ModelInfo[], // if null, user can type in any string as a model }) } @@ -245,6 +259,16 @@ export const defaultSettingsOfProvider: SettingsOfProvider = { ...voidInitModelOptions.openAI, enabled: 'false', }, + gemini: { + ...voidProviderDefaults.gemini, + ...voidInitModelOptions.gemini, + enabled: 'false', + }, + groq: { + ...voidProviderDefaults.groq, + ...voidInitModelOptions.groq, + enabled: 'false', + }, ollama: { ...voidProviderDefaults.ollama, ...voidInitModelOptions.ollama, @@ -260,16 +284,6 @@ export const defaultSettingsOfProvider: SettingsOfProvider = { ...voidInitModelOptions.openAICompatible, enabled: 'false', }, - gemini: { - ...voidProviderDefaults.gemini, - ...voidInitModelOptions.gemini, - enabled: 'false', - }, - groq: { - ...voidProviderDefaults.groq, - ...voidInitModelOptions.groq, - enabled: 'false', - } } 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 399feff8..4d177c88 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 @@ -15,8 +15,6 @@ import { useSidebarState } from '../util/services.js'; import '../styles.css' import { SidebarThreadSelector } from './SidebarThreadSelector.js'; import { SidebarChat } from './SidebarChat.js'; -import { ModelSelectionSettings } from '../void-settings-tsx/ModelSelectionSettings.js'; -import { VoidProviderSettings } from '../void-settings-tsx/VoidProviderSettings.js'; import ErrorBoundary from './ErrorBoundary.js'; export const Sidebar = () => { 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 abb47d23..9180640a 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 @@ -22,7 +22,7 @@ import { OnError, ServiceSendLLMMessageParams } from '../../../../../../../platf import { getCmdKey } from '../../../helpers/getCmdKey.js' import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; import { VoidInputBox } from './inputs.js'; -import { ModelSelectionOfFeature } from '../void-settings-tsx/ModelSelectionSettings.js'; +import { ModelDropdown } from '../void-settings-tsx/ModelDropdown.js'; const IconX = ({ size, className = '' }: { size: number, className?: string }) => { @@ -487,7 +487,7 @@ export const SidebarChat = () => {
{/* submit options */}
- +
{/* submit / stop button */} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx similarity index 67% rename from src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx rename to src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx index 6c8c53c1..71c84014 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSelectionSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx @@ -10,14 +10,14 @@ import { VoidSelectBox } from '../sidebar-tsx/inputs.js' import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js' -const SelectBoxOfFeature = ({ featureName }: { featureName: FeatureName }) => { +const ModelSelectBox = ({ featureName }: { featureName: FeatureName }) => { const voidSettingsService = useService('settingsStateService') const settingsState = useSettingsState() let weChangedText = false return { if (weChangedText) return voidSettingsService.setModelSelectionOfFeature(featureName, newVal) @@ -25,7 +25,7 @@ const SelectBoxOfFeature = ({ featureName }: { featureName: FeatureName }) => { // we are responsible for setting the initial state here. always sync instance when state changes. onCreateInstance={useCallback((instance: SelectBox) => { const syncInstance = () => { - const modelsListRef = voidSettingsService.state._modelsList // as a ref + const modelsListRef = voidSettingsService.state._modelOptions // as a ref const settingsAtProvider = voidSettingsService.state.modelSelectionOfFeature[featureName] const selectionIdx = settingsAtProvider === null ? -1 : modelsListRef.findIndex(v => modelSelectionsEqual(v.value, settingsAtProvider)) weChangedText = true @@ -39,34 +39,17 @@ const SelectBoxOfFeature = ({ featureName }: { featureName: FeatureName }) => { /> } +const DummySelectBox = () => { + return { }} + onCreateInstance={() => { }} + /> +} -export const ModelSelectionOfFeature = ({ featureName }: { featureName: FeatureName }) => { +export const ModelDropdown = ({ featureName }: { featureName: FeatureName }) => { const settingsState = useSettingsState() return <> - {settingsState._modelsList.length === 0 ? 'Please add a provider!' : } + {settingsState._modelOptions.length === 0 ? : } } - -const RefreshModels = () => { - const refreshModelState = useRefreshModelState() - const refreshModelService = useService('refreshModelService') - - return <> - - {refreshModelState === 'loading' ? 'loading...' : '✅'} - -} - -export const ModelSelectionSettings = () => { - return <> - {featureNames.map(featureName => )} - - - -} - diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSettings.tsx new file mode 100644 index 00000000..efcce3c0 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSettings.tsx @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * 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 <> + + {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
+ {modelName} {isDefault ? '' : '(custom)'} + {providerName} + { settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? 'hidden' : '✅'} +
+ })} + + + +} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ProviderSettings.tsx similarity index 100% rename from src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidProviderSettings.tsx rename to src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ProviderSettings.tsx 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 new file mode 100644 index 00000000..4a2ac4e0 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -0,0 +1,17 @@ +import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' +import { ModelMenu } from './ModelSettings.js' +import { VoidProviderSettings } from './ProviderSettings.js' + + +export const Settings = () => { + return
+ + + + + + + + +
+} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidSettings.tsx deleted file mode 100644 index ecf60499..00000000 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/VoidSettings.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' -import { VoidProviderSettings } from './VoidProviderSettings.js' - - -export const VoidSettings = () => { - return <> - - - - -} - - diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/index.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/index.tsx index ee89ffab..ff596b24 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/index.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/index.tsx @@ -1,6 +1,6 @@ import { mountFnGenerator } from '../util/mountFnGenerator.js' -import { VoidSettings } from './VoidSettings.js' +import { Settings } from './Settings.js' -export const mountVoidSettings = mountFnGenerator(VoidSettings) +export const mountVoidSettings = mountFnGenerator(Settings) diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index f09b5e63..2d638bf5 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -26,11 +26,12 @@ import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextke 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'; // refer to preferences.contribution.ts keybindings editor -class VoidEditorInput extends EditorInput { +class VoidSettingsInput extends EditorInput { static readonly ID: string = 'workbench.input.void.settings'; @@ -44,7 +45,7 @@ class VoidEditorInput extends EditorInput { } override get typeId(): string { - return VoidEditorInput.ID; + return VoidSettingsInput.ID; } override getName(): string { @@ -54,7 +55,7 @@ class VoidEditorInput extends EditorInput { } -class MyCustomPane extends EditorPane { +class VoidSettingsPane extends EditorPane { static readonly ID = 'workbench.test.myCustomPane'; constructor( @@ -64,14 +65,18 @@ class MyCustomPane extends EditorPane { @IStorageService storageService: IStorageService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { - super(MyCustomPane.ID, group, telemetryService, themeService, storageService); + super(VoidSettingsPane.ID, group, telemetryService, themeService, storageService); } - protected createEditor(container: HTMLElement): void { + protected createEditor(parent: HTMLElement): void { + parent.style.overflow = 'auto' + parent.style.userSelect = 'text' + // gets set immediately this.instantiationService.invokeFunction(accessor => { const services = getReactServices(accessor) - mountVoidSettings(container, services); + const disposables: IDisposable[] | undefined = mountVoidSettings(parent, services); + disposables?.forEach(d => this._register(d)) }) } @@ -87,8 +92,8 @@ class MyCustomPane extends EditorPane { Registry.as(EditorExtensions.EditorPane).registerEditorPane( - EditorPaneDescriptor.create(MyCustomPane, MyCustomPane.ID, nls.localize('MyCustomPane', "CustomPane")), - [new SyncDescriptor(VoidEditorInput)] + EditorPaneDescriptor.create(VoidSettingsPane, VoidSettingsPane.ID, nls.localize('VoidSettingsPane', "Void Settings Pane")), + [new SyncDescriptor(VoidSettingsInput)] ); @@ -117,7 +122,7 @@ registerAction2(class extends Action2 { async run(accessor: ServicesAccessor): Promise { const editorService = accessor.get(IEditorService); const instantiationService = accessor.get(IInstantiationService); - const input = instantiationService.createInstance(VoidEditorInput); + const input = instantiationService.createInstance(VoidSettingsInput); await editorService.openEditor(input); } }) From 420fb1a18659d1a2cbebdb097cadff03f2759b5d Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 16 Dec 2024 01:21:55 -0800 Subject: [PATCH 12/21] icon is incredibly annoying --- .../contrib/void/browser/sidebarActions.ts | 19 ------------- .../contrib/void/browser/sidebarViewPane.ts | 28 +++++++++++++------ .../contrib/void/browser/void.contribution.ts | 6 ++-- .../contrib/void/browser/voidSettingsPane.ts | 12 +++++--- 4 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/sidebarActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts index 06f86b8e..3b544a83 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarActions.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts @@ -161,22 +161,3 @@ registerAction2(class extends Action2 { } }) -// registerAction2(class extends Action2 { -// constructor() { -// super({ -// id: 'void.viewSettings', -// title: 'Void Settings', -// icon: { id: 'settings-gear' }, -// menu: [{ id: MenuId.ViewTitle, group: 'navigation', when: ContextKeyExpr.equals('view', VOID_VIEW_ID), }] -// }); -// } -// async run(accessor: ServicesAccessor): Promise { -// const stateService = accessor.get(IVoidSidebarStateService) -// const metricsService = accessor.get(IMetricsService) - -// metricsService.capture('Chat Navigation', { type: 'Settings' }) - -// stateService.setState({ isHistoryOpen: false, currentTab: stateService.state.currentTab === 'settings' ? 'chat' : 'settings' }) -// stateService.fireBlurChat() -// } -// }) diff --git a/src/vs/workbench/contrib/void/browser/sidebarViewPane.ts b/src/vs/workbench/contrib/void/browser/sidebarViewPane.ts index 8b778474..87d0872c 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarViewPane.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarViewPane.ts @@ -37,6 +37,10 @@ import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { mountSidebar } from './react/out/sidebar-tsx/index.js'; import { getReactServices } from './helpers/reactServicesHelper.js'; +import { Codicon } from '../../../../base/common/codicons.js'; +// import { Orientation } from '../../../../base/browser/ui/sash/sash.js'; +// import { Codicon } from '../../../../base/common/codicons.js'; +// import { Codicon } from '../../../../base/common/codicons.js'; // compare against search.contribution.ts and debug.contribution.ts, scm.contribution.ts (source control) @@ -90,17 +94,22 @@ class VoidSidebarViewPane extends ViewPane { // called VIEWLET_ID in other places for some reason export const VOID_VIEW_CONTAINER_ID = 'workbench.view.void' -export const VOID_VIEW_ID = VOID_VIEW_CONTAINER_ID // simplicity +export const VOID_VIEW_ID = VOID_VIEW_CONTAINER_ID // Register view container const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); -const viewContainer = viewContainerRegistry.registerViewContainer({ +const container = viewContainerRegistry.registerViewContainer({ id: VOID_VIEW_CONTAINER_ID, - title: nls.localize2('void chat', 'Void Chat'), // this is used to say "Void" (Ctrl + L) + title: nls.localize2('voidContainer', 'Void'), // this is used to say "Void" (Ctrl + L) ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [VOID_VIEW_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }]), hideIfEmpty: false, - // icon: voidViewIcon, order: 1, + + icon: Codicon.symbolMethod, + alwaysUseContainerInfo: true, + // alwaysUseContainerInfo: true, + // icon: , + }, ViewContainerLocation.AuxiliaryBar, { doNotRegisterOpenCommand: true, isDefault: true }); @@ -111,16 +120,19 @@ viewsRegistry.registerViews([{ id: VOID_VIEW_ID, hideByDefault: false, // start open // containerIcon: voidViewIcon, - name: nls.localize2('chat', 'Chat'), // this says ... : CHAT + name: nls.localize2('voidChat', 'Chat'), // this says ... : CHAT ctorDescriptor: new SyncDescriptor(VoidSidebarViewPane), canToggleVisibility: false, - canMoveView: true, + canMoveView: false, // can't move this out of its container + + // singleViewPaneContainerTitle: 'hi', + // openCommandActionDescriptor: { - // id: viewContainer.id, + // id: VOID_VIEW_CONTAINER_ID, // keybindings: { // primary: KeyMod.CtrlCmd | KeyCode.KeyL, // }, // order: 1 // }, -}], viewContainer); +}], container); diff --git a/src/vs/workbench/contrib/void/browser/void.contribution.ts b/src/vs/workbench/contrib/void/browser/void.contribution.ts index ee12a345..b7d3f8be 100644 --- a/src/vs/workbench/contrib/void/browser/void.contribution.ts +++ b/src/vs/workbench/contrib/void/browser/void.contribution.ts @@ -18,8 +18,8 @@ import './threadHistoryService.js' // register Autocomplete import './autocompleteService.js' -// register css -import './media/void.css' - // settings pane import './voidSettingsPane.js' + +// register css +import './media/void.css' diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index 2d638bf5..f94e9aff 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -49,7 +49,11 @@ class VoidSettingsInput extends EditorInput { } override getName(): string { - return nls.localize('voidSettingsInputsName', "Void Settings"); + return nls.localize('voidSettingsInputsName', 'Void Settings'); + } + + override getIcon() { + return Codicon.checklist // symbol for the actual editor pane } } @@ -90,7 +94,7 @@ class VoidSettingsPane extends EditorPane { } - +// register Settings pane Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create(VoidSettingsPane, VoidSettingsPane.ID, nls.localize('VoidSettingsPane', "Void Settings Pane")), [new SyncDescriptor(VoidSettingsInput)] @@ -98,14 +102,14 @@ Registry.as(EditorExtensions.EditorPane).registerEditorPane const OPEN_VOID_SETTINGS_ID = 'workbench.action.openVoidSettings' -// Register the gear +// register the gear on the top right registerAction2(class extends Action2 { constructor() { super({ id: OPEN_VOID_SETTINGS_ID, title: nls.localize2('voidSettings', "Void: Settings"), f1: true, - icon: Codicon.gear, + icon: Codicon.settingsGear, menu: [ { id: MenuId.LayoutControlMenuSubmenu, From dfdf29fb367c39d3563c1a78dee064bcec7a5506 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 16 Dec 2024 15:05:49 -0800 Subject: [PATCH 13/21] rename --- .../void/browser/react/src/util/services.tsx | 6 +++- .../contrib/void/browser/sidebarActions.ts | 2 +- .../{sidebarViewPane.ts => sidebarPane.ts} | 30 +++++++++++++------ .../void/browser/sidebarStateService.ts | 2 +- .../contrib/void/browser/void.contribution.ts | 2 +- 5 files changed, 29 insertions(+), 13 deletions(-) rename src/vs/workbench/contrib/void/browser/{sidebarViewPane.ts => sidebarPane.ts} (88%) 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 70219c0b..240ec032 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 @@ -40,7 +40,11 @@ export const _registerServices = (services_: ReactServicesType) => { const disposables: IDisposable[] = [] - if (wasCalled) console.error(`⚠️ Void _registerServices was called again! It should only be called once.`) + // don't register services twice + if (wasCalled) { + return + // console.error(`⚠️ Void _registerServices was called again! It should only be called once.`) + } wasCalled = true services = services_ diff --git a/src/vs/workbench/contrib/void/browser/sidebarActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts index 3b544a83..5f9a9356 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarActions.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts @@ -18,7 +18,7 @@ import { IEditorService } from '../../../services/editor/common/editorService.js import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { IRange } from '../../../../editor/common/core/range.js'; import { ITextModel } from '../../../../editor/common/model.js'; -import { VOID_VIEW_ID } from './sidebarViewPane.js'; +import { VOID_VIEW_ID } from './sidebarPane.js'; import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; import { ISidebarStateService } from './sidebarStateService.js'; diff --git a/src/vs/workbench/contrib/void/browser/sidebarViewPane.ts b/src/vs/workbench/contrib/void/browser/sidebarPane.ts similarity index 88% rename from src/vs/workbench/contrib/void/browser/sidebarViewPane.ts rename to src/vs/workbench/contrib/void/browser/sidebarPane.ts index 87d0872c..974b0e11 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarViewPane.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarPane.ts @@ -38,6 +38,7 @@ import { mountSidebar } from './react/out/sidebar-tsx/index.js'; import { getReactServices } from './helpers/reactServicesHelper.js'; import { Codicon } from '../../../../base/common/codicons.js'; +import { Orientation } from '../../../../base/browser/ui/sash/sash.js'; // import { Orientation } from '../../../../base/browser/ui/sash/sash.js'; // import { Codicon } from '../../../../base/common/codicons.js'; // import { Codicon } from '../../../../base/common/codicons.js'; @@ -47,7 +48,7 @@ import { Codicon } from '../../../../base/common/codicons.js'; // ---------- Define viewpane ---------- -class VoidSidebarViewPane extends ViewPane { +class SidebarViewPane extends ViewPane { constructor( options: IViewPaneOptions, @@ -83,6 +84,14 @@ class VoidSidebarViewPane extends ViewPane { }); } + override layoutBody(height: number, width: number): void { + super.layoutBody(height, width) + this.element.style.height = `${height}px` + this.element.style.width = `${width}px` + + + } + } @@ -100,15 +109,17 @@ export const VOID_VIEW_ID = VOID_VIEW_CONTAINER_ID const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const container = viewContainerRegistry.registerViewContainer({ id: VOID_VIEW_CONTAINER_ID, - title: nls.localize2('voidContainer', 'Void'), // this is used to say "Void" (Ctrl + L) - ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [VOID_VIEW_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }]), + title: nls.localize2('voidContainer', 'Void Chat'), // this is used to say "Void" (Ctrl + L) + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [VOID_VIEW_CONTAINER_ID, { + mergeViewWithContainerWhenSingleView: true, + orientation: Orientation.HORIZONTAL, + }]), hideIfEmpty: false, order: 1, + rejectAddedViews: true, icon: Codicon.symbolMethod, - alwaysUseContainerInfo: true, - // alwaysUseContainerInfo: true, - // icon: , + }, ViewContainerLocation.AuxiliaryBar, { doNotRegisterOpenCommand: true, isDefault: true }); @@ -120,11 +131,12 @@ viewsRegistry.registerViews([{ id: VOID_VIEW_ID, hideByDefault: false, // start open // containerIcon: voidViewIcon, - name: nls.localize2('voidChat', 'Chat'), // this says ... : CHAT - ctorDescriptor: new SyncDescriptor(VoidSidebarViewPane), + name: nls.localize2('voidChat', ''), // this says ... : CHAT + ctorDescriptor: new SyncDescriptor(SidebarViewPane), canToggleVisibility: false, canMoveView: false, // can't move this out of its container - + weight: 80, + order: 1, // singleViewPaneContainerTitle: 'hi', // openCommandActionDescriptor: { diff --git a/src/vs/workbench/contrib/void/browser/sidebarStateService.ts b/src/vs/workbench/contrib/void/browser/sidebarStateService.ts index 0cab1180..683a3ed4 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarStateService.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarStateService.ts @@ -3,7 +3,7 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; -import { VOID_VIEW_CONTAINER_ID, VOID_VIEW_ID } from './sidebarViewPane.js'; +import { VOID_VIEW_CONTAINER_ID, VOID_VIEW_ID } from './sidebarPane.js'; // service that manages sidebar's state diff --git a/src/vs/workbench/contrib/void/browser/void.contribution.ts b/src/vs/workbench/contrib/void/browser/void.contribution.ts index b7d3f8be..a67594a7 100644 --- a/src/vs/workbench/contrib/void/browser/void.contribution.ts +++ b/src/vs/workbench/contrib/void/browser/void.contribution.ts @@ -9,7 +9,7 @@ import './inlineDiffsService.js' // register Sidebar pane, state, actions (keybinds, menus) import './sidebarActions.js' -import './sidebarViewPane.js' +import './sidebarPane.js' import './sidebarStateService.js' // register Thread History From c485d2c478b05375d0b6c7fbdb55920e990ab597 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 16 Dec 2024 17:43:07 -0800 Subject: [PATCH 14/21] react updates --- .../react/src/sidebar-tsx/SidebarChat.tsx | 4 +- .../src/{sidebar-tsx => util}/inputs.tsx | 24 ++- .../void/browser/react/src/util/services.tsx | 3 + .../src/void-settings-tsx/ModelDropdown.tsx | 2 +- .../src/void-settings-tsx/ModelSettings.tsx | 58 ------- .../void-settings-tsx/ProviderSettings.tsx | 74 --------- .../react/src/void-settings-tsx/Settings.tsx | 143 ++++++++++++++++-- .../contrib/void/browser/sidebarPane.ts | 2 +- .../contrib/void/browser/voidSettingsPane.ts | 2 +- 9 files changed, 164 insertions(+), 148 deletions(-) rename src/vs/workbench/contrib/void/browser/react/src/{sidebar-tsx => util}/inputs.tsx (87%) delete mode 100644 src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSettings.tsx delete mode 100644 src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ProviderSettings.tsx 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 9180640a..e8ab57ed 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,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 (
({ ctor, propsFn, dispose, onCreateInstance } +export const WidgetComponent = ({ 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(null); @@ -31,7 +34,7 @@ export const WidgetComponent = ({ ctor, prop } }, [ctor, propsFn, dispose, onCreateInstance, containerRef]) - return
+ return
{children}
} @@ -142,6 +145,21 @@ export const VoidSelectBox = ({ onChangeSelection, onCreateInstance, selectB }; +export const VoidScrollableElt = ({ options, children }: { options: ScrollableElementCreationOptions, children: React.ReactNode }) => { + + return { + return [container, options] as const; + }, [options])} + onCreateInstance={useCallback(() => { return [] }, [])} + dispose={useCallback((instance: DomScrollableElement) => { + console.log('calling dispose!!!!') + // instance.dispose(); + // instance.getDomNode().remove() + }, [])} + >abcdefg +} // export const VoidSelectBox = ({ onChangeSelection, initVal, selectBoxRef, options }: { // initVal: T; 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 240ec032..0b5b85f4 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 @@ -136,3 +136,6 @@ export const useRefreshModelState = () => { }, [ss]) return s } + + + diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx index 71c84014..08cbd52e 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx @@ -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' diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSettings.tsx deleted file mode 100644 index efcce3c0..00000000 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelSettings.tsx +++ /dev/null @@ -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 <> - - {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
- {modelName} {isDefault ? '' : '(custom)'} - {providerName} - { settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? 'hidden' : '✅'} -
- })} - - - -} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ProviderSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ProviderSettings.tsx deleted file mode 100644 index 0b91bf93..00000000 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ProviderSettings.tsx +++ /dev/null @@ -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 <> - - { - 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} - /> - - -} - - -const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => { - const voidSettingsState = useSettingsState() - const { models, ...others } = voidSettingsState.settingsOfProvider[providerName] - - return <> -

{titleOfProviderName(providerName)}

- {/* settings besides models (e.g. api key) */} - {Object.keys(others).map((sName, i) => { - const settingName = sName as keyof typeof others - return - })} - -} - - -export const VoidProviderSettings = () => { - - return <> - {providerNames.map(providerName => - - )} - -} 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 4a2ac4e0..d4eaa679 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 @@ -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 <> + + {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
+ {modelName} {isDefault ? '' : '(custom)'} + {providerName} + { settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? 'hidden' : '✅'} +
+ })} + +} + + + +// providers + +const ProviderSetting = ({ providerName, settingName }: { providerName: ProviderName, settingName: SettingName }) => { + + const { title, type, placeholder } = displayInfoOfSettingName(providerName, settingName) + const voidSettingsService = useService('settingsStateService') + + + let weChangedTextRef = false + + return <> + + { + 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} + /> + + +} + +const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => { + const voidSettingsState = useSettingsState() + const { models, ...others } = voidSettingsState.settingsOfProvider[providerName] + + return <> +

{titleOfProviderName(providerName)}

+ {/* settings besides models (e.g. api key) */} + {Object.keys(others).map((sName, i) => { + const settingName = sName as keyof typeof others + return + })} + +} + + +export const VoidProviderSettings = () => { + + return <> + {providerNames.map(providerName => + + )} + +} + + + +// full settings + export const Settings = () => { - return
- - - + return
+
- - - +
+ + + + +
+ + + + +
} diff --git a/src/vs/workbench/contrib/void/browser/sidebarPane.ts b/src/vs/workbench/contrib/void/browser/sidebarPane.ts index 974b0e11..576850fa 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarPane.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarPane.ts @@ -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 diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index f94e9aff..73b42cc0 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -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 From c44497510ba69066746aff0c4626ee93d0414000 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 16 Dec 2024 18:57:28 -0800 Subject: [PATCH 15/21] add responsive dark mode --- .../platform/void/common/voidSettingsTypes.ts | 17 +++----- .../browser/react/src/sidebar-tsx/Sidebar.tsx | 8 ++-- .../react/src/sidebar-tsx/SidebarChat.tsx | 3 +- .../void/browser/react/src/util/inputs.tsx | 41 ++++++++++++------- .../react/src/util/mountFnGenerator.tsx | 5 ++- .../void/browser/react/src/util/services.tsx | 29 ++++++++++++- .../react/src/void-settings-tsx/Settings.tsx | 10 ++--- .../void/browser/react/tailwind.config.js | 1 + .../contrib/void/browser/voidSettingsPane.ts | 4 ++ 9 files changed, 77 insertions(+), 41 deletions(-) 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 } + } From a7aab9cf8608e080e68271c7872e9cd0074cfe9e Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 16 Dec 2024 21:49:03 -0800 Subject: [PATCH 16/21] some UI for models! --- .../void/common/refreshModelService.ts | 2 +- .../void/common/voidSettingsService.ts | 4 +- .../platform/void/common/voidSettingsTypes.ts | 137 ++++++++++-------- .../src/void-settings-tsx/ModelDropdown.tsx | 1 - .../react/src/void-settings-tsx/Settings.tsx | 134 ++++++++++++++--- 5 files changed, 189 insertions(+), 89 deletions(-) diff --git a/src/vs/platform/void/common/refreshModelService.ts b/src/vs/platform/void/common/refreshModelService.ts index b9b44e83..1f6b08f3 100644 --- a/src/vs/platform/void/common/refreshModelService.ts +++ b/src/vs/platform/void/common/refreshModelService.ts @@ -75,7 +75,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ this._cancelTimeout() // if ollama is disabled, obivously done - if (this.voidSettingsService.state.settingsOfProvider.ollama.enabled !== 'true') { + if (!this.voidSettingsService.state.settingsOfProvider.ollama.enabled) { this._setState('done') return } diff --git a/src/vs/platform/void/common/voidSettingsService.ts b/src/vs/platform/void/common/voidSettingsService.ts index a11e3f60..ab9d28f8 100644 --- a/src/vs/platform/void/common/voidSettingsService.ts +++ b/src/vs/platform/void/common/voidSettingsService.ts @@ -13,7 +13,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../storage/comm import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, modelInfoOfDefaultNames, ModelInfo } from './voidSettingsTypes.js'; -const STORAGE_KEY = 'void.voidSettings' +const STORAGE_KEY = 'void.voidSettingsI' type SetSettingOfProviderFn = ( providerName: ProviderName, @@ -60,7 +60,7 @@ let _computeModelOptions = (settingsOfProvider: SettingsOfProvider) => { let modelOptions: ModelOption[] = [] for (const providerName of providerNames) { const providerConfig = settingsOfProvider[providerName] - if (providerConfig.enabled !== 'true') continue + if (!providerConfig.enabled) continue // if disabled, don't display model options for (const { modelName, isHidden } of providerConfig.models) { if (isHidden) continue modelOptions.push({ text: `${modelName} (${providerName})`, value: { providerName, modelName } }) diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index 689888cb..6de2f47f 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -5,6 +5,8 @@ *--------------------------------------------------------------------------------------------*/ + + export type ModelInfo = { modelName: string, isDefault: boolean, // whether or not it's a default for its provider @@ -90,16 +92,11 @@ export const anthropicMaxPossibleTokens = (modelName: string) => { } -// export const dummyModelData = { -// anthropic: ['claude 3.5'], -// openAI: ['gpt 4o'], -// ollama: ['llama 3.2', 'codestral'], -// openRouter: ['qwen 2.5'], -// } +type UnionOfKeys = T extends T ? keyof T : never; -export const voidProviderDefaults = { +export const customProviderSettingsDefaults = { anthropic: { apiKey: '', }, @@ -124,57 +121,30 @@ export const voidProviderDefaults = { } } as const +export type ProviderName = keyof typeof customProviderSettingsDefaults +export const providerNames = Object.keys(customProviderSettingsDefaults) as ProviderName[] -export const voidInitModelOptions = { - anthropic: { - models: defaultAnthropicModels, - }, - openAI: { - models: defaultOpenAIModels, - }, - ollama: { - models: [], - }, - openRouter: { - models: [], // any string - }, - openAICompatible: { - models: [], - }, - gemini: { - models: defaultGeminiModels, - }, - groq: { - models: defaultGroqModels, - }, + +type CustomSettingName = UnionOfKeys + +type CustomProviderSettings = { + [k in CustomSettingName]: k extends keyof typeof customProviderSettingsDefaults[providerName] ? string : undefined } +type CommonProviderSettings = { + enabled: boolean, + models: ModelInfo[], // if null, user can type in any string as a model +} +type SettingsForProvider = CustomProviderSettings & CommonProviderSettings -export type ProviderName = keyof typeof voidProviderDefaults -export const providerNames = Object.keys(voidProviderDefaults) as ProviderName[] - - - -// state +// part of state export type SettingsOfProvider = { - [providerName in ProviderName]: ( - { - [optionName in keyof typeof voidProviderDefaults[providerName]]: string - } - & - { - enabled: string, // 'true' | 'false' - - models: ModelInfo[], // if null, user can type in any string as a model - }) + [providerName in ProviderName]: SettingsForProvider } -type UnionOfKeys = T extends T ? keyof T : never; - -export type SettingName = UnionOfKeys - +export type SettingName = keyof SettingsForProvider @@ -219,7 +189,7 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName title: providerName === 'ollama' ? 'Your Ollama endpoint' : providerName === 'openAICompatible' ? 'baseURL' // (do not include /chat/completions) : '(never)', - placeholder: providerName === 'ollama' ? voidProviderDefaults.ollama.endpoint + placeholder: providerName === 'ollama' ? customProviderSettingsDefaults.ollama.endpoint : providerName === 'openAICompatible' ? 'https://my-website.com/v1' : '(never)', } @@ -242,42 +212,81 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName } + + +const defaultCustomSettings: Record = { + apiKey: undefined, + endpoint: undefined, +} + +export const voidInitModelOptions = { + anthropic: { + models: defaultAnthropicModels, + }, + openAI: { + models: defaultOpenAIModels, + }, + ollama: { + models: [], + }, + openRouter: { + models: [], // any string + }, + openAICompatible: { + models: [], + }, + gemini: { + models: defaultGeminiModels, + }, + groq: { + models: defaultGroqModels, + }, +} + + // used when waiting and for a type reference export const defaultSettingsOfProvider: SettingsOfProvider = { anthropic: { - ...voidProviderDefaults.anthropic, + ...defaultCustomSettings, + ...customProviderSettingsDefaults.anthropic, ...voidInitModelOptions.anthropic, - enabled: 'false', + enabled: false, }, openAI: { - ...voidProviderDefaults.openAI, + ...defaultCustomSettings, + ...customProviderSettingsDefaults.openAI, ...voidInitModelOptions.openAI, - enabled: 'false', + enabled: false, }, gemini: { - ...voidProviderDefaults.gemini, + ...defaultCustomSettings, + ...customProviderSettingsDefaults.gemini, ...voidInitModelOptions.gemini, - enabled: 'false', + enabled: false, }, groq: { - ...voidProviderDefaults.groq, + ...defaultCustomSettings, + ...customProviderSettingsDefaults.groq, ...voidInitModelOptions.groq, - enabled: 'false', + enabled: false, }, ollama: { - ...voidProviderDefaults.ollama, + ...defaultCustomSettings, + ...customProviderSettingsDefaults.ollama, ...voidInitModelOptions.ollama, - enabled: 'false', + enabled: false, }, openRouter: { - ...voidProviderDefaults.openRouter, + ...defaultCustomSettings, + ...customProviderSettingsDefaults.openRouter, ...voidInitModelOptions.openRouter, - enabled: 'false', + enabled: false, }, openAICompatible: { - ...voidProviderDefaults.openAICompatible, + ...defaultCustomSettings, + ...customProviderSettingsDefaults.openAICompatible, ...voidInitModelOptions.openAICompatible, - enabled: 'false', + enabled: false, }, } diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx index 08cbd52e..59abfcde 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx @@ -43,7 +43,6 @@ const DummySelectBox = () => { return { }} - onCreateInstance={() => { }} /> } 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 6c5648de..60f8501b 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 @@ -1,8 +1,8 @@ -import React, { useCallback } from 'react' +import React, { useCallback, useRef, useState } 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 { VoidInputBox } from '../util/inputs.js' +import { VoidInputBox, VoidSelectBox } from '../util/inputs.js' import { useIsDark, useRefreshModelState, useService, useSettingsState } from '../util/services.js' @@ -15,7 +15,7 @@ const RefreshableModels = () => { const refreshModelState = useRefreshModelState() const refreshModelService = useService('refreshModelService') - if (settingsState.settingsOfProvider.ollama.enabled !== 'true') + if (!settingsState.settingsOfProvider.ollama.enabled) return null return <> @@ -26,8 +26,82 @@ const RefreshableModels = () => { +const AddModelMenu = ({ onSubmit }: { onSubmit: () => void }) => { + const settingsStateService = useService('settingsStateService') + const settingsState = useSettingsState() -export const ModelMenu = () => { + const providerNameRef = useRef(null) + const modelNameRef = useRef(null) + + const [errorString, setErrorString] = useState('') + + const providerOptions = providerNames.map(providerName => ({ text: titleOfProviderName(providerName), value: providerName })) + + return
+ {/* model */} +
+ { modelNameRef.current = modelName }} + multiline={false} + /> +
+ + {/* provider */} +
+ { providerNameRef.current = providerOptions[0].value }} // initialize state + onChangeSelection={(providerName: ProviderName) => { console.log('selecting provider', providerName); providerNameRef.current = providerName }} + options={providerOptions} + /> +
+ + {/* button */} +
+ +
+ + {!errorString ? null : <> + {errorString} + } +
+ +} + +const AddModelButton = () => { + const [open, setOpen] = useState(false) + + return <> + {open ? + { setOpen(false) }} /> + : + } + +} + + +export const ModelDump = () => { const settingsStateService = useService('settingsStateService') const settingsState = useSettingsState() @@ -36,21 +110,30 @@ export const ModelMenu = () => { const modelDump: (ModelInfo & { providerName: ProviderName })[] = [] for (let providerName of providerNames) { const providerSettings = settingsState.settingsOfProvider[providerName] - if (providerSettings.enabled !== 'true') continue + if (!providerSettings.enabled) continue modelDump.push(...providerSettings.models.map(model => ({ ...model, providerName }))) } - return <> + return
{modelDump.map(m => { const { isHidden, isDefault, modelName, providerName } = m - return
- {modelName} {isDefault ? '' : '(custom)'} - {providerName} - { settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? 'hidden' : '✅'} + return
+ {/* left part is width:full */} +
+ {`${modelName} (${providerName})`} +
+ {/* right part is anything that fits */} +
+ {isDefault ? '' : '(custom model)'} +
{ settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? '❌' : '✅'}
+
+ {isDefault ? null : } +
+
})} - +
} @@ -78,7 +161,7 @@ const ProviderSetting = ({ providerName, settingName }: { providerName: Provider onCreateInstance={useCallback((instance: InputBox) => { const syncInstance = () => { const settingsAtProvider = voidSettingsService.state.settingsOfProvider[providerName]; - const stateVal = settingsAtProvider[settingName as keyof typeof settingsAtProvider] + const stateVal = settingsAtProvider[settingName as SettingName] // console.log('SYNCING TO', providerName, settingName, stateVal) weChangedTextRef = true instance.value = stateVal as string @@ -96,10 +179,16 @@ const ProviderSetting = ({ providerName, settingName }: { providerName: Provider const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => { const voidSettingsState = useSettingsState() - const { models, ...others } = voidSettingsState.settingsOfProvider[providerName] + const voidSettingsService = useService('settingsStateService') + + const { models, enabled, ...others } = voidSettingsState.settingsOfProvider[providerName] return <> -

{titleOfProviderName(providerName)}

+ +
+

{titleOfProviderName(providerName)}

+ { voidSettingsService.setSettingOfProvider(providerName, 'enabled', !enabled) }}>{enabled ? '✅' : '❌'} +
{/* settings besides models (e.g. api key) */} {Object.keys(others).map((sName, i) => { const settingName = sName as keyof typeof others @@ -123,19 +212,22 @@ export const VoidProviderSettings = () => { export const Settings = () => { const isDark = useIsDark() - return
-
+ return
+
+

Models

- + + -
- - - +

Providers

+ + + +
From a7c4a1b179afdaad3ca1234d6ebfa97deb1efdf2 Mon Sep 17 00:00:00 2001 From: mp Date: Mon, 16 Dec 2024 23:01:41 -0800 Subject: [PATCH 17/21] input box style --- .../react/src/sidebar-tsx/SidebarChat.tsx | 172 ++++++++++-------- .../void/browser/react/src/util/inputs.tsx | 14 +- 2 files changed, 101 insertions(+), 85 deletions(-) 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 924f75a6..8fd4be9b 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 @@ -102,7 +102,7 @@ const ScrollToBottomContainer = ({ children, className, style }: { children: Rea const isBottom = Math.abs( div.scrollHeight - div.clientHeight - div.scrollTop - ) < 1; + ) < 4; setIsAtBottom(isBottom); }; @@ -159,71 +159,66 @@ export const SelectedFiles = ( return ( !!selections && selections.length !== 0 && ( -
- {selections.map((selection, i) => ( - - {/* selected file summary */} -
+ {selections.map((selection, i) => { + + const showSelectionText = selection.selectionStr && selectionIsOpened[i] + + return ( +
+ {/* selection summary */} +
{ - setSelectionIsOpened(s => { - const newS = [...s] - newS[i] = !newS[i] - return newS - }); - }} - > - - - {/* file name */} - {getBasename(selection.fileURI.fsPath)} - {/* selection range */} - {selection.selectionStr !== null ? ` (${selection.range.startLineNumber}-${selection.range.endLineNumber})` : ''} - - - {/* type of selection */} - {selection.selectionStr !== null ? 'Selection' : 'File'} - - {/* X button */} - {type === 'staging' && // hoveredIdx === i - { - e.stopPropagation(); - if (type !== 'staging') return; - setStaging([...selections.slice(0, i), ...selections.slice(i + 1)]) - setSelectionIsOpened(o => [...o.slice(0, i), ...o.slice(i + 1)]) - }} - > - + onClick={() => { + setSelectionIsOpened(s => { + const newS = [...s] + newS[i] = !newS[i] + return newS + }); + }} + > + + {/* file name */} + {getBasename(selection.fileURI.fsPath)} + {/* selection range */} + {selection.selectionStr !== null ? ` (${selection.range.startLineNumber}-${selection.range.endLineNumber})` : ''} + + {/* type of selection */} + {selection.selectionStr !== null ? 'Selection' : 'File'} + + {/* X button */} + {type === 'staging' && // hoveredIdx === i + { + e.stopPropagation(); + if (type !== 'staging') return; + setStaging([...selections.slice(0, i), ...selections.slice(i + 1)]) + setSelectionIsOpened(o => [...o.slice(0, i), ...o.slice(i + 1)]) + }} + > + + + } +
+ {/* selection text */} + {showSelectionText && +
+ +
}
- {/* selection full text */} - {selection.selectionStr && selectionIsOpened[i] && - { // clear the selection string but keep the file - // // setStaging([...selections.slice(0, i), { ...selection, selectionStr: null }, ...selections.slice(i + 1, Infinity)]) - // // }} - // onClick={() => { - // if (type !== 'staging') return - // setStaging([...selections.slice(0, i), ...selections.slice(i + 1, Infinity)]) - // }} - // className="btn btn-secondary btn-sm border border-vscode-input-border rounded" - // >Remove - // )} - /> - } - - )) - } + ) + })}
) ) @@ -296,7 +291,7 @@ export const SidebarChat = () => { // state of current message const [instructions, setInstructions] = useState('') // the user's instructions const isDisabled = !instructions.trim() - const [formHeight, setFormHeight] = useState(0) + const [formHeight, setFormHeight] = useState(0) // TODO should use resize observer instead const [sidebarHeight, setSidebarHeight] = useState(0) const onChangeText = useCallback((newStr: string) => { setInstructions(newStr) }, [setInstructions]) @@ -405,8 +400,7 @@ export const SidebarChat = () => { const previousMessages = currentThread?.messages ?? [] - - const [_test, _setTest] = useState([]) + // const [_test_messages, _set_test_messages] = useState([]) return
{ if (ref) { setSidebarHeight(ref.clientHeight); } }} @@ -414,7 +408,7 @@ export const SidebarChat = () => { > {/* previous messages */} {previousMessages.map((message, i) => )} @@ -422,12 +416,11 @@ export const SidebarChat = () => { {/* message stream */} - - {_test.map((_, i) =>
div {i}
)} -
{`totalHeight: ${sidebarHeight - formHeight - 30}`}
-
{`sidebarHeight: ${sidebarHeight}`}
-
{`formHeight: ${formHeight}`}
- + {/* {_test_messages.map((_, i) =>
div {i}
)} +
{`totalHeight: ${sidebarHeight - formHeight - 30}`}
+
{`sidebarHeight: ${sidebarHeight}`}
+
{`formHeight: ${formHeight}`}
+ */}
@@ -454,9 +447,14 @@ export const SidebarChat = () => { console.log('submit!') onSubmit(e) }} + onClick={(e) => { + if (e.currentTarget === e.target) { + inputBoxRef.current?.focus() + } + }} > {/* top row */} -
+ <> {/* selections */} {(selections && selections.length !== 0) && @@ -471,10 +469,24 @@ export const SidebarChat = () => { showDismiss={true} /> } -
+ {/* middle row */} -
+
`@@[&_textarea]:!void-${style}`) // apply styles to ancestor input and textarea elements + // .join(' ') + + // ` outline-none` + // .split(' ') + // .map(style => `@@[&_div.monaco-inputbox]:!void-${style}`) // apply styles to ancestor input and textarea elements + // .join(' '); + `@@[&_textarea]:!void-bg-transparent @@[&_textarea]:!void-outline-none @@[&_textarea]:!void-text-vscode-input-fg @@[&_textarea]:!void-min-h-[81px] @@[&_textarea]:!void-max-h-[500px]@@[&_div.monaco-inputbox]:!void- @@[&_div.monaco-inputbox]:!void-outline-none` + } + > + {/* text input */} {
{/* bottom row */} -
+
{/* submit options */}
@@ -495,7 +509,7 @@ export const SidebarChat = () => { {isLoading ? // stop button }
-
-
+
+
} 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 769056ed..adeb6930 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,9 +3,9 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { useService } from './services.js'; -import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { useService } from '../util/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'; @@ -39,15 +39,15 @@ export const WidgetComponent = ({ ctor, prop -export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, placeholder, multiline }: { +export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, placeholder, multiline, styles }: { onChangeText: (value: string) => void; + styles?: Partial, onCreateInstance?: (instance: InputBox) => void | IDisposable[]; inputBoxRef?: { current: InputBox | null }; placeholder: string; multiline: boolean; }) => { - const contextViewProvider = useService('contextViewService'); return Date: Mon, 16 Dec 2024 23:07:59 -0800 Subject: [PATCH 18/21] settings UI --- .../react/src/void-settings-tsx/Settings.tsx | 157 +++++++++++------- 1 file changed, 98 insertions(+), 59 deletions(-) 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 60f8501b..8144c76f 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 @@ -37,55 +37,58 @@ const AddModelMenu = ({ onSubmit }: { onSubmit: () => void }) => { const providerOptions = providerNames.map(providerName => ({ text: titleOfProviderName(providerName), value: providerName })) - return
- {/* model */} -
- { modelNameRef.current = modelName }} - multiline={false} - /> + return <> +
+ {/* model */} +
+ { modelNameRef.current = modelName }} + multiline={false} + /> +
+ + {/* provider */} +
+ { providerNameRef.current = providerOptions[0].value }} // initialize state + onChangeSelection={(providerName: ProviderName) => { console.log('selecting provider', providerName); providerNameRef.current = providerName }} + options={providerOptions} + /> +
+ + {/* button */} +
+ +
- {/* provider */} -
- { providerNameRef.current = providerOptions[0].value }} // initialize state - onChangeSelection={(providerName: ProviderName) => { console.log('selecting provider', providerName); providerNameRef.current = providerName }} - options={providerOptions} - /> -
- - {/* button */} -
- -
- - {!errorString ? null : <> + {!errorString ? null :
{errorString} - } -
+
} + } @@ -114,7 +117,7 @@ export const ModelDump = () => { modelDump.push(...providerSettings.models.map(model => ({ ...model, providerName }))) } - return
+ return
{modelDump.map(m => { const { isHidden, isDefault, modelName, providerName } = m @@ -212,21 +215,57 @@ export const VoidProviderSettings = () => { export const Settings = () => { const isDark = useIsDark() + + const [tab, setTab] = useState<'models' | 'providers'>('models') + return
-
+
-
-

Models

- - - - - +
+ +

Void Settings

+ + {/* separator */} +
+ +
+ + {/* tabs */} +
+ + +
+ + {/* separator */} +
+ + + {/* content */} +
+ +
+

Models

+ + + + + +
+ +
+

{ setTab('providers') }}>Providers

+ + + +
+ +
+
-

Providers

- - -
From 7752278720f99ae377824fa308110b6069d9a0a1 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 17 Dec 2024 01:33:33 -0800 Subject: [PATCH 19/21] settings styles --- .../react/src/sidebar-tsx/SidebarChat.tsx | 4 +- .../void/browser/react/src/util/inputs.tsx | 8 ++-- .../react/src/void-settings-tsx/Settings.tsx | 25 +++++------ .../contrib/void/browser/voidSettingsPane.ts | 41 +++++++++++++------ 4 files changed, 48 insertions(+), 30 deletions(-) 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 8fd4be9b..046dc5d9 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 @@ -498,10 +498,10 @@ export const SidebarChat = () => { {/* bottom row */}
{/* submit options */} -
+
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 adeb6930..504b88d2 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 @@ -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 = ({ ctor, propsFn, dispose, onCreateInstance, children } +export const WidgetComponent = ({ 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(null); @@ -34,7 +35,7 @@ export const WidgetComponent = ({ ctor, prop } }, [ctor, propsFn, dispose, onCreateInstance, containerRef]) - return
{children}
+ return
{children}
} @@ -105,6 +106,7 @@ export const VoidSelectBox = ({ onChangeSelection, onCreateInstance, selectB let containerRef = useRef(null); return { containerRef.current = container 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 8144c76f..2edc0137 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 @@ -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
{refreshModelState === 'loading' ? 'loading...' : 'good!'} - +
} @@ -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
{modelDump.map(m => { - const { isHidden, isDefault, modelName, providerName } = m + const { isHidden, isDefault, modelName, providerName, providerEnabled } = m return
{/* left part is width:full */} @@ -129,9 +130,9 @@ export const ModelDump = () => { {/* right part is anything that fits */}
{isDefault ? '' : '(custom model)'} -
{ settingsStateService.toggleModelHidden(providerName, modelName) }}>{isHidden ? '❌' : '✅'}
-
- {isDefault ? null : } + +
+ {isDefault ? null : }
@@ -190,7 +191,7 @@ const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) =

{titleOfProviderName(providerName)}

- { voidSettingsService.setSettingOfProvider(providerName, 'enabled', !enabled) }}>{enabled ? '✅' : '❌'} +
{/* settings besides models (e.g. api key) */} {Object.keys(others).map((sName, i) => { @@ -248,7 +249,7 @@ export const Settings = () => {
-

Models

+

Models

@@ -257,7 +258,7 @@ export const Settings = () => {
-

{ setTab('providers') }}>Providers

+

{ setTab('providers') }}>Providers

diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index 6d522842..7974fd4c 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -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(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create(VoidSettingsPane, VoidSettingsPane.ID, nls.localize('VoidSettingsPane', "Void Settings Pane")), From d618223289841469bc0f7a6f74440e6b71256f98 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 17 Dec 2024 01:55:32 -0800 Subject: [PATCH 20/21] misc improvements. need to add max h on thread histroy --- .../react/src/sidebar-tsx/SidebarThreadSelector.tsx | 4 ++-- .../browser/react/src/void-settings-tsx/Settings.tsx | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx index 3c3e00d3..39896d96 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx @@ -27,7 +27,7 @@ export const SidebarThreadSelector = () => { const sortedThreadIds = Object.keys(allThreads ?? {}).sort((threadId1, threadId2) => allThreads![threadId1].lastModified > allThreads![threadId2].lastModified ? -1 : 1) return ( -
+
{/* X button at top right */}
@@ -49,7 +49,7 @@ export const SidebarThreadSelector = () => {
{/* a list of all the past threads */} -
+
{sortedThreadIds.map((threadId) => { if (!allThreads) return <>Error: Threads not found. 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 2edc0137..c1df63bb 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 @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useRef, useState } 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' @@ -36,7 +36,7 @@ const AddModelMenu = ({ onSubmit }: { onSubmit: () => void }) => { const [errorString, setErrorString] = useState('') - const providerOptions = providerNames.map(providerName => ({ text: titleOfProviderName(providerName), value: providerName })) + const providerOptions = useMemo(() => providerNames.map(providerName => ({ text: titleOfProviderName(providerName), value: providerName })), [providerNames]) return <>
@@ -44,7 +44,7 @@ const AddModelMenu = ({ onSubmit }: { onSubmit: () => void }) => {
{ modelNameRef.current = modelName }} + onChangeText={useCallback((modelName) => { modelNameRef.current = modelName }, [])} multiline={false} />
@@ -52,8 +52,8 @@ const AddModelMenu = ({ onSubmit }: { onSubmit: () => void }) => { {/* provider */}
{ providerNameRef.current = providerOptions[0].value }} // initialize state - onChangeSelection={(providerName: ProviderName) => { console.log('selecting provider', providerName); providerNameRef.current = providerName }} + onCreateInstance={useCallback(() => { providerNameRef.current = providerOptions[0].value }, [providerOptions])} // initialize state + onChangeSelection={useCallback((providerName: ProviderName) => { providerNameRef.current = providerName }, [])} options={providerOptions} />
From 5b55eed08af0f512da920dc8aaf69c88dcaaeb7e Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 17 Dec 2024 18:15:09 -0800 Subject: [PATCH 21/21] threads styles + misc --- .../browser/react/src/sidebar-tsx/Sidebar.tsx | 2 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 2 +- .../src/sidebar-tsx/SidebarThreadSelector.tsx | 2 +- .../react/src/void-settings-tsx/Settings.tsx | 16 ++++++++-------- .../contrib/void/browser/sidebarActions.ts | 18 ++++++++++++++++++ .../contrib/void/browser/voidSettingsPane.ts | 6 +++--- src/vs/workbench/workbench.common.main.ts | 2 +- 7 files changed, 33 insertions(+), 15 deletions(-) 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 f8f78d00..55fa83b7 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 @@ -31,7 +31,7 @@ export const Sidebar = ({ className }: { className: string }) => { sidebarStateService.setState({ currentTab: tabs[(index + 1) % tabs.length] as any }) }}>clickme {tab} */} -
+
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 046dc5d9..e5ff34c9 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 @@ -407,7 +407,7 @@ export const SidebarChat = () => { className={`w-full h-full`} > {/* previous messages */} diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx index 39896d96..6a2b1943 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx @@ -27,7 +27,7 @@ export const SidebarThreadSelector = () => { const sortedThreadIds = Object.keys(allThreads ?? {}).sort((threadId1, threadId2) => allThreads![threadId1].lastModified > allThreads![threadId2].lastModified ? -1 : 1) return ( -
+
{/* X button at top right */}
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 c1df63bb..d9c34da9 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 @@ -217,7 +217,7 @@ export const VoidProviderSettings = () => { export const Settings = () => { const isDark = useIsDark() - const [tab, setTab] = useState<'models' | 'providers'>('models') + const [tab, setTab] = useState<'models' | 'features'>('models') return
@@ -236,9 +236,9 @@ export const Settings = () => { - +
{/* separator */} @@ -255,15 +255,15 @@ export const Settings = () => { -
- -
-

{ setTab('providers') }}>Providers

+
+

{ setTab('features') }}>Features

+
+
diff --git a/src/vs/workbench/contrib/void/browser/sidebarActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts index 5f9a9356..e44fc864 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarActions.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts @@ -21,6 +21,8 @@ import { ITextModel } from '../../../../editor/common/model.js'; import { VOID_VIEW_ID } from './sidebarPane.js'; import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; import { ISidebarStateService } from './sidebarStateService.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { OPEN_VOID_SETTINGS_ACTION_ID } from './voidSettingsPane.js'; // ---------- Register commands and keybindings ---------- @@ -161,3 +163,19 @@ registerAction2(class extends Action2 { } }) + +// Settings gear +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'void.settingsAction', + title: 'Void Settings', + icon: { id: 'settings-gear' }, + menu: [{ id: MenuId.ViewTitle, group: 'navigation', when: ContextKeyExpr.equals('view', VOID_VIEW_ID), }] + }); + } + async run(accessor: ServicesAccessor): Promise { + const commandService = accessor.get(ICommandService) + commandService.executeCommand(OPEN_VOID_SETTINGS_ACTION_ID) + } +}) diff --git a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts index 7974fd4c..3b7515b8 100644 --- a/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts +++ b/src/vs/workbench/contrib/void/browser/voidSettingsPane.ts @@ -120,12 +120,12 @@ Registry.as(EditorExtensions.EditorPane).registerEditorPane ); -const OPEN_VOID_SETTINGS_ID = 'workbench.action.openVoidSettings' +export const OPEN_VOID_SETTINGS_ACTION_ID = 'workbench.action.openVoidSettings' // register the gear on the top right registerAction2(class extends Action2 { constructor() { super({ - id: OPEN_VOID_SETTINGS_ID, + id: OPEN_VOID_SETTINGS_ACTION_ID, title: nls.localize2('voidSettings', "Void: Settings"), f1: true, icon: Codicon.settingsGear, @@ -155,7 +155,7 @@ registerAction2(class extends Action2 { MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '0_command', command: { - id: OPEN_VOID_SETTINGS_ID, + id: OPEN_VOID_SETTINGS_ACTION_ID, title: nls.localize('voidSettings', "Void Settings") }, order: 1 diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 36bd6ad4..14f3fec1 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -334,7 +334,7 @@ import './contrib/surveys/browser/nps.contribution.js'; import './contrib/surveys/browser/languageSurveys.contribution.js'; // Welcome -import './contrib/welcomeGettingStarted/browser/gettingStarted.contribution.js'; +// import './contrib/welcomeGettingStarted/browser/gettingStarted.contribution.js'; // Void commented this out (removes Welcome page on start) import './contrib/welcomeWalkthrough/browser/walkThrough.contribution.js'; import './contrib/welcomeViews/common/viewsWelcome.contribution.js'; import './contrib/welcomeViews/common/newFile.contribution.js';