diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 620548ec..4d1b7462 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -86,7 +86,7 @@ function prepareDebPackage(arch) { const dependencies = await dependenciesGenerator.getDependencies('deb', binaryDir, product.applicationName, debArch); gulp.src('resources/linux/debian/control.template', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`)) + .pipe(replace('@@VERSION@@', `${product.voidVersion}.${packageJson.release}`)) .pipe(replace('@@ARCHITECTURE@@', debArch)) .pipe(replace('@@DEPENDS@@', dependencies.join(', '))) .pipe(replace('@@RECOMMENDS@@', debianRecommendedDependencies.join(', '))) @@ -201,7 +201,7 @@ function prepareRpmPackage(arch) { .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@ICON@@', product.linuxIconName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`)) + .pipe(replace('@@VERSION@@', `${product.voidVersion}.${packageJson.release}`)) .pipe(replace('@@ARCHITECTURE@@', rpmArch)) .pipe(replace('@@LICENSE@@', product.licenseName)) .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) @@ -277,7 +277,7 @@ function prepareSnapPackage(arch) { const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`)) + .pipe(replace('@@VERSION@@', `${product.voidVersion}.${packageJson.release}`)) // Possible run-on values https://snapcraft.io/docs/architectures .pipe(replace('@@ARCHITECTURE@@', arch === 'x64' ? 'amd64' : arch)) .pipe(rename('snap/snapcraft.yaml')); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index a1175ae3..09c89795 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -22,10 +22,12 @@ import { ColorScheme } from '../../web.api.js'; import { OpenFileFolderAction, OpenFolderAction } from '../../actions/workspaceActions.js'; import { IWindowOpenable } from '../../../../platform/window/common/window.js'; import { splitRecentLabel } from '../../../../base/common/labels.js'; +import { IViewsService } from '../../../services/views/common/viewsService.js'; /* eslint-disable */ // Void import { VOID_CTRL_K_ACTION_ID, VOID_CTRL_L_ACTION_ID } from '../../../contrib/void/browser/actionIDs.js'; import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../contrib/void/browser/voidSettingsPane.js'; +import { VIEWLET_ID as REMOTE_EXPLORER_VIEWLET_ID } from '../../../contrib/remote/browser/remoteExplorer.js'; /* eslint-enable */ // interface WatermarkEntry { @@ -98,6 +100,7 @@ export class EditorGroupWatermark extends Disposable { @ICommandService private readonly commandService: ICommandService, @IHostService private readonly hostService: IHostService, @ILabelService private readonly labelService: ILabelService, + @IViewsService private readonly viewsService: IViewsService, ) { super(); @@ -182,14 +185,21 @@ export class EditorGroupWatermark extends Disposable { // Void - if the workbench is empty, show open if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + // Create a flex container for buttons with vertical direction + const buttonContainer = $('div'); + buttonContainer.style.display = 'flex'; + buttonContainer.style.flexDirection = 'column'; // Change to column for vertical stacking + buttonContainer.style.alignItems = 'center'; // Center the buttons horizontally + buttonContainer.style.gap = '8px'; // Reduce gap between buttons from 16px to 8px + buttonContainer.style.marginBottom = '16px'; + voidIconBox.appendChild(buttonContainer); + // Open a folder const openFolderButton = h('button') - openFolderButton.root.classList.add('void-watermark-button') + openFolderButton.root.classList.add('void-openfolder-button') openFolderButton.root.style.display = 'block' - openFolderButton.root.style.marginLeft = 'auto' - openFolderButton.root.style.marginRight = 'auto' - openFolderButton.root.style.marginBottom = '16px' - openFolderButton.root.textContent = 'Open a folder' + openFolderButton.root.style.width = '124px' // Set width to 124px as requested + openFolderButton.root.textContent = 'Open Folder' openFolderButton.root.onclick = () => { this.commandService.executeCommand(isMacintosh && isNative ? OpenFileFolderAction.ID : OpenFolderAction.ID) // if (this.contextKeyService.contextMatchesRules(ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace')))) { @@ -198,7 +208,19 @@ export class EditorGroupWatermark extends Disposable { // this.commandService.executeCommand(isMacintosh ? 'workbench.action.files.openFileFolder' : 'workbench.action.files.openFolder'); // } } - voidIconBox.appendChild(openFolderButton.root); + buttonContainer.appendChild(openFolderButton.root); + + // Open SSH button + const openSSHButton = h('button') + openSSHButton.root.classList.add('void-openssh-button') + openSSHButton.root.style.display = 'block' + openSSHButton.root.style.backgroundColor = '#5a5a5a' // Made darker than the default gray + openSSHButton.root.style.width = '124px' // Set width to 124px as requested + openSSHButton.root.textContent = 'Open SSH' + openSSHButton.root.onclick = () => { + this.viewsService.openViewContainer(REMOTE_EXPLORER_VIEWLET_ID); + } + buttonContainer.appendChild(openSSHButton.root); // Recents @@ -244,6 +266,9 @@ export class EditorGroupWatermark extends Disposable { const dirSpan = $('span'); dirSpan.style.paddingLeft = '4px'; + dirSpan.style.whiteSpace = 'nowrap'; + dirSpan.style.overflow = 'hidden'; + dirSpan.style.maxWidth = '300px'; dirSpan.innerText = parentPath; dirSpan.title = fullPath; diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index 93cf0be6..f79d0c9c 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -50,6 +50,7 @@ } /* light */ +.void-void-icon, .monaco-workbench .part.editor > .content .editor-group-container > .editor-group-watermark > .letterpress { width: 100%; max-height: 100%; @@ -60,14 +61,17 @@ background-repeat: no-repeat; } +.void-void-icon, .monaco-workbench.vs-dark .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress { background-image: url('./void_cube_noshadow.png'); /* // Void */ } +.void-void-icon, .monaco-workbench.hc-light .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress { background-image: url('./void_cube_noshadow.png'); /* // Void */ } +.void-void-icon, .monaco-workbench.hc-black .part.editor > .content .editor-group-container .editor-group-watermark > .letterpress { background-image: url('./void_cube_noshadow.png'); /* // Void */ } diff --git a/src/vs/workbench/contrib/void/browser/autocompleteService.ts b/src/vs/workbench/contrib/void/browser/autocompleteService.ts index 5bf1200d..95ec65c2 100644 --- a/src/vs/workbench/contrib/void/browser/autocompleteService.ts +++ b/src/vs/workbench/contrib/void/browser/autocompleteService.ts @@ -792,7 +792,6 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ const featureName: FeatureName = 'Autocomplete' const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined - const aiInstructions = this._settingsService.state.globalSettings.aiInstructions // set parameters of `newAutocompletion` appropriately newAutocompletion.llmPromise = new Promise((resolve, reject) => { @@ -804,8 +803,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ prefix: llmPrefix, suffix: llmSuffix, stopTokens: stopTokens, - }, - aiInstructions + } }), modelSelection, modelSelectionOptions, diff --git a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts index 053d25bd..19606415 100644 --- a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts +++ b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts @@ -13,6 +13,8 @@ import { IVoidSettingsService } from '../common/voidSettingsService.js'; import { ChatMode, FeatureName, ModelSelection } from '../common/voidSettingsTypes.js'; import { IDirectoryStrService } from './directoryStrService.js'; import { ITerminalToolService } from './terminalToolService.js'; +import { IVoidModelService } from '../common/voidModelService.js'; +import { URI } from '../../../../base/common/uri.js'; @@ -412,9 +414,9 @@ const prepareMessages = ({ export interface IConvertToLLMMessageService { readonly _serviceBrand: undefined; - prepareLLMSimpleMessages: (opts: { simpleMessages: SimpleLLMMessage[], systemMessage: string, modelSelection: ModelSelection | null, featureName: FeatureName }) => { messages: LLMChatMessage[], separateSystemMessage: string | undefined }; + prepareLLMSimpleMessages: (opts: { simpleMessages: SimpleLLMMessage[], systemMessage: string, modelSelection: ModelSelection | null, featureName: FeatureName }) => { messages: LLMChatMessage[], separateSystemMessage: string | undefined } prepareLLMChatMessages: (opts: { chatMessages: ChatMessage[], chatMode: ChatMode, modelSelection: ModelSelection | null }) => Promise<{ messages: LLMChatMessage[], separateSystemMessage: string | undefined }> - prepareFIMMessage(opts: { messages: LLMFIMMessage, aiInstructions: string, }): { prefix: string, suffix: string, stopTokens: string[] } + prepareFIMMessage(opts: { messages: LLMFIMMessage, }): { prefix: string, suffix: string, stopTokens: string[] } } @@ -431,10 +433,35 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess @IDirectoryStrService private readonly directoryStrService: IDirectoryStrService, @ITerminalToolService private readonly terminalToolService: ITerminalToolService, @IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService, + @IVoidModelService private readonly voidModelService: IVoidModelService, ) { super() } + // Read .voidinstructions files from workspace folders + private _getVoidInstructionsFileContents(): string { + const workspaceFolders = this.workspaceContextService.getWorkspace().folders; + let voidInstructions = ''; + for (const folder of workspaceFolders) { + const uri = URI.joinPath(folder.uri, '.voidinstructions') + const { model } = this.voidModelService.getModel(uri) + if (!model) continue + voidInstructions += model.getValue() + '\n\n'; + } + return voidInstructions.trim(); + } + + // Get combined AI instructions from settings and .voidinstructions files + private _getCombinedAIInstructions(): string { + const globalAIInstructions = this.voidSettingsService.state.globalSettings.aiInstructions; + const voidInstructionsFileContent = this._getVoidInstructionsFileContents(); + + const ans: string[] = [] + if (globalAIInstructions) ans.push(globalAIInstructions) + if (voidInstructionsFileContent) ans.push(voidInstructionsFileContent) + return ans.join('\n\n') + } + // system message private _generateChatMessagesSystemMessage = async (chatMode: ChatMode, specialToolFormat: 'openai-style' | 'anthropic-style' | undefined) => { @@ -502,7 +529,9 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess } = getModelCapabilities(providerName, modelName) const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] - const aiInstructions = this.voidSettingsService.state.globalSettings.aiInstructions + + // Get combined AI instructions + const aiInstructions = this._getCombinedAIInstructions(); const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions) const maxOutputTokens = getMaxOutputTokens(providerName, modelName, { isReasoningEnabled }) @@ -518,7 +547,6 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess maxOutputTokens, }) return { messages, separateSystemMessage }; - } prepareLLMChatMessages: IConvertToLLMMessageService['prepareLLMChatMessages'] = async ({ chatMessages, chatMode, modelSelection }) => { if (modelSelection === null) return { messages: [], separateSystemMessage: undefined } @@ -531,7 +559,9 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess const systemMessage = await this._generateChatMessagesSystemMessage(chatMode, specialToolFormat) const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection['Chat'][modelSelection.providerName]?.[modelSelection.modelName] - const aiInstructions = this.voidSettingsService.state.globalSettings.aiInstructions + + // Get combined AI instructions + const aiInstructions = this._getCombinedAIInstructions(); const isReasoningEnabled = getIsReasoningEnabledState('Chat', providerName, modelName, modelSelectionOptions) const maxOutputTokens = getMaxOutputTokens(providerName, modelName, { isReasoningEnabled }) @@ -548,19 +578,20 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess maxOutputTokens, }) return { messages, separateSystemMessage }; - } // --- FIM --- - prepareFIMMessage: IConvertToLLMMessageService['prepareFIMMessage'] = ({ messages, aiInstructions }) => { + prepareFIMMessage: IConvertToLLMMessageService['prepareFIMMessage'] = ({ messages }) => { + // Get combined AI instructions with the provided aiInstructions as the base + const combinedInstructions = this._getCombinedAIInstructions(); let prefix = `\ -${!aiInstructions ? '' : `\ +${!combinedInstructions ? '' : `\ // Instructions: // Do not output an explanation. Try to avoid outputting comments. Only output the middle code. -${aiInstructions.split('\n').map(line => `//${line}`).join('\n')}`} +${combinedInstructions.split('\n').map(line => `//${line}`).join('\n')}`} ${messages.prefix}` @@ -573,7 +604,6 @@ ${messages.prefix}` } -// pick one and delete the other: registerSingleton(IConvertToLLMMessageService, ConvertToLLMMessageService, InstantiationType.Eager); diff --git a/src/vs/workbench/contrib/void/browser/convertToLLMMessageWorkbenchContrib.ts b/src/vs/workbench/contrib/void/browser/convertToLLMMessageWorkbenchContrib.ts new file mode 100644 index 00000000..9e65f9da --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/convertToLLMMessageWorkbenchContrib.ts @@ -0,0 +1,37 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; +import { IVoidModelService } from '../common/voidModelService.js'; + +class ConvertContribWorkbenchContribution extends Disposable implements IWorkbenchContribution { + static readonly ID = 'workbench.contrib.void.convertcontrib' + _serviceBrand: undefined; + + constructor( + @IVoidModelService private readonly voidModelService: IVoidModelService, + @IWorkspaceContextService private readonly workspaceContext: IWorkspaceContextService, + ) { + super() + + const initializeURI = (uri: URI) => { + this.workspaceContext.getWorkspace() + const voidInstrsURI = URI.joinPath(uri, '.voidinstructions') + this.voidModelService.initializeModel(voidInstrsURI) + } + + // call + this._register(this.workspaceContext.onDidChangeWorkspaceFolders((e) => { + [...e.changed, ...e.added].forEach(w => { initializeURI(w.uri) }) + })) + this.workspaceContext.getWorkspace().folders.forEach(w => { initializeURI(w.uri) }) + } +} + + +registerWorkbenchContribution2(ConvertContribWorkbenchContribution.ID, ConvertContribWorkbenchContribution, WorkbenchPhase.BlockRestore); diff --git a/src/vs/workbench/contrib/void/browser/media/void.css b/src/vs/workbench/contrib/void/browser/media/void.css index ac5ff5f3..d732c4bf 100644 --- a/src/vs/workbench/contrib/void/browser/media/void.css +++ b/src/vs/workbench/contrib/void/browser/media/void.css @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ - .monaco-editor .void-sweepIdxBG { +.monaco-editor .void-sweepIdxBG { background-color: var(--vscode-void-sweepIdxBG); } @@ -23,10 +23,30 @@ background-color: var(--vscode-void-redBG); } -.void-watermark-button { - margin: 8px 0; + +/* Renamed from void-watermark-button to void-openfolder-button */ +.void-openfolder-button { padding: 8px 20px; - background-color: #3b82f6; + background-color: #306dce; + color: white; + border: none; + border-radius: 4px; + outline: none !important; + box-shadow: none !important; + cursor: pointer; + transition: background-color 0.2s ease; +} +.void-openfolder-button:hover { + background-color: #2563eb; +} +.void-openfolder-button:active { + background-color: #2563eb; +} + +/* Added for Open SSH button with slightly darker color */ +.void-openssh-button { + padding: 8px 20px; + background-color: #656565; /* Slightly darker than the #5a5a5a in the TS file */ color: white; border: none; border-radius: 4px; @@ -35,43 +55,41 @@ cursor: pointer; transition: background-color 0.2s ease; } -.void-watermark-button:hover { - background-color: #2563eb; +.void-openssh-button:hover { + background-color: #474747; /* Darker on hover */ } -.void-watermark-button:active { - background-color: #2563eb; +.void-openssh-button:active { + background-color: #474747; } - - .void-settings-watermark-button { - margin: 8px 0; - padding: 8px 20px; - background-color: var(--vscode-input-background); + margin: 8px 0; + padding: 8px 20px; + background-color: var(--vscode-input-background); color: var(--vscode-input-foreground); - border: none; - border-radius: 4px; - outline: none !important; + border: none; + border-radius: 4px; + outline: none !important; box-shadow: none !important; - cursor: pointer; - transition: all 0.2s ease; + cursor: pointer; + transition: all 0.2s ease; } + .void-settings-watermark-button:hover { filter: brightness(1.1); } + .void-settings-watermark-button:active { filter: brightness(1.1); } - - - .void-link { color: #3b82f6; cursor: pointer; transition: all 0.2s ease; } + .void-link:hover { opacity: 80%; } @@ -86,7 +104,8 @@ .void-scope, .void-scope * { scrollbar-width: thin !important; - scrollbar-color: var(--void-bg-1) var(--void-bg-3) !important; /* For Firefox */ + scrollbar-color: var(--void-bg-1) var(--void-bg-3) !important; + /* For Firefox */ } .void-scope::-webkit-scrollbar, @@ -133,13 +152,16 @@ background-color: var(--vscode-editor-background); --scrollbar-vertical-width: 14px; --scrollbar-horizontal-height: 6px; - overflow: auto; /* Ensure scrollbars are shown when needed */ + 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 */ + scrollbar-width: thin !important; + /* For Firefox */ + scrollbar-color: var(--void-bg-1) var(--void-bg-3) !important; + /* For Firefox */ } .void-scrollable-element::-webkit-scrollbar, diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx index 2ccc54b2..3720d1a6 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx @@ -5,6 +5,7 @@ import React, { Component, ErrorInfo, ReactNode } from 'react'; import { ErrorDisplay } from './ErrorDisplay.js'; +import { WarningBox } from '../void-settings-tsx/WarningBox.js'; interface Props { children: ReactNode; @@ -51,11 +52,12 @@ class ErrorBoundary extends Component { // Use ErrorDisplay component as the default error UI 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 97ffa768..0cefd52d 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 @@ -29,6 +29,7 @@ import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg import { ToolName, toolNames } from '../../../../common/prompt/prompts.js'; import { RawToolCallObj } from '../../../../common/sendLLMMessageTypes.js'; import { MAX_FILE_CHARS_PAGE } from '../../../toolsService.js'; +import ErrorBoundary from './ErrorBoundary.js'; @@ -2483,7 +2484,7 @@ export const SidebarChat = () => { const currCheckpointIdx = chatThreadsState.allThreads[threadId]?.state?.currCheckpointIdx ?? undefined // if not exist, treat like checkpoint is last message (infinity) const previousMessagesHTML = useMemo(() => { - const lastMessageIdx = previousMessages.findLastIndex(v => v.role !== 'checkpoint') + // const lastMessageIdx = previousMessages.findLastIndex(v => v.role !== 'checkpoint') // tool request shows up as Editing... if in progress return previousMessages.map((message, i) => { return {
{/* History selector */}
- + + +
- {messagesHTML} + + {messagesHTML} +
- {inputForm} + + {inputForm} +
) diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx index 47b3ce1c..40ee767c 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx @@ -3,13 +3,14 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useAccessor, useIsDark, useSettingsState } from '../util/services.js'; -import { Brain, Check, DollarSign, ExternalLink, Lock, X } from 'lucide-react'; +import { Brain, Check, ChevronRight, DollarSign, ExternalLink, Lock, X } from 'lucide-react'; import { displayInfoOfProviderName, ProviderName, providerNames, refreshableProviderNames } from '../../../../common/voidSettingsTypes.js'; import { getModelCapabilities, ollamaRecommendedModels } from '../../../../common/modelCapabilities.js'; import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js'; import { AddModelInputBox, AnimatedCheckmarkButton, ollamaSetupInstructions, OneClickSwitchButton, SettingsForProvider } from '../void-settings-tsx/Settings.js'; +import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js'; const OVERRIDE_VALUE = false @@ -34,6 +35,30 @@ export const VoidOnboarding = () => { ) } +const VoidIcon = () => { + const accessor = useAccessor() + const themeService = accessor.get('IThemeService') + + const divRef = useRef(null) + + useEffect(() => { + // void icon style + const updateTheme = () => { + const theme = themeService.getColorTheme().type + const isDark = theme === ColorScheme.DARK || theme === ColorScheme.HIGH_CONTRAST_DARK + if (divRef.current) { + divRef.current.style.maxWidth = '220px' + divRef.current.style.opacity = '50%' + divRef.current.style.filter = isDark ? '' : 'invert(1)' //brightness(.5) + } + } + updateTheme() + const d = themeService.onDidColorThemeChange(updateTheme) + return () => d.dispose() + }, []) + + return
+} const FADE_DURATION_MS = 2000 @@ -109,7 +134,7 @@ const NextButton = ({ onClick, ...props }: { onClick: () => void } & React.Butto className="px-6 py-2 bg-zinc-100 enabled:hover:bg-zinc-100 disabled:bg-zinc-100/40 disabled:cursor-not-allowed rounded text-black duration-600 transition-all" {...props.disabled && { 'data-tooltip-id': 'void-tooltip', - 'data-tooltip-content': 'Disabled (Please enter all required fields or choose another provider)', + 'data-tooltip-content': 'Please enter all required fields or choose another provider', 'data-tooltip-place': 'top', }} {...props} @@ -144,7 +169,7 @@ const OnboardingPageShell = ({ top, bottom, content, hasMaxWidth = true, classNa
{top && {top}} {content && {content}} - {bottom &&
{bottom}
} + {bottom &&
{bottom}
}
) } @@ -157,7 +182,7 @@ const OllamaDownloadOrRemoveModelButton = ({ modelName, isModelInstalled, sizeGb href={`https://ollama.com/library/${modelName}`} target="_blank" rel="noopener noreferrer" - className="flex items-center text-void-fg-2 hover:text-void-fg-1" + className="flex items-center justify-center text-void-fg-2 hover:text-void-fg-1" > @@ -353,7 +378,7 @@ const TableOfModelsForProvider = ({ providerName }: { providerName: ProviderName {/* */} - {isDetectableLocally && {!!isDownloaded ? : <>}} + {isDetectableLocally && {!!isDownloaded ? : <>}} {providerName === 'ollama' && } @@ -376,6 +401,57 @@ const TableOfModelsForProvider = ({ providerName }: { providerName: ProviderName +const PrimaryActionButton = ({ children, className, ringSize, ...props }: { children: React.ReactNode, ringSize?: undefined | 'xl' | 'screen' } & React.ButtonHTMLAttributes) => { + + + return ( + + ) +} + + type WantToUseOption = 'smart' | 'private' | 'cheap' | 'all' const VoidOnboardingContent = () => { @@ -447,6 +523,19 @@ const VoidOnboardingContent = () => {
+ const lastPagePrevAndNextButtons =
+
+ { setPageIndex(pageIndex - 1) }} + /> + { voidSettingsService.setGlobalSetting('isOnboardingComplete', true); }} + ringSize={voidSettingsState.globalSettings.isOnboardingComplete ? 'screen' : undefined} + >Enter the Void +
+
+ + // cannot be md const basicDescOfWantToUseOption: { [wantToUseOption in WantToUseOption]: string } = { smart: "Models with the best performance on benchmarks.", @@ -459,7 +548,7 @@ const VoidOnboardingContent = () => { 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 with Ollama or vLLM for free.", + cheap: "Use great deals like Gemini 2.5 Pro, or self-host a model with Ollama or vLLM for free.", all: "", } @@ -490,17 +579,26 @@ const VoidOnboardingContent = () => { const contentOfIdx: { [pageIndex: number]: React.ReactNode } = { 0: +
Welcome to Void
+ + {/* Slice of Void image */} +
+ +
+ + { setPageIndex(pageIndex + 1) }} + delayMs={1000} > - Get Started + { setPageIndex(pageIndex + 1) }} + > + Get Started + - +
} />, 1: {
- + +

Local Providers

+

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

+ +
+ {ollamaSetupInstructions} +
+ + + + + +

Providers

+

{`Void can access models from Anthropic, OpenAI, OpenRouter, and more.`}

+ + + + + + +

Feature Options

+ {/* L1 */} + +
+ + {/* FIM */} +
+

{displayInfoOfFeatureName('Autocomplete')}

+
+ + Experimental.{' '} + + + Only works with FIM models.* + +
+ +
+ {/* Enable Switch */} + +
+ voidSettingsService.setGlobalSetting('enableAutocomplete', newVal)} + /> + {settingsState.globalSettings.enableAutocomplete ? 'Enabled' : 'Disabled'} +
+
+ + {/* Model Dropdown */} + +
+ +
+
+ +
+ +
+
+ + {/* Apply */} + + +
+

{displayInfoOfFeatureName('Apply')}

+
Settings that control the behavior of the Apply button and the Edit tool.
+ +
+ {/* Sync to Chat Switch */} +
+ voidSettingsService.setGlobalSetting('syncApplyToChat', newVal)} + /> + {settingsState.globalSettings.syncApplyToChat ? 'Same as Chat model' : 'Different model'} +
+ + {/* Model Dropdown */} +
+ +
+
+ + +
+ {/* Fast Apply Method Dropdown */} +
+ +
+
+ +
+
+ +
+ + + + {/* L2 */} + +
+ + {/* Tools Section */} +
+

Tools

+
{`Tools are functions that LLMs can call. Some tools require user approval.`}
+ +
+ {/* Auto Accept Switch */} + + +
+ voidSettingsService.setGlobalSetting('autoApprove', newVal)} + /> + {settingsState.globalSettings.autoApprove ? 'Auto-approve' : 'Auto-approve'} +
+
+ + {/* Tool Lint Errors Switch */} + + +
+ voidSettingsService.setGlobalSetting('includeToolLintErrors', newVal)} + /> + {settingsState.globalSettings.includeToolLintErrors ? 'Fix lint errors' : `Fix lint errors`} +
+
+
- {/* separator */} -
- {/* content */} -
+
+

Editor

+
{`Settings that control the visibility of suggestions and widgets in the code editor.`}
-
- +
+ {/* Auto Accept Switch */} + +
+ voidSettingsService.setGlobalSetting('showInlineSuggestions', newVal)} + /> + {settingsState.globalSettings.showInlineSuggestions ? 'Show suggestions on select' : 'Show suggestions on select'} +
+
- -
- -
-
+ + {/* General section (formerly GeneralTab) */} +
+ +

One-Click Switch

+

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

+ +
+ + + +
+
+
+ + + +
+ +

Built-in Settings

+

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

+ + +
+ { commandService.executeCommand('workbench.action.openSettings') }}> + General Settings + +
+
+ { commandService.executeCommand('workbench.action.openGlobalKeybindings') }}> + Keyboard Settings + +
+
+ { commandService.executeCommand('workbench.action.selectTheme') }}> + Theme Settings + +
+
+ { nativeHostService.showItemInFolder(environmentService.logsHome.fsPath) }}> + Open Logs + +
+
+
+ + +
+

