diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts index f81b0858..26cbb0ae 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeService.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts @@ -1599,7 +1599,7 @@ class EditCodeService extends Disposable implements IEditCodeService { const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined - const N_RETRIES = 5 + const N_RETRIES = 2 // allowed to throw errors - this is called inside a promise that handles everything const runSearchReplace = async () => { diff --git a/src/vs/workbench/contrib/void/browser/media/void.css b/src/vs/workbench/contrib/void/browser/media/void.css index 3a420737..724a8d28 100644 --- a/src/vs/workbench/contrib/void/browser/media/void.css +++ b/src/vs/workbench/contrib/void/browser/media/void.css @@ -76,93 +76,108 @@ opacity: 80%; } +/* styles for all containers used by void */ +.void-scope { + background-color: var(--vscode-editor-background); + --scrollbar-vertical-width: 8px; + --scrollbar-horizontal-height: 6px; +} +/* Target both void-scope and all its descendants with scrollbars */ +.void-scope, +.void-scope * { + scrollbar-width: thin !important; + scrollbar-color: var(--void-bg-1) var(--void-bg-3) !important; /* For Firefox */ +} +.void-scope::-webkit-scrollbar, +.void-scope *::-webkit-scrollbar { + width: var(--scrollbar-vertical-width) !important; + height: var(--scrollbar-horizontal-height) !important; + background-color: var(--void-bg-3) !important; +} +.void-scope::-webkit-scrollbar-thumb, +.void-scope *::-webkit-scrollbar-thumb { + background-color: var(--void-bg-1) !important; + border-radius: 4px !important; + border: none !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; +} + +.void-scope::-webkit-scrollbar-thumb:hover, +.void-scope *::-webkit-scrollbar-thumb:hover { + background-color: var(--void-bg-1) !important; + filter: brightness(1.1) !important; +} + +.void-scope::-webkit-scrollbar-thumb:active, +.void-scope *::-webkit-scrollbar-thumb:active { + background-color: var(--void-bg-1) !important; + filter: brightness(1.2) !important; +} + +.void-scope::-webkit-scrollbar-track, +.void-scope *::-webkit-scrollbar-track { + background-color: var(--void-bg-3) !important; + border: none !important; +} + +.void-scope::-webkit-scrollbar-corner, +.void-scope *::-webkit-scrollbar-corner { + background-color: var(--void-bg-3) !important; +} + +/* Add void-scrollable-element styles to match */ +.void-scrollable-element { + background-color: var(--vscode-editor-background); + --scrollbar-vertical-width: 14px; + --scrollbar-horizontal-height: 6px; + overflow: auto; /* Ensure scrollbars are shown when needed */ +} + +.void-scrollable-element, +.void-scrollable-element * { + scrollbar-width: thin !important; /* For Firefox */ + scrollbar-color: var(--void-bg-1) var(--void-bg-3) !important; /* For Firefox */ +} .void-scrollable-element::-webkit-scrollbar, .void-scrollable-element *::-webkit-scrollbar { - width: 14px !important; - height: 4px !important; -} - -.void-scrollable-element::-webkit-scrollbar-track, -.void-scrollable-element *::-webkit-scrollbar-track { - background: transparent !important; + width: var(--scrollbar-vertical-width) !important; + height: var(--scrollbar-horizontal-height) !important; + background-color: var(--void-bg-3) !important; } .void-scrollable-element::-webkit-scrollbar-thumb, .void-scrollable-element *::-webkit-scrollbar-thumb { - background-color: transparent !important; - border-radius: 0px !important; + background-color: var(--void-bg-1) !important; + border-radius: 4px !important; + border: none !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; } .void-scrollable-element::-webkit-scrollbar-thumb:hover, .void-scrollable-element *::-webkit-scrollbar-thumb:hover { - background-color: var(--vscode-scrollbarSlider-hoverBackground) !important; + background-color: var(--void-bg-1) !important; + filter: brightness(1.1) !important; } .void-scrollable-element::-webkit-scrollbar-thumb:active, .void-scrollable-element *::-webkit-scrollbar-thumb:active { - background-color: var(--vscode-scrollbarSlider-activeBackground) !important; + background-color: var(--void-bg-1) !important; + filter: brightness(1.2) !important; +} + +.void-scrollable-element::-webkit-scrollbar-track, +.void-scrollable-element *::-webkit-scrollbar-track { + background-color: var(--void-bg-3) !important; + border: none !important; } .void-scrollable-element::-webkit-scrollbar-corner, .void-scrollable-element *::-webkit-scrollbar-corner { - background-color: transparent !important; -} - -.void-scrollable-element.show-scrollbar-0::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-0 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 0%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-1::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-1 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 10%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-2::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-2 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 20%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-3::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-3 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 30%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-4::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-4 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 40%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-5::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-5 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 50%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-6::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-6 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 60%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-7::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-7 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 70%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-8::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-8 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 80%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-9::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-9 *::-webkit-scrollbar-thumb { - background-color: color-mix(in srgb, var(--vscode-scrollbarSlider-background) 90%, transparent) !important; -} - -.void-scrollable-element.show-scrollbar-10::-webkit-scrollbar-thumb, -.void-scrollable-element.show-scrollbar-10 *::-webkit-scrollbar-thumb { - background-color: var(--vscode-scrollbarSlider-background) !important; + background-color: var(--void-bg-3) !important; } diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index 8983c7bc..6a13d4bc 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -20,19 +20,16 @@ enum CopyButtonText { type IconButtonProps = { - onClick: () => void; Icon: LucideIcon - disabled?: boolean - className?: string } -export const IconShell1 = ({ onClick, Icon, disabled, className }: IconButtonProps) => ( +export const IconShell1 = ({ onClick, Icon, disabled, className, ...props }: IconButtonProps & React.ButtonHTMLAttributes) => ( @@ -163,10 +161,11 @@ export const useApplyButtonState = ({ applyBoxId, uri }: { applyBoxId: string, u rerender(c => c + 1) console.log('rerendering....') } - }, [applyBoxId, applyBoxId, uri])) + }, [applyBoxId, uri])) const currStreamState = getStreamState() + return { getStreamState, isDisabled, @@ -192,7 +191,8 @@ export const StatusIndicator = ({ color, title, className }: { color: 'green' | ); }; -export const StatusIndicatorHTML = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI | 'current' }) => { +export const StatusIndicatorForApplyButton = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI | 'current' }) => { + const { currStreamState } = useApplyButtonState({ applyBoxId, uri }) const color = ( @@ -329,7 +329,7 @@ export const BlockCodeApplyWrapper = ({ {/* header */}
- + {name} diff --git a/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx index d541dc43..2a531815 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx @@ -10,7 +10,6 @@ import { QuickEditPropsType } from '../../../quickEditActions.js'; import { ButtonStop, ButtonSubmit, IconX, VoidChatArea } from '../sidebar-tsx/SidebarChat.js'; import { VOID_CTRL_K_ACTION_ID } from '../../../actionIDs.js'; import { useRefState } from '../util/helpers.js'; -import { useScrollbarStyles } from '../util/useScrollbarStyles.js'; import { isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js'; export const QuickEditChat = ({ @@ -89,8 +88,6 @@ export const QuickEditChat = ({ editCodeService.removeCtrlKZone({ diffareaid }) }, [editCodeService, diffareaid]) - useScrollbarStyles(sizerRef) - const keybindingString = accessor.get('IKeybindingService').lookupKeybinding(VOID_CTRL_K_ACTION_ID)?.getLabel() const chatAreaRef = useRef(null) 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 57f79f0c..57ea58e8 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 @@ -15,7 +15,6 @@ import { ErrorDisplay } from './ErrorDisplay.js'; import { BlockCode, TextAreaFns, VoidCustomDropdownBox, VoidInputBox2, VoidSlider, VoidSwitch } from '../util/inputs.js'; import { ModelDropdown, } from '../void-settings-tsx/ModelDropdown.js'; import { SidebarThreadSelector } from './SidebarThreadSelector.js'; -import { useScrollbarStyles } from '../util/useScrollbarStyles.js'; import { VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js'; import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js'; import { ChatMode, FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js'; @@ -1027,6 +1026,7 @@ const SmallProseWrapper = ({ children }: { children: React.ReactNode }) => { prose-blockquote:pl-2 prose-blockquote:my-2 + prose-code:text-void-fg-3 prose-code:text-[12px] prose-code:before:content-none prose-code:after:content-none @@ -1294,7 +1294,7 @@ export const ToolChildrenWrapper = ({ children, className }: { children: React.R
} export const CodeChildren = ({ children }: { children: React.ReactNode }) => { - return
+ return
{children}
@@ -1328,6 +1328,8 @@ const EditToolChildren = ({ uri, changeDescription }: { uri: URI, changeDescript const EditToolHeaderButtons = ({ applyBoxId, uri, codeStr }: { applyBoxId: string, uri: URI, codeStr: string }) => { const { currStreamState } = useApplyButtonState({ applyBoxId, uri }) return
+ + {currStreamState === 'idle-no-changes' && } @@ -1767,18 +1769,18 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper, resolveReason.type === 'toofull' ? `\n(truncated)` : null - componentParams.children = + componentParams.children =
- {`Ran command: `} - {command} + {`Ran command: `} + {command}
{resolveReason.type === 'bgtask' ? 'Result so far:\n' : null} {`Result: `} - {terminalResult} - {additionalDetailsStr} + {terminalResult} + {additionalDetailsStr}
@@ -2150,7 +2152,7 @@ const CommandBarInChat = () => { @@ -2254,8 +2256,6 @@ export const SidebarChat = () => { const sidebarRef = useRef(null) const scrollContainerRef = useRef(null) - useScrollbarStyles(sidebarRef) - const onSubmit = useCallback(async () => { if (isDisabled) return 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 51febb6a..1ae9719d 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 @@ -153,12 +153,13 @@ export const VoidInputBox2 = forwardRef(fun }) -export const VoidSimpleInputBox = ({ value, onChangeValue, placeholder, className, disabled, passwordBlur, ...inputProps }: { +export const VoidSimpleInputBox = ({ value, onChangeValue, placeholder, className, disabled, passwordBlur, compact, ...inputProps }: { value: string; onChangeValue: (value: string) => void; placeholder: string; className?: string; disabled?: boolean; + compact?: boolean; passwordBlur?: boolean; } & React.InputHTMLAttributes) => { @@ -168,7 +169,11 @@ export const VoidSimpleInputBox = ({ value, onChangeValue, placeholder, classNam onChange={(e) => onChangeValue(e.target.value)} placeholder={placeholder} disabled={disabled} - className={`w-full resize-none text-void-fg-1 placeholder:text-void-fg-3 px-2 py-1 rounded-sm + // className='max-w-44 w-full border border-void-border-2 bg-void-bg-1 text-void-fg-3 text-root' + // className={`w-full resize-none text-void-fg-1 placeholder:text-void-fg-3 px-2 py-1 rounded-sm + className={`w-full resize-none bg-void-bg-1 text-void-fg-1 placeholder:text-void-fg-3 border border-void-border-2 focus:border-void-border-1 + ${compact ? 'py-1 px-2' : 'py-2 px-4 '} + rounded ${disabled ? 'opacity-50 cursor-not-allowed' : ''} ${className}`} style={{ @@ -954,9 +959,9 @@ export const BlockCode = ({ initValue, language, maxHeight, showScrollbars }: Bl } -export const VoidButton = ({ children, disabled, onClick }: { children: React.ReactNode; disabled?: boolean; onClick: () => void }) => { +export const VoidButtonBgDarken = ({ children, disabled, onClick }: { children: React.ReactNode; disabled?: boolean; onClick: () => void }) => { return } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/useScrollbarStyles.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/useScrollbarStyles.tsx index 1ba69c24..1e20f692 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/useScrollbarStyles.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/useScrollbarStyles.tsx @@ -1,128 +1,130 @@ -import { useEffect } from 'react'; +// Get rid of this as it was causing lag -export const useScrollbarStyles = (containerRef: React.MutableRefObject) => { - useEffect(() => { - if (!containerRef.current) return; +// import { useEffect } from 'react'; - // Create selector for specific overflow classes - const overflowSelector = [ - '[class*="overflow-auto"]', - '[class*="overflow-x-auto"]', - '[class*="overflow-y-auto"]' - ].join(','); +// export const useScrollbarStyles = (containerRef: React.RefObject) => { +// useEffect(() => { +// if (!containerRef.current) return; - // Function to initialize scrollbar styles for elements - const initializeScrollbarStyles = () => { - // Get all matching elements within the container, including the container itself - const scrollElements = [ - ...(containerRef.current?.matches(overflowSelector) ? [containerRef.current] : []), - ...Array.from(containerRef.current?.querySelectorAll(overflowSelector) || []) - ]; +// // Create selector for specific overflow classes +// const overflowSelector = [ +// '[class*="overflow-auto"]', +// '[class*="overflow-x-auto"]', +// '[class*="overflow-y-auto"]' +// ].join(','); - // Apply basic styling to all elements - scrollElements.forEach(element => { - element.classList.add('void-scrollable-element'); - }); +// // Function to initialize scrollbar styles for elements +// const initializeScrollbarStyles = () => { +// // Get all matching elements within the container, including the container itself +// const scrollElements = [ +// ...(containerRef.current?.matches(overflowSelector) ? [containerRef.current] : []), +// ...Array.from(containerRef.current?.querySelectorAll(overflowSelector) || []) +// ]; - // Only initialize fade effects for elements that haven't been initialized yet - scrollElements.forEach(element => { - if (!(element as any).__scrollbarCleanup) { - let fadeTimeout: NodeJS.Timeout | null = null; - let fadeInterval: NodeJS.Timeout | null = null; +// // Apply basic styling to all elements +// scrollElements.forEach(element => { +// element.classList.add('void-scrollable-element'); +// }); - const fadeIn = () => { - if (fadeInterval) clearInterval(fadeInterval); +// // Only initialize fade effects for elements that haven't been initialized yet +// scrollElements.forEach(element => { +// if (!(element as any).__scrollbarCleanup) { +// let fadeTimeout: NodeJS.Timeout | null = null; +// let fadeInterval: NodeJS.Timeout | null = null; - let step = 0; - fadeInterval = setInterval(() => { - if (step <= 10) { - element.classList.remove(`show-scrollbar-${step - 1}`); - element.classList.add(`show-scrollbar-${step}`); - step++; - } else { - clearInterval(fadeInterval!); - } - }, 10); - }; +// const fadeIn = () => { +// if (fadeInterval) clearInterval(fadeInterval); - const fadeOut = () => { - if (fadeInterval) clearInterval(fadeInterval); +// let step = 0; +// fadeInterval = setInterval(() => { +// if (step <= 10) { +// element.classList.remove(`show-scrollbar-${step - 1}`); +// element.classList.add(`show-scrollbar-${step}`); +// step++; +// } else { +// clearInterval(fadeInterval!); +// } +// }, 10); +// }; - let step = 10; - fadeInterval = setInterval(() => { - if (step >= 0) { - element.classList.remove(`show-scrollbar-${step + 1}`); - element.classList.add(`show-scrollbar-${step}`); - step--; - } else { - clearInterval(fadeInterval!); - } - }, 60); - }; +// const fadeOut = () => { +// if (fadeInterval) clearInterval(fadeInterval); - const onMouseEnter = () => { - if (fadeTimeout) clearTimeout(fadeTimeout); - if (fadeInterval) clearInterval(fadeInterval); - fadeIn(); - }; +// let step = 10; +// fadeInterval = setInterval(() => { +// if (step >= 0) { +// element.classList.remove(`show-scrollbar-${step + 1}`); +// element.classList.add(`show-scrollbar-${step}`); +// step--; +// } else { +// clearInterval(fadeInterval!); +// } +// }, 60); +// }; - const onMouseLeave = () => { - if (fadeTimeout) clearTimeout(fadeTimeout); - fadeTimeout = setTimeout(() => { - fadeOut(); - }, 10); - }; +// const onMouseEnter = () => { +// if (fadeTimeout) clearTimeout(fadeTimeout); +// if (fadeInterval) clearInterval(fadeInterval); +// fadeIn(); +// }; - element.addEventListener('mouseenter', onMouseEnter); - element.addEventListener('mouseleave', onMouseLeave); +// const onMouseLeave = () => { +// if (fadeTimeout) clearTimeout(fadeTimeout); +// fadeTimeout = setTimeout(() => { +// fadeOut(); +// }, 10); +// }; - // Store cleanup function - const cleanup = () => { - element.removeEventListener('mouseenter', onMouseEnter); - element.removeEventListener('mouseleave', onMouseLeave); - if (fadeTimeout) clearTimeout(fadeTimeout); - if (fadeInterval) clearInterval(fadeInterval); - element.classList.remove('void-scrollable-element'); - // Remove any remaining show-scrollbar classes - for (let i = 0; i <= 10; i++) { - element.classList.remove(`show-scrollbar-${i}`); - } - }; +// element.addEventListener('mouseenter', onMouseEnter); +// element.addEventListener('mouseleave', onMouseLeave); - // Store the cleanup function on the element for later use - (element as any).__scrollbarCleanup = cleanup; - } - }); - }; +// // Store cleanup function +// const cleanup = () => { +// element.removeEventListener('mouseenter', onMouseEnter); +// element.removeEventListener('mouseleave', onMouseLeave); +// if (fadeTimeout) clearTimeout(fadeTimeout); +// if (fadeInterval) clearInterval(fadeInterval); +// element.classList.remove('void-scrollable-element'); +// // Remove any remaining show-scrollbar classes +// for (let i = 0; i <= 10; i++) { +// element.classList.remove(`show-scrollbar-${i}`); +// } +// }; - // Initialize for the first time - initializeScrollbarStyles(); +// // Store the cleanup function on the element for later use +// (element as any).__scrollbarCleanup = cleanup; +// } +// }); +// }; - // Set up mutation observer to do the same - const observer = new MutationObserver(() => { - initializeScrollbarStyles(); - }); +// // Initialize for the first time +// initializeScrollbarStyles(); - // Start observing the container for child changes - observer.observe(containerRef.current, { - childList: true, - subtree: true - }); +// // Set up mutation observer to do the same +// const observer = new MutationObserver(() => { +// initializeScrollbarStyles(); +// }); - return () => { - observer.disconnect(); - // Your existing cleanup code... - if (containerRef.current) { - const scrollElements = [ - ...(containerRef.current.matches(overflowSelector) ? [containerRef.current] : []), - ...Array.from(containerRef.current.querySelectorAll(overflowSelector)) - ]; - scrollElements.forEach(element => { - if ((element as any).__scrollbarCleanup) { - (element as any).__scrollbarCleanup(); - } - }); - } - }; - }, [containerRef]); -}; +// // Start observing the container for child changes +// observer.observe(containerRef.current, { +// childList: true, +// subtree: true +// }); + +// return () => { +// observer.disconnect(); +// // Your existing cleanup code... +// if (containerRef.current) { +// const scrollElements = [ +// ...(containerRef.current.matches(overflowSelector) ? [containerRef.current] : []), +// ...Array.from(containerRef.current.querySelectorAll(overflowSelector)) +// ]; +// scrollElements.forEach(element => { +// if ((element as any).__scrollbarCleanup) { +// (element as any).__scrollbarCleanup(); +// } +// }); +// } +// }; +// }, [containerRef]); +// }; diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidCommandBar.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidCommandBar.tsx index b144a34d..b4940e9e 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidCommandBar.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidCommandBar.tsx @@ -309,7 +309,7 @@ const VoidCommandBar = ({ uri, editor }: VoidCommandBarProps) => {
- return
+ return
{showAcceptRejectAll && acceptRejectAllButtons} {leftRightUpDownButtons} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidSelectionHelper.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidSelectionHelper.tsx index 61c76e53..65220134 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidSelectionHelper.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-editor-widgets-tsx/VoidSelectionHelper.tsx @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------*/ -import { useAccessor, useIsDark, useSettingsState } from '../util/services.js'; +import { useAccessor, useActiveURI, useIsDark, useSettingsState } from '../util/services.js'; import '../styles.css' import { VOID_CTRL_K_ACTION_ID, VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js'; @@ -65,6 +65,8 @@ const VoidSelectionHelper = ({ rerenderKey }: VoidSelectionHelperProps) => { // }, [rerenderKey, reactRerenderCount, setReactRerenderKey, setClickState]) // if the user selected an option, close + + if (clickState === 'clickedOption') { return null } 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 71919a72..c4064d58 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 @@ -5,12 +5,11 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js' -import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidModelInfo, globalSettingNames, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, defaultProviderSettings, nonlocalProviderNames, localProviderNames, GlobalSettingName, featureNames, displayInfoOfFeatureName, isProviderNameDisabled, FeatureName } from '../../../../common/voidSettingsTypes.js' +import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidStatefulModelInfo, globalSettingNames, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, nonlocalProviderNames, localProviderNames, GlobalSettingName, featureNames, displayInfoOfFeatureName, isProviderNameDisabled, FeatureName, hasDownloadButtonsOnModelsProviderNames } from '../../../../common/voidSettingsTypes.js' import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' -import { VoidButton, VoidCheckBox, VoidCustomDropdownBox, VoidInputBox, VoidInputBox2, VoidSimpleInputBox, VoidSwitch } from '../util/inputs.js' +import { VoidButtonBgDarken, VoidCheckBox, VoidCustomDropdownBox, VoidInputBox, VoidInputBox2, VoidSimpleInputBox, VoidSwitch } from '../util/inputs.js' import { useAccessor, useIsDark, useRefreshModelListener, useRefreshModelState, useSettingsState } from '../util/services.js' -import { X, RefreshCw, Loader2, Check, MoveRight } from 'lucide-react' -import { useScrollbarStyles } from '../util/useScrollbarStyles.js' +import { X, RefreshCw, Loader2, Check, MoveRight, PlusCircle, MinusCircle, Download, Trash, StopCircle, Square, ExternalLink } from 'lucide-react' import { isWindows, isLinux, isMacintosh } from '../../../../../../../base/common/platform.js' import { URI } from '../../../../../../../base/common/uri.js' import { env } from '../../../../../../../base/common/process.js' @@ -18,11 +17,14 @@ import { ModelDropdown } from './ModelDropdown.js' import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js' import { WarningBox } from './WarningBox.js' import { os } from '../../../../common/helpers/systemInfo.js' -import { IconX } from '../sidebar-tsx/SidebarChat.js' +import { IconLoading, IconX } from '../sidebar-tsx/SidebarChat.js' +import { getModelCapabilities, getProviderCapabilities, ollamaRecommendedModels, VoidStaticModelInfo } from '../../../../common/modelCapabilities.js' +import { ToolCallType, AnthropicReasoning } from '../../../../common/sendLLMMessageTypes.js' +import { IconShell1, StatusIndicatorForApplyButton } from '../markdown/ApplyBlockHoverButtons.js' const ButtonLeftTextRightOption = ({ text, leftButton }: { text: string, leftButton?: React.ReactNode }) => { - return
+ return
{leftButton ? leftButton : null} {text} @@ -98,58 +100,134 @@ const RefreshableModels = () => { -const AddModelMenu = ({ onSubmit, onClose }: { onSubmit: () => void, onClose: () => void }) => { +const AnimatedCheckmarkButton = ({ text, className }: { text?: string, className?: string }) => { + const [dashOffset, setDashOffset] = useState(40); + + useEffect(() => { + const startTime = performance.now(); + const duration = 500; // 500ms animation + + const animate = (currentTime: number) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + const newOffset = 40 - (progress * 40); + + setDashOffset(newOffset); + + if (progress < 1) { + requestAnimationFrame(animate); + } + }; + + const animationId = requestAnimationFrame(animate); + return () => cancelAnimationFrame(animationId); + }, []); + + return
+ + + + {text} +
+} + + +const AddButton = ({ disabled, text = 'Add', ...props }: { disabled?: boolean, text?: React.ReactNode } & React.ButtonHTMLAttributes) => { + + return + +} + + +// shows a providerName dropdown if no `providerName` is given +const AddModelInputBox = ({ providerName: permanentProviderName, className, compact }: { providerName?: ProviderName, className?: string, compact?: boolean }) => { const accessor = useAccessor() const settingsStateService = accessor.get('IVoidSettingsService') const settingsState = useSettingsState() + const [isOpen, setIsOpen] = useState(false) + // const providerNameRef = useRef(null) - const [providerName, setProviderName] = useState(null) + const [userChosenProviderName, setUserChosenProviderName] = useState(null) - const modelNameRef = useRef(null) + const providerName = permanentProviderName ?? userChosenProviderName; + const [modelName, setModelName] = useState('') const [errorString, setErrorString] = useState('') + if (!providerName) { return null; } + + const numModels = settingsState.settingsOfProvider[providerName].models.length + + if (!isOpen) { + return
setIsOpen(true)} + + > +
+ {numModels > 0 ? `Add a different model?` : `Add a model`} +
+
+ } + return <> -
+
- {/* provider */} - setProviderName(pn)} - getOptionDisplayName={(pn) => pn ? displayInfoOfProviderName(pn).title : 'Provider Name'} - getOptionDropdownName={(pn) => pn ? displayInfoOfProviderName(pn).title : 'Provider Name'} - getOptionsEqual={(a, b) => a === b} - className={`max-w-44 w-full border border-void-border-2 bg-void-bg-1 text-void-fg-3 text-root + {/* X button + */} + + {/* provider input */} + {!permanentProviderName && + setUserChosenProviderName(pn)} + getOptionDisplayName={(pn) => pn ? displayInfoOfProviderName(pn).title : 'Provider Name'} + getOptionDropdownName={(pn) => pn ? displayInfoOfProviderName(pn).title : 'Provider Name'} + getOptionsEqual={(a, b) => a === b} + className={`max-w-44 w-full border border-void-border-2 bg-void-bg-1 text-void-fg-3 text-root py-[4px] px-[6px] `} - arrowTouchesText={false} - /> - {/* <_VoidSelectBox - onCreateInstance={useCallback(() => { providerNameRef.current = providerOptions[0].value }, [providerOptions])} // initialize state - onChangeSelection={useCallback((providerName: ProviderName) => { providerNameRef.current = providerName }, [])} - options={providerOptions} - /> */} - - {/* model */} -
- -
+ } - {/* button */} - { - const modelName = modelNameRef.current?.value + {/* model input */} + + {/* add button */} + { if (providerName === null) { setErrorString('Please select a provider.') return @@ -160,17 +238,20 @@ const AddModelMenu = ({ onSubmit, onClose }: { onSubmit: () => void, onClose: () } // if model already exists here if (settingsState.settingsOfProvider[providerName].models.find(m => m.modelName === modelName)) { - setErrorString(`This model already exists under ${providerName}.`) + // setErrorString(`This model already exists under ${providerName}.`) + setErrorString(`This model already exists.`) return } settingsStateService.addModel(providerName, modelName) - onSubmit() + setIsOpen(false) + setErrorString('') + setModelName('') }} - >Add model + /> - -
+ + {!errorString ? null :
{errorString} @@ -180,17 +261,6 @@ const AddModelMenu = ({ onSubmit, onClose }: { onSubmit: () => void, onClose: () } -const AddModelMenuFull = () => { - const [open, setOpen] = useState(false) - - return
- {open ? - setOpen(false)} onClose={() => setOpen(false)} /> - : setOpen(true)}>Add Model - } -
-} - export const ModelDump = () => { @@ -200,7 +270,7 @@ export const ModelDump = () => { const settingsState = useSettingsState() // a dump of all the enabled providers' models - const modelDump: (VoidModelInfo & { providerName: ProviderName, providerEnabled: boolean })[] = [] + const modelDump: (VoidStatefulModelInfo & { providerName: ProviderName, providerEnabled: boolean })[] = [] for (let providerName of providerNames) { const providerSettings = settingsState.settingsOfProvider[providerName] // if (!providerSettings.enabled) continue @@ -277,6 +347,7 @@ const ProviderSetting = ({ providerName, settingName }: { providerName: Provider // placeholder={`${providerTitle} ${settingTitle} (${placeholder})`} placeholder={`${settingTitle} (${placeholder})`} passwordBlur={isPasswordField} + compact={true} /> {subTextMd === undefined ? null :
@@ -286,7 +357,52 @@ const ProviderSetting = ({ providerName, settingName }: { providerName: Provider } -const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => { +// const OldSettingsForProvider = ({ providerName, showProviderTitle }: { providerName: ProviderName, showProviderTitle: boolean }) => { +// const voidSettingsState = useSettingsState() + +// const needsModel = isProviderNameDisabled(providerName, voidSettingsState) === 'addModel' + +// // const accessor = useAccessor() +// // const voidSettingsService = accessor.get('IVoidSettingsService') + +// // const { enabled } = voidSettingsState.settingsOfProvider[providerName] +// const settingNames = customSettingNamesOfProvider(providerName) + +// const { title: providerTitle } = displayInfoOfProviderName(providerName) + +// return
+ +//
+// {showProviderTitle &&

{providerTitle}

} + +// {/* enable provider switch */} +// {/* { +// const enabledRef = voidSettingsService.state.settingsOfProvider[providerName].enabled +// voidSettingsService.setSettingOfProvider(providerName, 'enabled', !enabledRef) +// }, [voidSettingsService, providerName])} +// size='sm+' +// /> */} +//
+ +//
+// {/* settings besides models (e.g. api key) */} +// {settingNames.map((settingName, i) => { +// return +// })} + +// {needsModel ? +// providerName === 'ollama' ? +// +// : +// : null} +//
+//
+// } + +const SettingsForProvider = ({ providerName, showProviderTitle, showProviderSuggestions }: { providerName: ProviderName, showProviderTitle: boolean, showProviderSuggestions: boolean }) => { const voidSettingsState = useSettingsState() const needsModel = isProviderNameDisabled(providerName, voidSettingsState) === 'addModel' @@ -299,10 +415,10 @@ const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) = const { title: providerTitle } = displayInfoOfProviderName(providerName) - return
+ return
-

{providerTitle}

+ {showProviderTitle &&

{providerTitle}

} {/* enable provider switch */} {/* })} - {needsModel ? + {showProviderSuggestions && needsModel ? providerName === 'ollama' ? : @@ -335,7 +451,7 @@ const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) = export const VoidProviderSettings = ({ providerNames }: { providerNames: ProviderName[] }) => { return <> {providerNames.map(providerName => - + )} } @@ -418,7 +534,7 @@ export const FeaturesTab = () => {

Models

- + @@ -429,14 +545,7 @@ export const FeaturesTab = () => { {/*

{`Instructions:`}

*/} {/*

{`Void can access any model that you host locally. We automatically detect your local models by default.`}

*/}

{`Void can access any model that you host locally. We automatically detect your local models by default.`}

-
- - - - - - {/* TODO we should create UI for downloading models without user going into terminal */} -
+ {ollamaSetupInstructions} @@ -564,41 +673,91 @@ export const FeaturesTab = () => { } - +type TransferEditorType = 'VS Code' | 'Cursor' | 'Windsurf' // https://github.com/VSCodium/vscodium/blob/master/docs/index.md#migrating-from-visual-studio-code-to-vscodium // https://code.visualstudio.com/docs/editor/extension-marketplace#_where-are-extensions-installed type TransferFilesInfo = { from: URI, to: URI }[] -const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null): TransferFilesInfo => { +const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEditor: TransferEditorType = 'VS Code'): TransferFilesInfo => { if (os === null) throw new Error(`One-click switch is not possible in this environment.`) if (os === 'mac') { const homeDir = env['HOME'] if (!homeDir) throw new Error(`$HOME not found`) - return [{ - from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'settings.json'), - to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'settings.json'), - }, { - from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'keybindings.json'), - to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'keybindings.json'), - }, { - from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.vscode', 'extensions'), - to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'), - }] + + if (fromEditor === 'VS Code') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Code', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.vscode', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'), + }] + } else if (fromEditor === 'Cursor') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Cursor', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.cursor', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'), + }] + } else if (fromEditor === 'Windsurf') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Windsurf', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Windsurf', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, 'Library', 'Application Support', 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.windsurf', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'), + }] + } } if (os === 'linux') { const homeDir = env['HOME'] if (!homeDir) throw new Error(`variable for $HOME location not found`) - return [{ - from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'settings.json'), - to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'settings.json'), - }, { - from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'keybindings.json'), - to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'keybindings.json'), - }, { - from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.vscode', 'extensions'), - to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'), - }] + + if (fromEditor === 'VS Code') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Code', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.vscode', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'), + }] + } else if (fromEditor === 'Cursor') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Cursor', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Cursor', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.cursor', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'), + }] + } else if (fromEditor === 'Windsurf') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Windsurf', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Windsurf', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.config', 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.windsurf', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), homeDir, '.void-editor', 'extensions'), + }] + } } if (os === 'windows') { @@ -607,73 +766,115 @@ const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null): Transfe const userprofile = env['USERPROFILE'] if (!userprofile) throw new Error(`variable for %USERPROFILE% location not found`) - return [{ - from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'settings.json'), - to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'settings.json'), - }, { - from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'keybindings.json'), - to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'keybindings.json'), - }, { - from: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.vscode', 'extensions'), - to: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.void-editor', 'extensions'), - }] + if (fromEditor === 'VS Code') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Code', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.vscode', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.void-editor', 'extensions'), + }] + } else if (fromEditor === 'Cursor') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Cursor', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Cursor', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.cursor', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.void-editor', 'extensions'), + }] + } else if (fromEditor === 'Windsurf') { + return [{ + from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Windsurf', 'User', 'settings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'settings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Windsurf', 'User', 'keybindings.json'), + to: URI.joinPath(URI.from({ scheme: 'file' }), appdata, 'Void', 'User', 'keybindings.json'), + }, { + from: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.windsurf', 'extensions'), + to: URI.joinPath(URI.from({ scheme: 'file' }), userprofile, '.void-editor', 'extensions'), + }] + } } - throw new Error(`os '${os}' not recognized`) + throw new Error(`os '${os}' not recognized or editor type '${fromEditor}' not supported for this OS`) } -let transferTheseFiles: TransferFilesInfo = [] -let transferError: string | null = null - -try { transferTheseFiles = transferTheseFilesOfOS(os) } -catch (e) { transferError = e + '' } - -const OneClickSwitchButton = () => { +const OneClickSwitchButton = ({ fromEditor = 'VS Code' }: { fromEditor?: TransferEditorType }) => { const accessor = useAccessor() const fileService = accessor.get('IFileService') - const [state, setState] = useState<{ type: 'done', error?: string } | { type: | 'loading' | 'justfinished' }>({ type: 'done' }) + const [transferState, setTransferState] = useState<{ type: 'done', error?: string } | { type: | 'loading' | 'justfinished' }>({ type: 'done' }) + + let transferTheseFiles: TransferFilesInfo = []; + let editorError: string | null = null; + + try { + transferTheseFiles = transferTheseFilesOfOS(os, fromEditor) + } catch (e) { + editorError = e + '' + } if (transferTheseFiles.length === 0) return <> - + - - const onClick = async () => { + if (transferState.type !== 'done') return - if (state.type !== 'done') return - - setState({ type: 'loading' }) + setTransferState({ type: 'loading' }) let errAcc = '' for (let { from, to } of transferTheseFiles) { console.log('transferring', from, to) - // not sure if this can fail, just wrapping it with try/catch for now - try { await fileService.copy(from, to, true) } - catch (e) { errAcc += e + '\n' } + // Check if the source file exists before attempting to copy + try { + const exists = await fileService.exists(from) + if (exists) { + // Ensure the destination directory exists + const toParent = URI.joinPath(to, '..') + const toParentExists = await fileService.exists(toParent) + if (!toParentExists) { + await fileService.createFolder(toParent) + } + await fileService.copy(from, to, true) + } else { + console.log(`Skipping file that doesn't exist: ${from.toString()}`) + } + } + catch (e) { + console.error('Error copying file:', e) + errAcc += `Error copying ${from.toString()}: ${e}\n` + } } + + // Even if some files were missing, consider it a success if no actual errors occurred const hadError = !!errAcc if (hadError) { - setState({ type: 'done', error: errAcc }) + setTransferState({ type: 'done', error: errAcc }) } else { - setState({ type: 'justfinished' }) - setTimeout(() => { setState({ type: 'done' }); }, 3000) + setTransferState({ type: 'justfinished' }) + setTimeout(() => { setTransferState({ type: 'done' }); }, 3000) } } return <> - - {state.type === 'done' ? 'Transfer my Settings' - : state.type === 'loading' ? 'Transferring...' - : state.type === 'justfinished' ? 'Success!' + + {transferState.type === 'done' ? `Transfer from ${fromEditor}` + : transferState.type === 'loading' ? Transferring + : transferState.type === 'justfinished' ? : null } - - {state.type === 'done' && state.error ? : null} + + {transferState.type === 'done' && transferState.error ? : null} } @@ -685,12 +886,15 @@ const GeneralTab = () => { const nativeHostService = accessor.get('INativeHostService') return <> - -

One-Click Switch

-

{`Transfer your settings from VS Code to Void in one click.`}

- +

{`Transfer your settings from another editor to Void in one click.`}

+ +
+ + + +
@@ -700,24 +904,24 @@ const GeneralTab = () => {

{`IDE settings, keyboard settings, and theme customization.`}

- { commandService.executeCommand('workbench.action.openSettings') }}> + { commandService.executeCommand('workbench.action.openSettings') }}> General Settings - +
- { commandService.executeCommand('workbench.action.openGlobalKeybindings') }}> + { commandService.executeCommand('workbench.action.openGlobalKeybindings') }}> Keyboard Settings - +
- { commandService.executeCommand('workbench.action.selectTheme') }}> + { commandService.executeCommand('workbench.action.selectTheme') }}> Theme Settings - +
- { nativeHostService.showItemInFolder(environmentService.logsHome.fsPath) }}> + { nativeHostService.showItemInFolder(environmentService.logsHome.fsPath) }}> Open Logs - +
@@ -739,11 +943,16 @@ export const Settings = () => { const [tab, setTab] = useState('models') - const containerRef = useRef(null) - useScrollbarStyles(containerRef) + + const deleteme = true + if (deleteme) { + return
+ +
+ } return
-
+
@@ -756,10 +965,10 @@ export const Settings = () => { {/* tabs */}
- -
@@ -787,3 +996,664 @@ export const Settings = () => {
} + + +const FADE_DURATION_MS = 2000 + + +const FadeIn = ({ children, className, delayMs = 0, ...props }: { children: React.ReactNode, delayMs?: number, className?: string } & React.HTMLAttributes) => { + const [opacity, setOpacity] = useState(0) + + useEffect(() => { + + const timeout = setTimeout(() => { + setOpacity(1) + }, delayMs) + + return () => clearTimeout(timeout) + }, [setOpacity, delayMs]) + + + return ( +
+ {children} +
+ ) +} + +// Onboarding +// OnboardingPage +// title: +// div +// "Welcome to Void" +// image +// content:<> +// title +// content +// prev/next + +// OnboardingPage +// title: +// div +// "How would you like to use Void?" +// content: +// ModelQuestionContent +// | +// div +// "I want to:" +// div +// "Use the smartest models" +// "Keep my data fully private" +// "Save money" +// "I don't know" +// | div +// | div +// "We recommend using " +// "Set API" +// | div +// "" +// | div +// +// title +// content +// prev/next +// +// OnboardingPage +// title +// content +// prev/next + + +const NextButton = ({ onClick, ...props }: { onClick: () => void } & React.ButtonHTMLAttributes) => { + return ( + + ) +} + +const SkipButton = ({ onClick, ...props }: { onClick: () => void } & React.ButtonHTMLAttributes) => { + return ( + + ) +} + +const PreviousButton = ({ onClick, ...props }: { onClick: () => void } & React.ButtonHTMLAttributes) => { + return ( + + ) +} + + +const ollamaSetupInstructions =
+
+
+
+
+
+
+ +const OllamaDownloadOrRemoveModelButton = ({ modelName, isModelInstalled, sizeGb }: { modelName: string, isModelInstalled: boolean, sizeGb: number | false | 'not-known' }) => { + + + // for now just link to the ollama download page + return + + + + // if (isModelInstalled) { + // return
+ + // Uninstall + + // { + + // setIsModelInstalling(false); + // }} + // /> + + //
+ // } + + + + // else if (isModelInstalling) { + // return
+ + // {`Download? ${typeof sizeGb === 'number' ? `(${sizeGb} Gb)` : ''}`} + + // { + // // abort() + + // // TODO!!!!!!!!!!! don't do this + // setIsModelInstalling(false); + // }} + // /> + + //
+ // } + + + // else if (!isModelInstalled) { + + // return
+ + // Download ({sizeGb} Gb) + + // { + // // this is a check for whether the model was installed: + + // if (isModelInstalling) return + + + // // TODO!!!!!! don't do this + + + // // install(modelname), callback = setIsModelInstalling(false); + + // setIsModelInstalling(true); + // }} + // /> + + //
+ + // } + + // return <> + + +} + + +const YesNoText = ({ val }: { val: boolean | null }) => { + + return
+ { + val === true ? "Yes" + : val === false ? 'No' + : "Yes*" + } +
+ +} + + + +const abbreviateNumber = (num: number): string => { + if (num >= 1000000) { + // For millions + return Math.floor(num / 1000000) + 'M'; + } else if (num >= 1000) { + // For thousands + return Math.floor(num / 1000) + 'K'; + } else { + // For numbers less than 1000 + return num.toString(); + } +} + +const TableOfModelsForProvider = ({ providerName }: { providerName: ProviderName }) => { + + const accessor = useAccessor() + const voidSettingsService = accessor.get('IVoidSettingsService') + const voidSettingsState = useSettingsState() + const isDetectableLocally = (refreshableProviderNames as ProviderName[]).includes(providerName) + // const providerCapabilities = getProviderCapabilities(providerName) + + + // info used to show the table + const infoOfModelName: Record = {} + + voidSettingsState.settingsOfProvider[providerName].models.forEach(m => { + infoOfModelName[m.modelName] = { + showAsDefault: m.isDefault, + isDownloaded: true + } + }) + + // special case columns for ollama; show recommended models as default + if (providerName === 'ollama') { + for (const modelName of ollamaRecommendedModels) { + if (modelName in infoOfModelName) continue + infoOfModelName[modelName] = { + ...infoOfModelName[modelName], + showAsDefault: true, + } + } + } + + return + + + + + + + + + {/* */} + {isDetectableLocally && } + {providerName === 'ollama' && } + + + + {Object.keys(infoOfModelName).map(modelName => { + const { showAsDefault, isDownloaded } = infoOfModelName[modelName] + + const { + downloadable, + cost, + supportsTools, + supportsFIM, + reasoningCapabilities, + contextWindow, + + isUnrecognizedModel, + maxOutputTokens, + supportsSystemMessage, + } = getModelCapabilities(providerName, modelName) + + + const removeModelButton = + + + + return ( + + + + + + + + {/* */} + {isDetectableLocally && } + {providerName === 'ollama' && } + + + ) + })} + + + + + +
Models OfferedCost/MContextChatAgentAutotabReasoningDetectedDownload
+ {!showAsDefault && removeModelButton} + {modelName} + ${cost.output ?? ''}{contextWindow ? abbreviateNumber(contextWindow) : ''}{!!isDownloaded ? : <>} + +
+ +
+} + + + + +type WantToUseOption = 'smart' | 'private' | 'cheap' | 'all' + +const VoidOnboarding = () => { + + const accessor = useAccessor() + const voidSettingsService = accessor.get('IVoidSettingsService') + + const voidSettingsState = useSettingsState() + const isOnboardingComplete = false // voidSettingsService._isOnboardingComplete + + if (isOnboardingComplete) { + return null + } + + const [pageIndex, setPageIndex] = useState(0) + + + const skipButton = { setPageIndex(pageIndex + 1) }} /> + + + // page 1 state + const [wantToUseOption, setWantToUseOption] = useState('smart') + + // page 2 state + const [selectedProviderName, setSelectedProviderName] = useState(null) + + const providerNamesOfWantToUseOption: { [wantToUseOption in WantToUseOption]: ProviderName[] } = { + smart: ['anthropic', 'openAI', 'gemini', 'openRouter'], + private: ['ollama', 'vLLM', 'openAICompatible'], + cheap: ['gemini', 'deepseek', 'openRouter', 'ollama', 'vLLM'], + all: providerNames, + // TODO allow user to redo onboarding + } + + + const didFillInProviderSettings = selectedProviderName && voidSettingsState.settingsOfProvider[selectedProviderName]._didFillInProviderSettings + const isApiKeyLongEnoughIfApiKeyExists = selectedProviderName && voidSettingsState.settingsOfProvider[selectedProviderName].apiKey ? voidSettingsState.settingsOfProvider[selectedProviderName].apiKey.length > 15 : true + const isAtLeastOneModel = selectedProviderName && voidSettingsState.settingsOfProvider[selectedProviderName].models.length >= 1 + + const didFillInSelectedProviderSettings = !!(didFillInProviderSettings && isApiKeyLongEnoughIfApiKeyExists && isAtLeastOneModel) + + const prevAndNextButtons =
+ { setPageIndex(pageIndex - 1) }} + /> + { setPageIndex(pageIndex + 1) }} + disabled={pageIndex === 2 && !didFillInSelectedProviderSettings} + /> +
+ + + // cannot be md + const basicDescOfWantToUseOption: { [wantToUseOption in WantToUseOption]: string } = { + smart: "Models with the best performance on benchmarks.", + private: "Fully private and hosted on your computer/network.", + cheap: "Free and affordable options.", + all: "", + } + + // can be md + const detailedDescOfWantToUseOption: { [wantToUseOption in WantToUseOption]: string } = { + smart: "Most intelligent and best for agent mode.", + private: "Private-hosted so your data never leaves your computer or network. [Email us](mailto:founders@voideditor.com) for help setting up at your company.", + cheap: "Great deals like Gemini 2.5 Pro or self-host a model for free.", + all: "", + } + + // set the selected provider name appropriately + useEffect(() => { + if (wantToUseOption && providerNamesOfWantToUseOption[wantToUseOption].length > 0) { + setSelectedProviderName(providerNamesOfWantToUseOption[wantToUseOption][0]); + } else { + setSelectedProviderName(null); + } + }, [wantToUseOption]); + + // set wantToUseOption to smart when page changes + useEffect(() => { + setWantToUseOption(wantToUseOption); + }, [pageIndex]); + + + // TODO add a description next to the skip button saying (you can always restart the onboarding in Settings) + const contentOfIdx: { [pageIndex: number]: React.ReactNode } = { + 0:
+ + Welcome to Void + + Image +
+ Void Logo +
+
+ + { setPageIndex(pageIndex + 1) }}> + Get Started + +
, + 1:
+ + + +
AI Preferences
+ +
+ +
What are you looking for in an AI model?
+ +
+
{ setWantToUseOption('smart'); setPageIndex(pageIndex + 1); }} + className="flex flex-col items-center justify-center p-6 rounded-md transition-all duration-300 cursor-pointer md:aspect-[8/7] border-void-border-1 border bg-gradient-to-br from-[#0e70c0]/15 via-[#0e70c0]/5 to-transparent hover:from-[#0e70c0]/25 hover:via-[#0e70c0]/10 hover:to-[#0e70c0]/5 dark:from-[#0e70c0]/20 dark:via-[#0e70c0]/10 dark:to-[#0e70c0]/5 dark:hover:from-[#0e70c0]/30 dark:hover:via-[#0e70c0]/15 dark:hover:to-[#0e70c0]/5" + > + 🧠 +

Intelligence

+

{basicDescOfWantToUseOption['smart']}

+
+ +
{ setWantToUseOption('private'); setPageIndex(pageIndex + 1); }} + className="flex flex-col items-center justify-center p-6 rounded-md transition-all duration-300 cursor-pointer md:aspect-[8/7] border-void-border-1 border bg-gradient-to-br from-[#0e70c0]/15 via-[#0e70c0]/5 to-transparent hover:from-[#0e70c0]/25 hover:via-[#0e70c0]/10 hover:to-[#0e70c0]/5 dark:from-[#0e70c0]/20 dark:via-[#0e70c0]/10 dark:to-[#0e70c0]/5 dark:hover:from-[#0e70c0]/30 dark:hover:via-[#0e70c0]/15 dark:hover:to-[#0e70c0]/5" + > + 🔒 +

Privacy

+

{basicDescOfWantToUseOption['private']}

+
+ +
{ setWantToUseOption('cheap'); setPageIndex(pageIndex + 1); }} + className="flex flex-col items-center justify-center p-6 rounded-md transition-all duration-300 cursor-pointer md:aspect-[8/7] border-void-border-1 border bg-gradient-to-br from-[#0e70c0]/15 via-[#0e70c0]/5 to-transparent hover:from-[#0e70c0]/25 hover:via-[#0e70c0]/10 hover:to-[#0e70c0]/5 dark:from-[#0e70c0]/20 dark:via-[#0e70c0]/10 dark:to-[#0e70c0]/5 dark:hover:from-[#0e70c0]/30 dark:hover:via-[#0e70c0]/15 dark:hover:to-[#0e70c0]/5" + > + 💵 +

Low-Cost

+

{basicDescOfWantToUseOption['cheap']}

+
+
+
+ +
+ +
+ {prevAndNextButtons} +
+ +
, + 2:
+ + +
Choose a Provider
+ +
+ + + + +
+ + {/* Provider Buttons */} +
+ + {(wantToUseOption === 'all' ? providerNames : providerNamesOfWantToUseOption[wantToUseOption]).map((providerName) => { + const isSelected = selectedProviderName === providerName + + return ( + + ) + })} + +
+ + {/* Description */} +
+ +
+ +
+ +
+ + + {/* ModelsTable and ProviderFields */} + {selectedProviderName &&
+ + + {/* Models Table */} + + + + {/* Add provider section - simplified styling */} +
+
+ Add {displayInfoOfProviderName(selectedProviderName).title} + + + {selectedProviderName === 'ollama' ? ollamaSetupInstructions : ''} + +
+ + {selectedProviderName && + + } + + {/* Button and status indicators */} + {!didFillInProviderSettings ?

Please fill in all fields to continue

+ : !isAtLeastOneModel ?

Please add a model to continue

+ : !isApiKeyLongEnoughIfApiKeyExists ?

Please enter a valid API key

+ :
} +
+ + +
} + +
+ + {prevAndNextButtons} +
, + // 2.5:
+ // + //
Autocomplete
+ + //
+ //

Void offers free autocomplete with locally hosted models

+ //

[have buttons for Ollama install Qwen2.5coder3b and memory requirements]

+ + //
+ //
+ + // {prevAndNextButtons} + //
, + 3:
+ +
Settings and Themes
+ +
+

Transfer your settings from an existing editor?

+ + + +
+ +
+ + {prevAndNextButtons} +
, + 4:
+ + Jump in + + + + Enter the Void + + + {prevAndNextButtons} +
, + } + + + return
+ {contentOfIdx[pageIndex]} +
+} diff --git a/src/vs/workbench/contrib/void/browser/voidSelectionHelperWidget.ts b/src/vs/workbench/contrib/void/browser/voidSelectionHelperWidget.ts index cd0df1e7..57135ef1 100644 --- a/src/vs/workbench/contrib/void/browser/voidSelectionHelperWidget.ts +++ b/src/vs/workbench/contrib/void/browser/voidSelectionHelperWidget.ts @@ -125,6 +125,10 @@ export class SelectionHelperContribution extends Disposable implements IEditorCo return; } + if (this._editor.getModel().uri.scheme !== 'file') { + return; + } + const selection = this._editor.getSelection(); if (!selection || selection.isEmpty()) { diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index e037114d..17536043 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -6,6 +6,46 @@ import { FeatureName, ModelSelectionOptions, ProviderName } from './voidSettingsTypes.js'; + + + +export const defaultProviderSettings = { + anthropic: { + apiKey: '', + }, + openAI: { + apiKey: '', + }, + deepseek: { + apiKey: '', + }, + ollama: { + endpoint: 'http://127.0.0.1:11434', + }, + vLLM: { + endpoint: 'http://localhost:8000', + }, + openRouter: { + apiKey: '', + }, + openAICompatible: { + endpoint: '', + apiKey: '', + }, + gemini: { + apiKey: '', + }, + groq: { + apiKey: '', + }, + xAI: { + apiKey: '' + }, +} as const + + + + export const defaultModelsOfProvider = { openAI: [ // https://platform.openai.com/docs/models/gp 'o3-mini', @@ -68,20 +108,22 @@ export const defaultModelsOfProvider = { - - - -type ModelOptions = { +export type VoidStaticModelInfo = { // not stateful contextWindow: number; // input tokens maxOutputTokens: number | null; // output tokens, defaults to 4092 - cost: { // <-- UNUSED + cost: { // <-- UNUSED input: number; output: number; cache_read?: number; cache_write?: number; } - supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated'; - supportsTools: false | 'anthropic-style' | 'openai-style'; + + downloadable: false | { + sizeGb: number | 'not-known' + } + + supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated'; // separated = anthropic where "system" is a special parameter + supportsTools: false | 'TODO-yes-but-we-handle-it-manually' | 'anthropic-style' | 'openai-style'; supportsFIM: boolean; reasoningCapabilities: false | { @@ -109,18 +151,19 @@ type ProviderReasoningIOSettings = { | { nameOfFieldInDelta?: undefined, needsManualParse?: true, }; } -type ProviderSettings = { +type VoidStaticProviderInfo = { // doesn't change (not stateful) providerReasoningIOSettings?: ProviderReasoningIOSettings; // input/output settings around thinking (allowed to be empty) - only applied if the model supports reasoning output - modelOptions: { [key: string]: ModelOptions }; - modelOptionsFallback: (modelName: string) => (ModelOptions & { modelName: string }) | null; + modelOptions: { [key: string]: VoidStaticModelInfo }; + modelOptionsFallback: (modelName: string, fallbackKnownValues?: Partial) => (VoidStaticModelInfo & { modelName: string }) | null; } -const modelOptionsDefaults: ModelOptions = { +const modelOptionsDefaults: VoidStaticModelInfo = { contextWindow: 32_000, maxOutputTokens: 4_096, cost: { input: 0, output: 0 }, + downloadable: false, supportsSystemMessage: false, supportsTools: false, supportsFIM: false, @@ -261,21 +304,24 @@ const openSourceModelOptions_assumingOAICompat = { contextWindow: 128_000, maxOutputTokens: 8_192, }, -} as const satisfies { [s: string]: Omit } +} as const satisfies { [s: string]: Partial } -const extensiveModelFallback: ProviderSettings['modelOptionsFallback'] = (modelName) => { +const extensiveModelFallback: VoidStaticProviderInfo['modelOptionsFallback'] = (modelName, fallbackKnownValues) => { + const lower = modelName.toLowerCase() - const toFallback = (opts: Omit): ModelOptions & { modelName: string } => { + const toFallback = (opts: Omit): VoidStaticModelInfo & { modelName: string } => { return { modelName, ...opts, supportsSystemMessage: opts.supportsSystemMessage ? 'system-role' : false, cost: { input: 0, output: 0 }, + downloadable: false, + ...fallbackKnownValues } } if (Object.keys(openSourceModelOptions_assumingOAICompat).map(k => k.toLowerCase()).includes(lower)) @@ -332,6 +378,7 @@ const anthropicModelOptions = { contextWindow: 200_000, maxOutputTokens: 8_192, cost: { input: 3.00, cache_read: 0.30, cache_write: 3.75, output: 15.00 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'separated', supportsTools: 'anthropic-style', @@ -347,6 +394,7 @@ const anthropicModelOptions = { contextWindow: 200_000, maxOutputTokens: 8_192, cost: { input: 3.00, cache_read: 0.30, cache_write: 3.75, output: 15.00 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'separated', supportsTools: 'anthropic-style', @@ -356,6 +404,7 @@ const anthropicModelOptions = { contextWindow: 200_000, maxOutputTokens: 8_192, cost: { input: 0.80, cache_read: 0.08, cache_write: 1.00, output: 4.00 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'separated', supportsTools: 'anthropic-style', @@ -365,6 +414,7 @@ const anthropicModelOptions = { contextWindow: 200_000, maxOutputTokens: 4_096, cost: { input: 15.00, cache_read: 1.50, cache_write: 18.75, output: 75.00 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'separated', supportsTools: 'anthropic-style', @@ -372,15 +422,16 @@ const anthropicModelOptions = { }, 'claude-3-sonnet-20240229': { // no point of using this, but including this for people who put it in contextWindow: 200_000, cost: { input: 3.00, output: 15.00 }, + downloadable: false, maxOutputTokens: 4_096, supportsFIM: false, supportsSystemMessage: 'separated', supportsTools: 'anthropic-style', reasoningCapabilities: false, } -} as const satisfies { [s: string]: ModelOptions } +} as const satisfies { [s: string]: VoidStaticModelInfo } -const anthropicSettings: ProviderSettings = { +const anthropicSettings: VoidStaticProviderInfo = { providerReasoningIOSettings: { input: { includeInPayload: (reasoningInfo) => { @@ -412,6 +463,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing contextWindow: 128_000, maxOutputTokens: 100_000, cost: { input: 15.00, cache_read: 7.50, output: 60.00, }, + downloadable: false, supportsFIM: false, supportsTools: false, supportsSystemMessage: 'developer-role', @@ -421,6 +473,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing contextWindow: 200_000, maxOutputTokens: 100_000, cost: { input: 1.10, cache_read: 0.55, output: 4.40, }, + downloadable: false, supportsFIM: false, supportsTools: false, supportsSystemMessage: 'developer-role', @@ -430,6 +483,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing contextWindow: 128_000, maxOutputTokens: 16_384, cost: { input: 2.50, cache_read: 1.25, output: 10.00, }, + downloadable: false, supportsFIM: false, supportsTools: 'openai-style', supportsSystemMessage: 'system-role', @@ -439,6 +493,7 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing contextWindow: 128_000, maxOutputTokens: 65_536, cost: { input: 1.10, cache_read: 0.55, output: 4.40, }, + downloadable: false, supportsFIM: false, supportsTools: false, supportsSystemMessage: false, // does not support any system @@ -448,15 +503,16 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing contextWindow: 128_000, maxOutputTokens: 16_384, cost: { input: 0.15, cache_read: 0.075, output: 0.60, }, + downloadable: false, supportsFIM: false, supportsTools: 'openai-style', supportsSystemMessage: 'system-role', // ?? reasoningCapabilities: false, }, -} as const satisfies { [s: string]: ModelOptions } +} as const satisfies { [s: string]: VoidStaticModelInfo } -const openAISettings: ProviderSettings = { +const openAISettings: VoidStaticProviderInfo = { modelOptions: openAIModelOptions, modelOptionsFallback: (modelName) => { const lower = modelName.toLowerCase() @@ -475,14 +531,15 @@ const xAIModelOptions = { contextWindow: 131_072, maxOutputTokens: null, // 131_072, cost: { input: 2.00, output: 10.00 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', reasoningCapabilities: false, }, -} as const satisfies { [s: string]: ModelOptions } +} as const satisfies { [s: string]: VoidStaticModelInfo } -const xAISettings: ProviderSettings = { +const xAISettings: VoidStaticProviderInfo = { modelOptions: xAIModelOptions, modelOptionsFallback: (modelName) => { const lower = modelName.toLowerCase() @@ -500,6 +557,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing contextWindow: 1_048_576, maxOutputTokens: 8_192, cost: { input: 0, output: 0 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', // we are assuming OpenAI SDK when calling gemini @@ -509,6 +567,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing contextWindow: 1_048_576, maxOutputTokens: 8_192, // 8_192, cost: { input: 0.10, output: 0.40 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', // we are assuming OpenAI SDK when calling gemini @@ -518,6 +577,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing contextWindow: 1_048_576, maxOutputTokens: 8_192, // 8_192, cost: { input: 0.075, output: 0.30 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -527,6 +587,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing contextWindow: 1_048_576, maxOutputTokens: 8_192, // 8_192, cost: { input: 0.075, output: 0.30 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -536,6 +597,7 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing contextWindow: 2_097_152, maxOutputTokens: 8_192, cost: { input: 1.25, output: 5.00 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -545,14 +607,15 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing contextWindow: 1_048_576, maxOutputTokens: 8_192, cost: { input: 0.0375, output: 0.15 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', reasoningCapabilities: false, }, -} as const satisfies { [s: string]: ModelOptions } +} as const satisfies { [s: string]: VoidStaticModelInfo } -const geminiSettings: ProviderSettings = { +const geminiSettings: VoidStaticProviderInfo = { modelOptions: geminiModelOptions, modelOptionsFallback: (modelName) => { return null } } @@ -566,17 +629,19 @@ const deepseekModelOptions = { contextWindow: 64_000, // https://api-docs.deepseek.com/quick_start/pricing maxOutputTokens: 8_000, // 8_000, cost: { cache_read: .07, input: .27, output: 1.10, }, + downloadable: false, }, 'deepseek-reasoner': { ...openSourceModelOptions_assumingOAICompat.deepseekCoderV2, contextWindow: 64_000, maxOutputTokens: 8_000, // 8_000, cost: { cache_read: .14, input: .55, output: 2.19, }, + downloadable: false, }, -} as const satisfies { [s: string]: ModelOptions } +} as const satisfies { [s: string]: VoidStaticModelInfo } -const deepseekSettings: ProviderSettings = { +const deepseekSettings: VoidStaticProviderInfo = { modelOptions: deepseekModelOptions, providerReasoningIOSettings: { // reasoning: OAICompat + response.choices[0].delta.reasoning_content // https://api-docs.deepseek.com/guides/reasoning_model @@ -591,6 +656,7 @@ const groqModelOptions = { // https://console.groq.com/docs/models, https://groq contextWindow: 128_000, maxOutputTokens: 32_768, // 32_768, cost: { input: 0.59, output: 0.79 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -600,6 +666,7 @@ const groqModelOptions = { // https://console.groq.com/docs/models, https://groq contextWindow: 128_000, maxOutputTokens: 8_192, cost: { input: 0.05, output: 0.08 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -609,6 +676,7 @@ const groqModelOptions = { // https://console.groq.com/docs/models, https://groq contextWindow: 128_000, maxOutputTokens: null, // not specified? cost: { input: 0.79, output: 0.79 }, + downloadable: false, supportsFIM: false, // unfortunately looks like no FIM support on groq supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -618,13 +686,14 @@ const groqModelOptions = { // https://console.groq.com/docs/models, https://groq contextWindow: 128_000, maxOutputTokens: null, // not specified? cost: { input: 0.29, output: 0.39 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', reasoningCapabilities: { supportsReasoning: true, canIOReasoning: true, canTurnOffReasoning: false, openSourceThinkTags: ['', ''] }, // we're using reasoning_format:parsed so really don't need to know openSourceThinkTags }, -} as const satisfies { [s: string]: ModelOptions } -const groqSettings: ProviderSettings = { +} as const satisfies { [s: string]: VoidStaticModelInfo } +const groqSettings: VoidStaticProviderInfo = { providerReasoningIOSettings: { input: { includeInPayload: (reasoningInfo) => { @@ -640,23 +709,71 @@ const groqSettings: ProviderSettings = { modelOptionsFallback: (modelName) => { return null } } +const ollamaModelOptions = { + 'qwen2.5-coder:3b': { + contextWindow: 32_000, + maxOutputTokens: null, + cost: { input: 0, output: 0 }, + downloadable: { sizeGb: 1.9 }, + supportsFIM: true, + supportsSystemMessage: 'system-role', + supportsTools: false, + reasoningCapabilities: false, + }, + 'qwen2.5-coder': { + contextWindow: 128_000, + maxOutputTokens: null, + cost: { input: 0, output: 0 }, + downloadable: { sizeGb: 4.7 }, + supportsFIM: false, + supportsSystemMessage: 'system-role', + supportsTools: false, + reasoningCapabilities: false, + }, + 'qwq': { + contextWindow: 128_000, + maxOutputTokens: 32_000, + cost: { input: 0, output: 0 }, + downloadable: { sizeGb: 20 }, + supportsFIM: false, + supportsSystemMessage: 'system-role', + supportsTools: 'TODO-yes-but-we-handle-it-manually', + reasoningCapabilities: { supportsReasoning: true, canIOReasoning: false, canTurnOffReasoning: false, openSourceThinkTags: ['', ''] }, + }, + 'deepseek-r1': { + contextWindow: 128_000, + maxOutputTokens: null, + cost: { input: 0, output: 0 }, + downloadable: { sizeGb: 4.7 }, + supportsFIM: false, + supportsSystemMessage: 'system-role', + supportsTools: 'TODO-yes-but-we-handle-it-manually', + reasoningCapabilities: { supportsReasoning: true, canIOReasoning: false, canTurnOffReasoning: false, openSourceThinkTags: ['', ''] }, + }, + +} as const satisfies Record + +export const ollamaRecommendedModels = ['qwen2.5-coder:3b', 'qwq', 'deepseek-r1'] as const satisfies (keyof typeof ollamaModelOptions)[] + + // ---------------- VLLM, OLLAMA, OPENAICOMPAT (self-hosted / local) ---------------- -const vLLMSettings: ProviderSettings = { + +const vLLMSettings: VoidStaticProviderInfo = { // reasoning: OAICompat + response.choices[0].delta.reasoning_content // https://docs.vllm.ai/en/stable/features/reasoning_outputs.html#streaming-chat-completions providerReasoningIOSettings: { output: { nameOfFieldInDelta: 'reasoning_content' }, }, - modelOptionsFallback: (modelName) => extensiveModelFallback(modelName), - modelOptions: {}, + modelOptionsFallback: (modelName) => extensiveModelFallback(modelName, { downloadable: { sizeGb: 'not-known' } }), + modelOptions: {}, // TODO } -const ollamaSettings: ProviderSettings = { +const ollamaSettings: VoidStaticProviderInfo = { // reasoning: we need to filter out reasoning tags manually providerReasoningIOSettings: { output: { needsManualParse: true }, }, - modelOptionsFallback: (modelName) => extensiveModelFallback(modelName), - modelOptions: {}, + modelOptionsFallback: (modelName) => extensiveModelFallback(modelName, { downloadable: { sizeGb: 'not-known' } }), + modelOptions: ollamaModelOptions, } -const openaiCompatible: ProviderSettings = { +const openaiCompatible: VoidStaticProviderInfo = { // reasoning: we have no idea what endpoint they used, so we can't consistently parse out reasoning modelOptionsFallback: (modelName) => extensiveModelFallback(modelName), modelOptions: {}, @@ -669,6 +786,7 @@ const openRouterModelOptions_assumingOpenAICompat = { contextWindow: 128_000, maxOutputTokens: null, cost: { input: 0, output: 0 }, + downloadable: false, supportsFIM: false, supportsTools: 'openai-style', supportsSystemMessage: 'system-role', @@ -678,6 +796,7 @@ const openRouterModelOptions_assumingOpenAICompat = { contextWindow: 1_048_576, maxOutputTokens: null, cost: { input: 0, output: 0 }, + downloadable: false, supportsFIM: false, supportsTools: 'openai-style', supportsSystemMessage: 'system-role', @@ -687,6 +806,7 @@ const openRouterModelOptions_assumingOpenAICompat = { contextWindow: 1_048_576, maxOutputTokens: null, cost: { input: 0, output: 0 }, + downloadable: false, supportsFIM: false, supportsTools: 'openai-style', supportsSystemMessage: 'system-role', @@ -696,6 +816,7 @@ const openRouterModelOptions_assumingOpenAICompat = { contextWindow: 1_048_576, maxOutputTokens: null, cost: { input: 0, output: 0 }, + downloadable: false, supportsFIM: false, supportsTools: 'openai-style', supportsSystemMessage: 'system-role', @@ -706,11 +827,13 @@ const openRouterModelOptions_assumingOpenAICompat = { contextWindow: 128_000, maxOutputTokens: null, cost: { input: 0.8, output: 2.4 }, + downloadable: false, }, 'anthropic/claude-3.7-sonnet:thinking': { contextWindow: 200_000, maxOutputTokens: null, cost: { input: 3.00, output: 15.00 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -726,6 +849,7 @@ const openRouterModelOptions_assumingOpenAICompat = { contextWindow: 200_000, maxOutputTokens: null, cost: { input: 3.00, output: 15.00 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -735,6 +859,7 @@ const openRouterModelOptions_assumingOpenAICompat = { contextWindow: 200_000, maxOutputTokens: null, cost: { input: 3.00, output: 15.00 }, + downloadable: false, supportsFIM: false, supportsSystemMessage: 'system-role', supportsTools: 'openai-style', @@ -745,6 +870,7 @@ const openRouterModelOptions_assumingOpenAICompat = { contextWindow: 256_000, maxOutputTokens: null, cost: { input: 0.3, output: 0.9 }, + downloadable: false, supportsTools: 'openai-style', reasoningCapabilities: false, }, @@ -754,6 +880,7 @@ const openRouterModelOptions_assumingOpenAICompat = { maxOutputTokens: null, supportsTools: false, // openrouter qwen doesn't seem to support tools...? cost: { input: 0.07, output: 0.16 }, + downloadable: false, }, 'qwen/qwq-32b': { ...openSourceModelOptions_assumingOAICompat['qwq'], @@ -761,10 +888,11 @@ const openRouterModelOptions_assumingOpenAICompat = { maxOutputTokens: null, supportsTools: false, // openrouter qwen doesn't seem to support tools...? cost: { input: 0.07, output: 0.16 }, + downloadable: false, } -} as const satisfies { [s: string]: ModelOptions } +} as const satisfies { [s: string]: VoidStaticModelInfo } -const openRouterSettings: ProviderSettings = { +const openRouterSettings: VoidStaticProviderInfo = { // reasoning: OAICompat + response.choices[0].delta.reasoning : payload should have {include_reasoning: true} https://openrouter.ai/announcements/reasoning-tokens-for-thinking-models providerReasoningIOSettings: { input: { @@ -791,7 +919,7 @@ const openRouterSettings: ProviderSettings = { // ---------------- model settings of everything above ---------------- -const modelSettingsOfProvider: { [providerName in ProviderName]: ProviderSettings } = { +const modelSettingsOfProvider: { [providerName in ProviderName]: VoidStaticProviderInfo } = { openAI: openAISettings, anthropic: anthropicSettings, xAI: xAISettings, @@ -817,8 +945,10 @@ const modelSettingsOfProvider: { [providerName in ProviderName]: ProviderSetting // ---------------- exports ---------------- // returns the capabilities and the adjusted modelName if it was a fallback -export const getModelCapabilities = (providerName: ProviderName, modelName: string): ModelOptions & { modelName: string; isUnrecognizedModel: boolean } => { +export const getModelCapabilities = (providerName: ProviderName, modelName: string): VoidStaticModelInfo & { modelName: string; isUnrecognizedModel: boolean } => { + const lowercaseModelName = modelName.toLowerCase() + const { modelOptions, modelOptionsFallback } = modelSettingsOfProvider[providerName] // search model options object directly first diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index c80a0a3b..fb056de2 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -85,6 +85,10 @@ export const voidTools = { } }, + // pathname_search: { + // name: 'pathname_search', + // description: `Returns all pathnames that match a given \`find\`-style query over the entire workspace. ONLY searches file names. ONLY searches the current workspace. You should use this when looking for a file with a specific name or path. ${paginationHelper.desc}`, + search_pathnames_only: { name: 'search_pathnames_only', description: `Returns all pathnames that match a given query (searches ONLY file names). You should use this when looking for a file with a specific name or path. ${paginationHelper.desc}`, @@ -95,6 +99,8 @@ export const voidTools = { }, }, + + search_files: { name: 'search_files', description: `Returns all pathnames that match a given \`grep\`-style query (searches ONLY file contents). The query can be any regex. This is often followed by the \`read_file\` tool to view the full file contents of results. ${paginationHelper.desc}`, diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts index 47558e71..d9d8a584 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts @@ -11,9 +11,9 @@ import { registerSingleton, InstantiationType } from '../../../../platform/insta import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { IMetricsService } from './metricsService.js'; -import { getModelCapabilities } from './modelCapabilities.js'; +import { defaultProviderSettings, getModelCapabilities } from './modelCapabilities.js'; import { VOID_SETTINGS_STORAGE_KEY } from './storageKeys.js'; -import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, defaultProviderSettings, ModelSelectionOptions, OptionsOfModelSelection, ChatMode } from './voidSettingsTypes.js'; +import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidStatefulModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, ModelSelectionOptions, OptionsOfModelSelection, ChatMode } from './voidSettingsTypes.js'; // name is the name in the dropdown @@ -71,10 +71,10 @@ export interface IVoidSettingsService { -const _updatedModelsAfterDefaultModelsChange = (defaultModelNames: string[], options: { existingModels: VoidModelInfo[] }) => { +const _updatedModelsAfterDefaultModelsChange = (defaultModelNames: string[], options: { existingModels: VoidStatefulModelInfo[] }) => { const { existingModels } = options - const existingModelsMap: Record = {} + const existingModelsMap: Record = {} for (const existingModel of existingModels) { existingModelsMap[existingModel.modelName] = existingModel } @@ -363,7 +363,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { const modelIdx = models.findIndex(m => m.modelName === modelName) if (modelIdx === -1) return const newIsHidden = !models[modelIdx].isHidden - const newModels: VoidModelInfo[] = [ + const newModels: VoidStatefulModelInfo[] = [ ...models.slice(0, modelIdx), { ...models[modelIdx], isHidden: newIsHidden }, ...models.slice(modelIdx + 1, Infinity) diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index fc402c7d..47c1f515 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -4,49 +4,13 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { defaultModelsOfProvider } from './modelCapabilities.js'; +import { defaultModelsOfProvider, defaultProviderSettings } from './modelCapabilities.js'; import { VoidSettingsState } from './voidSettingsService.js' type UnionOfKeys = T extends T ? keyof T : never; -export const defaultProviderSettings = { - anthropic: { - apiKey: '', - }, - openAI: { - apiKey: '', - }, - deepseek: { - apiKey: '', - }, - ollama: { - endpoint: 'http://127.0.0.1:11434', - }, - vLLM: { - endpoint: 'http://localhost:8000', - }, - openRouter: { - apiKey: '', - }, - openAICompatible: { - endpoint: '', - apiKey: '', - }, - gemini: { - apiKey: '', - }, - groq: { - apiKey: '', - }, - xAI: { - apiKey: '' - }, -} as const - - - export type ProviderName = keyof typeof defaultProviderSettings export const providerNames = Object.keys(defaultProviderSettings) as ProviderName[] @@ -64,7 +28,7 @@ export const customSettingNamesOfProvider = (providerName: ProviderName) => { -export type VoidModelInfo = { // <-- STATEFUL +export type VoidStatefulModelInfo = { // <-- STATEFUL modelName: string, isDefault: boolean, // whether or not it's a default for its provider isHidden: boolean, // whether or not the user is hiding it (switched off) @@ -75,7 +39,7 @@ export type VoidModelInfo = { // <-- STATEFUL type CommonProviderSettings = { _didFillInProviderSettings: boolean | undefined, // undefined initially, computed when user types in all fields - models: VoidModelInfo[], + models: VoidStatefulModelInfo[], } export type SettingsAtProvider = CustomProviderSettings & CommonProviderSettings @@ -227,7 +191,7 @@ const defaultCustomSettings: Record = { } -const modelInfoOfDefaultModelNames = (defaultModelNames: string[]): { models: VoidModelInfo[] } => { +const modelInfoOfDefaultModelNames = (defaultModelNames: string[]): { models: VoidStatefulModelInfo[] } => { return { models: defaultModelNames.map((modelName, i) => ({ modelName, @@ -334,6 +298,8 @@ export const displayInfoOfFeatureName = (featureName: FeatureName) => { export const refreshableProviderNames = localProviderNames export type RefreshableProviderName = typeof refreshableProviderNames[number] +// models that come with download buttons +export const hasDownloadButtonsOnModelsProviderNames = ['ollama'] as const satisfies ProviderName[] diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts index f14fb285..88223020 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/preprocessLLMMessages.ts @@ -372,7 +372,7 @@ const prepareMessages_tools_anthropic = ({ messages }: { messages: InternalLLMCh type PrepareMessagesTools = PrepareMessagesToolsAnthropic | PrepareMessagesToolsOpenAI -const prepareMessages_tools = ({ messages, supportsTools }: { messages: InternalLLMChatMessage[], supportsTools: false | 'anthropic-style' | 'openai-style' }): { messages: PrepareMessagesTools } => { +const prepareMessages_tools = ({ messages, supportsTools }: { messages: InternalLLMChatMessage[], supportsTools: false | 'TODO-yes-but-we-handle-it-manually' | 'anthropic-style' | 'openai-style' }): { messages: PrepareMessagesTools } => { if (!supportsTools) { return { messages: messages } } @@ -466,7 +466,7 @@ export const prepareMessages = ({ messages: LLMChatMessage[], aiInstructions: string, supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated', - supportsTools: false | 'anthropic-style' | 'openai-style', + supportsTools: false | 'TODO-yes-but-we-handle-it-manually' | 'anthropic-style' | 'openai-style', supportsAnthropicReasoningSignature: boolean, contextWindow: number, maxOutputTokens: number | null | undefined, diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index 05610f6a..58ccc443 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -9,9 +9,9 @@ import OpenAI, { ClientOptions } from 'openai'; import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '../../common/helpers/extractCodeFromResult.js'; import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/sendLLMMessageTypes.js'; -import { defaultProviderSettings, displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; +import { displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; import { prepareFIMMessage, prepareMessages } from './preprocessLLMMessages.js'; -import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities } from '../../common/modelCapabilities.js'; +import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities, defaultProviderSettings } from '../../common/modelCapabilities.js'; import { InternalToolInfo, ToolName, isAToolName } from '../../common/toolsServiceTypes.js';