AI Instructions

+

+ +

+ + + +
-
} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-tooltip/VoidTooltip.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-tooltip/VoidTooltip.tsx index 0bd7d9e0..9c2eec45 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-tooltip/VoidTooltip.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-tooltip/VoidTooltip.tsx @@ -102,8 +102,7 @@ export const VoidTooltip = () => { id="void-tooltip-ollama-settings" border='1px solid rgba(100,100,100,.2)' opacity={1} - openOnClick - openEvents={{ mouseover: true }} + openEvents={{ mouseover: true, click: true, focus: true }} place='right' style={{ pointerEvents: 'all', userSelect: 'text', fontSize: 11 }} > diff --git a/src/vs/workbench/contrib/void/browser/void.contribution.ts b/src/vs/workbench/contrib/void/browser/void.contribution.ts index 30cdfc8b..cd7b6f60 100644 --- a/src/vs/workbench/contrib/void/browser/void.contribution.ts +++ b/src/vs/workbench/contrib/void/browser/void.contribution.ts @@ -32,6 +32,7 @@ import './media/void.css' // update (frontend part, also see platform/) import './voidUpdateActions.js' +import './convertToLLMMessageWorkbenchContrib.js' // tools import './toolsService.js' diff --git a/src/vs/workbench/contrib/void/browser/voidCommandBarService.ts b/src/vs/workbench/contrib/void/browser/voidCommandBarService.ts index 02eadbde..6cfcb1d0 100644 --- a/src/vs/workbench/contrib/void/browser/voidCommandBarService.ts +++ b/src/vs/workbench/contrib/void/browser/voidCommandBarService.ts @@ -362,8 +362,6 @@ export class VoidCommandBarService extends Disposable implements IVoidCommandBar registerSingleton(IVoidCommandBarService, VoidCommandBarService, InstantiationType.Delayed); // delayed is needed here :( -// registerWorkbenchContribution2(VoidCommandBarService.ID, VoidCommandBarService, WorkbenchPhase.BlockRestore); - export type VoidCommandBarProps = { uri: URI | null; diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index 5d5b4169..4cbf5444 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -783,7 +783,7 @@ const groqSettings: VoidStaticProviderInfo = { } const ollamaModelOptions = { - 'qwen2.5-coder:3b': { + 'qwen2.5-coder:1.5b': { contextWindow: 32_000, maxOutputTokens: null, cost: { input: 0, output: 0 }, @@ -792,6 +792,15 @@ const ollamaModelOptions = { supportsSystemMessage: 'system-role', reasoningCapabilities: false, }, + 'llama3.1': { + contextWindow: 128_000, + maxOutputTokens: null, + cost: { input: 0, output: 0 }, + downloadable: { sizeGb: 4.9 }, + supportsFIM: false, + supportsSystemMessage: 'system-role', + reasoningCapabilities: false, + }, 'qwen2.5-coder': { contextWindow: 128_000, maxOutputTokens: null, @@ -822,7 +831,7 @@ const ollamaModelOptions = { } as const satisfies Record -export const ollamaRecommendedModels = ['qwen2.5-coder:3b', 'qwq', 'deepseek-r1'] as const satisfies (keyof typeof ollamaModelOptions)[] +export const ollamaRecommendedModels = ['qwen2.5-coder:1.5b', 'llama3.1', 'qwq', 'deepseek-r1'] as const satisfies (keyof typeof ollamaModelOptions)[] diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts index 09be5a02..819a01b7 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts @@ -245,6 +245,17 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { } // the stored data structure might be outdated, so we need to update it here + readS = { + ...readS, + settingsOfProvider: { + ...defaultSettingsOfProvider, + ...readS.settingsOfProvider, + mistral: { // we added mistral + ...defaultSettingsOfProvider.mistral, + ...readS.settingsOfProvider.mistral, + }, + } // we added mistral + } this.state = readS this.state = _stateWithUpdatedDefaultModels(this.state) this.state = _validatedModelState(this.state);