diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 48638aa12..01087d596 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -15,6 +15,7 @@ import { IRequestService } from '../../request/common/request.js'; import { AvailableForDownload, DisablementReason, IUpdateService, State, StateType, UpdateType } from '../common/update.js'; export function createUpdateURL(platform: string, quality: string, productService: IProductService): string { + // return `https://voideditor.dev/api/update/${platform}/stable`; return `${productService.updateUrl}/api/update/${platform}/${quality}/${productService.commit}`; } @@ -70,9 +71,11 @@ export abstract class AbstractUpdateService implements IUpdateService { */ protected async initialize(): Promise { if (!this.environmentMainService.isBuilt) { + console.log('is NOT built, canceling update service') this.setState(State.Disabled(DisablementReason.NotBuilt)); return; // updates are never enabled when running out of sources } + console.log('is built, continuing with update service') if (this.environmentMainService.disableUpdates) { this.setState(State.Disabled(DisablementReason.DisabledByEnvironment)); @@ -86,16 +89,19 @@ export abstract class AbstractUpdateService implements IUpdateService { return; } + // Void - for now, always update + const updateMode = this.configurationService.getValue<'none' | 'manual' | 'start' | 'default'>('update.mode'); - const quality = this.getProductQuality(updateMode); + const quality = this.getProductQuality(updateMode); if (!quality) { this.setState(State.Disabled(DisablementReason.ManuallyDisabled)); this.logService.info('update#ctor - updates are disabled by user preference'); return; } - this.url = this.buildUpdateFeedUrl(quality); + // const quality = 'stable' + this.url = this.doBuildUpdateFeedUrl(quality); if (!this.url) { this.setState(State.Disabled(DisablementReason.InvalidConfiguration)); this.logService.info('update#ctor - updates are disabled as the update URL is badly formed'); @@ -131,13 +137,10 @@ export abstract class AbstractUpdateService implements IUpdateService { return updateMode === 'none' ? undefined : this.productService.quality; } - private scheduleCheckForUpdates(delay = 60 * 60 * 1000): Promise { - return timeout(delay) - .then(() => this.checkForUpdates(false)) - .then(() => { - // Check again after 1 hour - return this.scheduleCheckForUpdates(60 * 60 * 1000); - }); + private async scheduleCheckForUpdates(delay = 60 * 60 * 1000): Promise { + await timeout(delay); + await this.checkForUpdates(false); + return await this.scheduleCheckForUpdates(60 * 60 * 1000); } async checkForUpdates(explicit: boolean): Promise { @@ -160,6 +163,7 @@ export abstract class AbstractUpdateService implements IUpdateService { await this.doDownloadUpdate(this.state); } + // override implemented by windows and linux protected async doDownloadUpdate(state: AvailableForDownload): Promise { // noop } @@ -174,6 +178,7 @@ export abstract class AbstractUpdateService implements IUpdateService { await this.doApplyUpdate(); } + // windows overrides this protected async doApplyUpdate(): Promise { // noop } @@ -236,6 +241,6 @@ export abstract class AbstractUpdateService implements IUpdateService { // noop } - protected abstract buildUpdateFeedUrl(quality: string): string | undefined; + protected abstract doBuildUpdateFeedUrl(quality: string): string | undefined; protected abstract doCheckForUpdates(context: any): void; } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index d3f27d371..c521b76f0 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -73,7 +73,7 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau this.setState(State.Idle(UpdateType.Archive, message)); } - protected buildUpdateFeedUrl(quality: string): string | undefined { + protected doBuildUpdateFeedUrl(quality: string): string | undefined { let assetID: string; if (!this.productService.darwinUniversalAssetId) { assetID = process.arch === 'x64' ? 'darwin' : 'darwin-arm64'; diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 6e076c72e..b01840c5a 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -30,7 +30,7 @@ export class LinuxUpdateService extends AbstractUpdateService { super(lifecycleMainService, configurationService, environmentMainService, requestService, logService, productService); } - protected buildUpdateFeedUrl(quality: string): string { + protected doBuildUpdateFeedUrl(quality: string): string { return createUpdateURL(`linux-${process.arch}`, quality, this.productService); } diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 61109e547..c987ecceb 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -99,7 +99,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun await super.initialize(); } - protected buildUpdateFeedUrl(quality: string): string | undefined { + protected doBuildUpdateFeedUrl(quality: string): string | undefined { let platform = `win32-${process.arch}`; if (getUpdateType() === UpdateType.Archive) { diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index 64c74477b..3c9b9da05 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -177,6 +177,7 @@ export type SettingName = keyof SettingsForProvider type DisplayInfoForProviderName = { title: string, + desc?: string, } export const displayInfoOfProviderName = (providerName: ProviderName): DisplayInfoForProviderName => { @@ -203,7 +204,7 @@ export const displayInfoOfProviderName = (providerName: ProviderName): DisplayIn } else if (providerName === 'openAICompatible') { return { - title: 'Other', + title: 'OpenAI-Compatible', } } else if (providerName === 'gemini') { @@ -256,7 +257,7 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName : providerName === 'openAICompatible' ? 'https://my-website.com/v1' : '(never)', - subTextMd: providerName === 'ollama' ? 'Read about advanced [Endpoints here](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-expose-ollama-on-my-network).' : + subTextMd: providerName === 'ollama' ? 'If you would like to change this endpoint, please read more about [Endpoints here](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-expose-ollama-on-my-network).' : undefined, } } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 975f494cb..216cff20c 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -289,6 +289,7 @@ export interface IFileTemplateData { readonly templateDisposables: DisposableStore; readonly elementDisposables: DisposableStore; readonly label: IResourceLabel; + readonly voidLabels: IResourceLabel; readonly container: HTMLElement; readonly contribs: IExplorerFileContribution[]; currentContext?: ExplorerItem; @@ -347,15 +348,24 @@ export class FilesRenderer implements ICompressibleTreeRenderer { + // console.log('ON CLICK', templateData.currentContext?.children) + // }) + const voidLabels = this.labels.create(voidButtonsContainer, { supportHighlights: false, supportIcons: false, }); + voidLabels.element.textContent = 'hi333' + const label = templateDisposables.add(this.labels.create(container, { supportHighlights: true })); templateDisposables.add(label.onDidRender(() => { - try { - if (templateData.currentContext) { - this.updateWidth(templateData.currentContext); - } - } catch (e) { - // noop since the element might no longer be in the tree, no update of width necessary - } + try { if (templateData.currentContext) this.updateWidth(templateData.currentContext); } + catch (e) { /* noop since the element might no longer be in the tree, no update of width necessary*/ } })); const contribs = explorerFileContribRegistry.create(this.instantiationService, container, templateDisposables); @@ -365,10 +375,12 @@ export class FilesRenderer implements ICompressibleTreeRenderer, index: number, templateData: IFileTemplateData): void { const stat = node.element; templateData.currentContext = stat; @@ -382,8 +394,7 @@ export class FilesRenderer implements ICompressibleTreeRenderer c.setResource(undefined)); @@ -477,6 +488,13 @@ export class FilesRenderer implements ICompressibleTreeRenderer { label: { container: label, onDidRender: emitter.event - } + }, + voidLabels: { + container: label, + onDidRender: emitter.event + }, + }, 1, false); ds.add(navigationController); diff --git a/src/vs/workbench/contrib/void/browser/autocompleteService.ts b/src/vs/workbench/contrib/void/browser/autocompleteService.ts index 3bd4d955f..bbefb748a 100644 --- a/src/vs/workbench/contrib/void/browser/autocompleteService.ts +++ b/src/vs/workbench/contrib/void/browser/autocompleteService.ts @@ -17,7 +17,7 @@ import { IEditorService } from '../../../services/editor/common/editorService.js import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { EditorResourceAccessor } from '../../../common/editor.js'; import { IModelService } from '../../../../editor/common/services/model.js'; -import { extractCodeFromResult } from './helpers/extractCodeFromResult.js'; +import { extractCodeFromRegular } from './helpers/extractCodeFromResult.js'; // The extension this was called from is here - https://github.com/voideditor/void/blob/autocomplete/extensions/void/src/extension/extension.ts @@ -652,7 +652,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ // newAutocompletion.abortRef = { current: () => { } } newAutocompletion.status = 'finished' // newAutocompletion.promise = undefined - newAutocompletion.insertText = postprocessResult(extractCodeFromResult(fullText)) + newAutocompletion.insertText = postprocessResult(extractCodeFromRegular(fullText)) resolve(newAutocompletion.insertText) diff --git a/src/vs/workbench/contrib/void/browser/helpers/extractCodeFromResult.ts b/src/vs/workbench/contrib/void/browser/helpers/extractCodeFromResult.ts index 048ebbfcf..6ac12fd1f 100644 --- a/src/vs/workbench/contrib/void/browser/helpers/extractCodeFromResult.ts +++ b/src/vs/workbench/contrib/void/browser/helpers/extractCodeFromResult.ts @@ -3,10 +3,66 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -export const extractCodeFromResult = (result: string) => { + +// modelWasTrainedOnFIM should be false here +export const extractCodeFromFIM = ({ text, midTag, modelWasTrainedOnFIM }: { text: string, midTag: string, modelWasTrainedOnFIM: false }) => { + + /* desired matches +` +`` +``` +< +

+

 a
+
 a 
+
 a 
< +
 a 
a
a a + +
 a 
 ->
+	*/
+
+
+	/* ------------- summary of the regex -------------
+		[optional ` | `` | ```]
+		(match optional_language_name)
+		[optional strings here]
+		[required  tag]
+		(match the stuff between mid tags)
+		[optional  tag]
+		[optional ` | `` | ```]
+	*/
+
+	// const regex = /[\s\S]*?(?:`{1,3}\s*([a-zA-Z_]+[\w]*)?[\s\S]*?)?([\s\S]*?)(?:<\/MID>|`{1,3}|$)/;
+	const regex = new RegExp(
+		`[\\s\\S]*?(?:\`{1,3}\\s*([a-zA-Z_]+[\\w]*)?[\\s\\S]*?)?<${midTag}>([\\s\\S]*?)(?:|\`{1,3}|$)`,
+		''
+	);
+	const match = text.match(regex);
+	if (match) {
+		const [_, languageName, codeBetweenMidTags] = match;
+		return [languageName, codeBetweenMidTags] as const
+
+	} else {
+		return [undefined, extractCodeFromRegular(text)] as const
+	}
+
+}
+
+
+
+export const extractCodeFromRegular = (result: string) => {
 	// Match either:
 	// 1. ```language\n```
 	// 2. ``````
+
+	// 4 
 A
+	// 3. 
 A 
B -> B const match = result.match(/```(?:\w+\n)?([\s\S]*?)```|```([\s\S]*?)```/); if (!match) { diff --git a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts index 5ccb08ecb..2f85ffcd7 100644 --- a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts +++ b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts @@ -27,15 +27,16 @@ import * as dom from '../../../../base/browser/dom.js'; import { Widget } from '../../../../base/browser/ui/widget.js'; import { URI } from '../../../../base/common/uri.js'; import { IConsistentEditorItemService, IConsistentItemService } from './helperServices/consistentItemService.js'; -import { ctrlKStream_prefixAndSuffix, ctrlKStream_prompt, ctrlKStream_systemMessage, ctrlLStream_prompt, ctrlLStream_systemMessage } from './prompt/prompts.js'; +import { ctrlKStream_prefixAndSuffix, ctrlKStream_prompt, ctrlKStream_systemMessage, ctrlLStream_prompt, ctrlLStream_systemMessage, defaultFimTags } from './prompt/prompts.js'; import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js'; import { IPosition } from '../../../../editor/common/core/position.js'; -import { mountCtrlK } from '../browser/react/out/ctrl-k-tsx/index.js' +import { mountCtrlK } from '../browser/react/out/quick-edit-tsx/index.js' import { QuickEditPropsType } from './quickEditActions.js'; import { InputBox } from '../../../../base/browser/ui/inputbox/inputBox.js'; import { LLMMessage } from '../../../../platform/void/common/llmMessageTypes.js'; import { IModelContentChangedEvent } from '../../../../editor/common/textModelEvents.js'; +import { extractCodeFromFIM, extractCodeFromRegular } from './helpers/extractCodeFromResult.js'; const configOfBG = (color: Color) => { return { dark: color, light: color, hcDark: color, hcLight: color, } @@ -822,7 +823,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { const lastDiff = computedDiffs.pop() if (!lastDiff) { - console.log('!lastDiff') + // console.log('!lastDiff') // if the writing is identical so far, display no changes originalCodeStartLine = 1 newCodeEndLine = 1 @@ -842,7 +843,8 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { const newCodeTop = llmText.split('\n').slice(0, (newCodeEndLine - 1) + 1).join('\n') const oldFileBottom = diffZone.originalCode.split('\n').slice((originalCodeStartLine - 1) + 1, Infinity).join('\n') - const newCode = `${newCodeTop}\n${oldFileBottom}` + // oriignalCode[1 + line...Infinity]. Must make sure 1 + line < originalCode.length. This is another way to check: + const newCode = (newCodeTop && oldFileBottom) ? `${newCodeTop}\n${oldFileBottom}` : (oldFileBottom || newCodeTop) this._writeText(uri, newCode, { startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, // 1-indexed @@ -1044,6 +1046,10 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { // this._deleteDiffArea(ctrlKZone) // } + // TODO ctrl+K case should be replaced with an actual check for model.isFIM + const modelWasTrainedOnFIM = featureName === 'Ctrl+K' ? false : false + const modelFimTags = defaultFimTags + const adding: Omit = { type: 'DiffZone', originalCode, @@ -1072,7 +1078,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { } else if (featureName === 'Ctrl+K') { const { prefix, suffix } = ctrlKStream_prefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine }) - const userContent = ctrlKStream_prompt({ selection: originalCode, userMessage, prefix, suffix }) + const userContent = ctrlKStream_prompt({ selection: originalCode, userMessage, prefix, suffix, modelWasTrainedOnFIM, fimTags: modelFimTags }) console.log('PREFIX:\n', prefix) console.log('SUFFIX:\n', suffix) console.log('USER CONTENT:\n', userContent) @@ -1103,17 +1109,30 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { // refresh now in case onText takes a while to get 1st message this._refreshStylesAndDiffsInURI(uri) + + const extractText = (fullText: string) => { + if (featureName === 'Ctrl+K') { + const [_, textSoFar] = extractCodeFromFIM({ text: fullText, midTag: modelFimTags.midTag, modelWasTrainedOnFIM }) + return textSoFar + } + else if (featureName === 'Ctrl+L') { + return extractCodeFromRegular(fullText) + } + throw 1 + } + streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({ featureName, logging: { loggingName: `startApplying - ${featureName}` }, messages, onText: ({ newText, fullText }) => { - this._writeDiffZoneLLMText(diffZone, fullText, latestCurrentFileEnd, latestOriginalFileStart) + this._writeDiffZoneLLMText(diffZone, extractText(fullText), latestCurrentFileEnd, latestOriginalFileStart) this._refreshStylesAndDiffsInURI(uri) }, onFinalMessage: ({ fullText }) => { + // console.log('DONE! FULL TEXT\n', extractText(fullText), diffZone.startLine, diffZone.endLine) // at the end, re-write whole thing to make sure no sync errors - this._writeText(uri, fullText, + this._writeText(uri, extractText(fullText), { startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER }, // 1-indexed { shouldRealignDiffAreas: false } ) @@ -1279,8 +1298,18 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { // B| <-- endLine (we want to delete this whole line) // C else if (diff.type === 'insertion') { - writeText = '' - toRange = { startLineNumber: diff.startLine, startColumn: 1, endLineNumber: diff.endLine + 1, endColumn: 1 } // 1-indexed + // console.log('REJECTING:', diff) + // handle the case where the insertion was a newline at end of diffarea (applying to the next line doesnt work because it doesnt exist, vscode just doesnt delete the correct # of newlines) + if (diff.endLine === diffArea.endLine) { + // delete the line before instead of after + writeText = '' + toRange = { startLineNumber: diff.startLine - 1, startColumn: Number.MAX_SAFE_INTEGER, endLineNumber: diff.endLine, endColumn: 1 } // 1-indexed + } + else { + writeText = '' + toRange = { startLineNumber: diff.startLine, startColumn: 1, endLineNumber: diff.endLine + 1, endColumn: 1 } // 1-indexed + } + } // if it was an edit, just edit the range // (this image applies to writeText and toRange, not newOriginalCode) diff --git a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts index 8570fb1b0..f27597446 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts @@ -324,13 +324,25 @@ export const ctrlKStream_prefixAndSuffix = ({ fullFileStr, startLine, endLine }: } -export const ctrlKStream_prompt = ({ selection, prefix, suffix, userMessage }: { selection: string, prefix: string, suffix: string, userMessage: string, }) => { - const onlySpeaksFIM = false - if (onlySpeaksFIM) { - const preTag = 'PRE' - const sufTag = 'SUF' - const midTag = 'MID' +export type FimTagsType = { + preTag: string, + sufTag: string, + midTag: string +} +export const defaultFimTags: FimTagsType = { + preTag: 'BEFORE', + sufTag: 'AFTER', + midTag: 'SELECTION', +} + +export const ctrlKStream_prompt = ({ selection, prefix, suffix, userMessage, modelWasTrainedOnFIM, fimTags }: { selection: string, prefix: string, suffix: string, userMessage: string, modelWasTrainedOnFIM: boolean, fimTags: FimTagsType }) => { + const { preTag, sufTag, midTag } = fimTags + + if (modelWasTrainedOnFIM) { + // const preTag = 'PRE' + // const sufTag = 'SUF' + // const midTag = 'MID' return `\ <${preTag}> /* Original Selection: @@ -341,32 +353,34 @@ ${prefix} <${sufTag}>${suffix} <${midTag}>` } - // prompt the model on how to do FIM + // prompt the model artifically on how to do FIM else { - const preTag = 'PRE' - const sufTag = 'SUF' - const midTag = 'MID' + // const preTag = 'BEFORE' + // const sufTag = 'AFTER' + // const midTag = 'SELECTION' return `\ -Here is the user's original selection: +The user is selecting this code as their SELECTION: \`\`\` <${midTag}>${selection} \`\`\` -The user wants to apply the following instructions to the selection: +The user wants to apply the following INSTRUCTIONS to the SELECTION: ${userMessage} -Please rewrite the selection following the user's instructions. +Please edit the SELECTION following the user's INSTRUCTIONS, and return the new SELECTION. -Instructions to follow: -1. Follow the user's instructions -2. You may ONLY CHANGE the selection, and nothing else in the file -3. Make sure all brackets in the new selection are balanced the same was as in the original selection -3. Be careful not to duplicate or remove variables, comments, or other syntax by mistake +Note that the SELECTION has code that comes before it. This code is indicated with <${preTag}>...before<${preTag}/>. +Note also that the SELECTION has code that comes after it. This code is indicated with <${sufTag}>...after<${sufTag}/>. + +Instructions: +1. Your OUTPUT should be a SINGLE PIECE OF CODE of the form <${midTag}>...new_selection<${midTag}/>. Do not give any explanation before or after this. ONLY output this format, nothing more. +2. You may ONLY CHANGE the original SELECTION, and NOT the content in the <${preTag}>...<${preTag}/> or <${sufTag}>...<${sufTag}/> tags. +3. Make sure all brackets in the new selection are balanced the same as in the original selection. +4. Be careful not to duplicate or remove variables, comments, or other syntax by mistake. Complete the following: <${preTag}>${prefix} -<${sufTag}>${suffix} -<${midTag}>` +<${sufTag}>${suffix}` } }; diff --git a/src/vs/workbench/contrib/void/browser/quickEditActions.ts b/src/vs/workbench/contrib/void/browser/quickEditActions.ts index 39461b188..285e21d29 100644 --- a/src/vs/workbench/contrib/void/browser/quickEditActions.ts +++ b/src/vs/workbench/contrib/void/browser/quickEditActions.ts @@ -11,6 +11,7 @@ import { IMetricsService } from '../../../../platform/void/common/metricsService import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { IInlineDiffsService } from './inlineDiffsService.js'; import { InputBox } from '../../../../base/browser/ui/inputbox/inputBox.js'; +import { roundRangeToLines } from './sidebarActions.js'; export type QuickEditPropsType = { @@ -54,7 +55,7 @@ registerAction2(class extends Action2 { if (!editor) return; const model = editor.getModel() if (!model) return; - const selection = editor.getSelection() + const selection = roundRangeToLines(editor.getSelection(), { emptySelectionBehavior: 'line' }) if (!selection) return; diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx index e64c43c3e..f77bc2b42 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx @@ -69,7 +69,7 @@ export const BlockCode = ({ text, buttonsOnHover, language }: { text: string, bu const isSingleLine = !text.includes('\n') return (<> -
+
{buttonsOnHover === null ? null : (
{buttonsOnHover}
diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index ef5221f78..e89cb665b 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -49,14 +49,14 @@ const CodeButtonsOnHover = ({ text }: { text: string }) => { return <> )} {showDismiss && onDismiss && ( - )}
@@ -75,10 +75,10 @@ export const ErrorDisplay = ({ {/* Expandable Details */} {isExpanded && details && ( -
+
- Full Error: -
{details}
+ Full Error: +
{details}
)} diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx index 92ecf82e1..4a5f23b10 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx @@ -24,7 +24,14 @@ export const Sidebar = ({ className }: { className: string }) => { const isDark = useIsDark() // ${isDark ? 'dark' : ''} return
-
+
{/* { const tabs = ['chat', 'settings', 'threadSelector'] @@ -32,7 +39,7 @@ export const Sidebar = ({ className }: { className: string }) => { sidebarStateService.setState({ currentTab: tabs[(index + 1) % tabs.length] as any }) }}>clickme {tab} */} -
+
diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 0ac769f31..e34209ae0 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 @@ -272,7 +272,7 @@ export const SelectedFiles = ( return ( !!selections && selections.length !== 0 && (
{selections.map((selection, i) => { @@ -285,13 +285,13 @@ export const SelectedFiles = ( {/* selection summary */}
{ setSelectionIsOpened(s => { @@ -311,12 +311,7 @@ export const SelectedFiles = ( {/* X button */} {type === 'staging' && { e.stopPropagation(); if (type !== 'staging') return; @@ -347,7 +342,7 @@ export const SelectedFiles = (
{/* selection text */} {isThisSelectionOpened && -
+
} @@ -636,13 +631,13 @@ export const SidebarChat = () => { // .split(' ') // .map(style => `@@[&_div.monaco-inputbox]:!void-${style}`) // .join(' '); - `@@[&_textarea]:!void-bg-transparent + ` @@[&_textarea]:!void-outline-none - @@[&_textarea]:!void-text-vscode-input-fg @@[&_textarea]:!void-min-h-[81px] @@[&_textarea]:!void-max-h-[500px] @@[&_div.monaco-inputbox]:!void-border-none - @@[&_div.monaco-inputbox]:!void-outline-none` + @@[&_div.monaco-inputbox]:!void-outline-none + ` } > diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx index e6adb5f22..130ccf10d 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx @@ -53,7 +53,7 @@ export const SidebarThreadSelector = () => {
{/* a list of all the past threads */} -
+
{sortedThreadIds.map((threadId) => { if (!allThreads) return <>Error: Threads not found. @@ -88,7 +88,7 @@ export const SidebarThreadSelector = () => { ) })} -
+
) diff --git a/src/vs/workbench/contrib/void/browser/react/src/styles.css b/src/vs/workbench/contrib/void/browser/react/src/styles.css index 317421539..fce5698e3 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/styles.css +++ b/src/vs/workbench/contrib/void/browser/react/src/styles.css @@ -20,6 +20,16 @@ +.inherit-bg-all-restyle > * { + background-color: inherit !important; +} + + +.bg-editor-style-override { + --vscode-sideBar-background: var(--vscode-editor-background); +} + + /* html { font-size: var(--vscode-font-size); 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 402ab1a8f..029e00001 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 @@ -60,6 +60,10 @@ export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, plac const contextViewProvider = accessor.get('IContextViewService') return [ container, contextViewProvider, @@ -193,11 +197,12 @@ export const VoidCheckBox = ({ label, value, onClick, className }: { label: stri } -export const VoidSelectBox = ({ onChangeSelection, onCreateInstance, selectBoxRef, options }: { +export const VoidSelectBox = ({ onChangeSelection, onCreateInstance, selectBoxRef, options, className }: { onChangeSelection: (value: T) => void; onCreateInstance?: ((instance: SelectBox) => void | IDisposable[]); selectBoxRef?: React.MutableRefObject; options: readonly { text: string, value: T }[]; + className?:string; }) => { const accessor = useAccessor() const contextViewProvider = accessor.get('IContextViewService') @@ -205,7 +210,9 @@ export const VoidSelectBox = ({ onChangeSelection, onCreateInstance, selectB let containerRef = useRef(null); return { containerRef.current = container @@ -301,9 +308,9 @@ export const VoidCodeEditor = ({ initValue, language }: { initValue: string, lan return
- instantiationService.createInstance( + className='relative z-0 @@bg-editor-style-override' // text-sm + ctor={useCallback((container) => { + return instantiationService.createInstance( CodeEditorWidget, container, { @@ -347,7 +354,7 @@ export const VoidCodeEditor = ({ initValue, language }: { initValue: string, lan { isSimpleWidget: true, }) - , [instantiationService]) + }, [instantiationService]) } onCreateInstance={useCallback((editor: CodeEditorWidget) => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx index 65d107cef..1a3bd7343 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/ModelDropdown.tsx @@ -32,6 +32,7 @@ const ModelSelectBox = ({ options, featureName }: { options: ModelOption[], feat let weChangedText = false return { if (weChangedText) return @@ -84,17 +85,20 @@ const DummySelectBox = () => { className={` flex items-center flex-nowrap text-ellipsis - text-vscode-charts-yellow - hover:brightness-90 transition-all duration-200 + + text-void-warning brightness-90 opacity-90 + + hover:brightness-75 transition-all duration-200 cursor-pointer + text-xs `} onClick={openSettings} > - Model required + Provider required
// return void, text: string, icon: React.ReactNode, disabled: boolean }) => { - return
+ return
- + {text}
@@ -195,16 +195,19 @@ export const ModelDump = () => { const disabled = !providerEnabled - return
+ return
{/* left part is width:full */}
- {isNewProviderName ? displayInfoOfProviderName(providerName).title : ''} + {isNewProviderName ? displayInfoOfProviderName(providerName).title : ''} {modelName} {/* {`${modelName} (${providerName})`} */}
{/* right part is anything that fits */}
- {isAutodetected ? '(detected locally)' : isDefault ? '' : '(custom model)'} + {isAutodetected ? '(detected locally)' : isDefault ? '' : '(custom model)'}
{ if (weChangedTextRef) return voidSettingsService.setSettingOfProvider(providerName, settingName, newVal) @@ -281,7 +284,7 @@ const ProviderSetting = ({ providerName, settingName }: { providerName: Provider }, [voidSettingsService, providerName, settingName])} multiline={false} /> - {subTextMd === undefined ? null :
+ {subTextMd === undefined ? null :
} @@ -357,6 +360,7 @@ export const VoidProviderSettings = ({ providerNames }: { providerNames: Provide // })} // // } +type TabName = 'models' | 'general' export const VoidFeatureFlagSettings = () => { const accessor = useAccessor() @@ -380,12 +384,85 @@ export const VoidFeatureFlagSettings = () => { } +export const FeaturesTab = () => { + return <> +

Local Providers

+ {/*

{`Keep your data private by hosting AI locally on your computer.`}

*/} + {/*

{`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 */} +
+ + + + + +

Providers

+

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

+ {/*

{`Access models like ChatGPT and Claude. We recommend using Anthropic or OpenAI as providers, or Groq as a faster alternative.`}

*/} + + + + +

Models

+ + + + + + + +} + + + + + + +const OneClickSwitch = () => { + +} + + +const GeneralTab = () => { + return <> + {/* */} + + {/* keyboard shortcuts */} + +

General Settings

+

{`VS Code's built-in settings.`}

+ +

Keyboard Settings

+

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

+ + +

One-click Switch

+ + Transfer your VS Code settings to Void. + +

Theme

+ + +

Rules for AI

+ + + +} + // full settings export const Settings = () => { const isDark = useIsDark() - const [tab, setTab] = useState<'models' | 'features'>('models') + const [tab, setTab] = useState('models') return
@@ -404,9 +481,9 @@ export const Settings = () => { - {/* */} +
{/* separator */} @@ -417,41 +494,11 @@ export const Settings = () => {
-

Local Providers

- {/*

{`Keep your data private by hosting AI locally on your computer.`}

*/} - {/*

{`Instructions:`}

*/} -

{`Void can access any model that you host locally. By default, we automatically detect your local models.`}

-
-

-

-

-

- {/* TODO we should create UI for downloading models without user going into terminal */} -
- - - - - -

More Providers

-

{`Void can also access models like ChatGPT and Claude. We recommend using Anthropic or OpenAI.`}

- {/*

{`Access models like ChatGPT and Claude. We recommend using Anthropic or OpenAI as providers, or Groq as a faster alternative.`}

*/} - - - - -

Models

- - - - - - +
-
-

{ setTab('features') }}>Features

- {/* */} +
+
diff --git a/src/vs/workbench/contrib/void/browser/react/tailwind.config.js b/src/vs/workbench/contrib/void/browser/react/tailwind.config.js index d310f0e8f..5f29d06fd 100644 --- a/src/vs/workbench/contrib/void/browser/react/tailwind.config.js +++ b/src/vs/workbench/contrib/void/browser/react/tailwind.config.js @@ -9,7 +9,34 @@ module.exports = { content: ['./src2/**/*.{jsx,tsx}'], // uses these files to decide how to transform the css file theme: { extend: { + fontSize: { + xs: '10px', + sm: '12px', + root: '13px', + lg: '14px', + xl: '16px', + '2xl': '18px', + '3xl': '20px', + '4xl': '24px', + '5xl': '30px', + '6xl': '36px', + '7xl': '48px', + '8xl': '64px', + '9xl': '72px', + }, + // common colors to use, ordered light to dark + colors: { + "void-bg-1": "var(--vscode-input-background)", + "void-bg-2": "var(--vscode-sideBar-background)", + "void-bg-3": "var(--vscode-editor-background)", + + "void-fg-1": "var(--vscode-editor-foreground)", + "void-fg-2": "var(--vscode-input-foreground)", + "void-fg-3": "var(--vscode-input-placeholderForeground)", + "void-warning": "var(--vscode-charts-orange)", + + vscode: { // see: https://code.visualstudio.com/api/extension-guides/webview#theming-webview-content @@ -39,8 +66,8 @@ module.exports = { "input-bg": "var(--vscode-input-background)", "input-border": "var(--vscode-input-border)", "input-fg": "var(--vscode-input-foreground)", - "input-placeholder-fg": "var(--vscode-placeholderForeground)", - "input-active-bg": "var(--vscode-activeBackground)", + "input-placeholder-fg": "var(--vscode-input-placeholderForeground)", + "input-active-bg": "var(--vscode-input-activeBackground)", "input-option-active-border": "var(--vscode-inputOption-activeBorder)", "input-option-active-fg": "var(--vscode-inputOption-activeForeground)", "input-option-hover-bg": "var(--vscode-inputOption-hoverBackground)", diff --git a/src/vs/workbench/contrib/void/browser/react/tsup.config.js b/src/vs/workbench/contrib/void/browser/react/tsup.config.js index 2a7547a05..9070f8e15 100644 --- a/src/vs/workbench/contrib/void/browser/react/tsup.config.js +++ b/src/vs/workbench/contrib/void/browser/react/tsup.config.js @@ -9,7 +9,7 @@ export default defineConfig({ entry: [ './src2/sidebar-tsx/index.tsx', './src2/void-settings-tsx/index.tsx', - './src2/ctrl-k-tsx/index.tsx', + './src2/quick-edit-tsx/index.tsx', './src2/diff/index.tsx', ], outDir: './out', diff --git a/src/vs/workbench/contrib/void/browser/sidebarActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts index be76d2aa8..b2a4df915 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarActions.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts @@ -26,9 +26,18 @@ import { VOID_OPEN_SETTINGS_ACTION_ID } from './voidSettingsPane.js'; // ---------- Register commands and keybindings ---------- -const roundRangeToLines = (range: IRange | null | undefined) => { +export const roundRangeToLines = (range: IRange | null | undefined, options: { emptySelectionBehavior: 'null' | 'line' }) => { if (!range) return null + + // treat as no selection if selection is empty + if (range.endColumn === range.startColumn && range.endLineNumber === range.startLineNumber) { + if (options.emptySelectionBehavior === 'null') + return null + else if (options.emptySelectionBehavior === 'line') + return { startLineNumber: range.startLineNumber, startColumn: 1, endLineNumber: range.startLineNumber, endColumn: 1 } + } + // IRange is 1-indexed const endLine = range.endColumn === 1 ? range.endLineNumber - 1 : range.endLineNumber // e.g. if the user triple clicks, it selects column=0, line=line -> column=0, line=line+1 const newRange: IRange = { @@ -72,51 +81,49 @@ registerAction2(class extends Action2 { stateService.fireFocusChat() const editor = editorService.getActiveCodeEditor() - const selectionRange = roundRangeToLines( - // accessor.get(IEditorService).activeTextEditorControl?.getSelection() - editor?.getSelection() - ) + // accessor.get(IEditorService).activeTextEditorControl?.getSelection() + const selectionRange = roundRangeToLines(editor?.getSelection(), { emptySelectionBehavior: 'null' }) + // select whole lines if (selectionRange) { - // select whole lines editor?.setSelection({ startLineNumber: selectionRange.startLineNumber, endLineNumber: selectionRange.endLineNumber, startColumn: 1, endColumn: Number.MAX_SAFE_INTEGER }) + } - const selectionStr = getContentInRange(model, selectionRange) - - const selection: CodeStagingSelection = selectionStr === null || selectionRange.startLineNumber > selectionRange.endLineNumber ? { - type: 'File', - fileURI: model.uri, - selectionStr: null, - range: null, - } : { - type: 'Selection', - fileURI: model.uri, - selectionStr: selectionStr, - range: selectionRange, - } - - // add selection to staging - const threadHistoryService = accessor.get(IThreadHistoryService) - const currentStaging = threadHistoryService.state._currentStagingSelections - const currentStagingEltIdx = currentStaging?.findIndex(s => - s.fileURI.fsPath === model.uri.fsPath - && s.range?.startLineNumber === selection.range?.startLineNumber - && s.range?.endLineNumber === selection.range?.endLineNumber - ) - - // if matches with existing selection, overwrite - if (currentStagingEltIdx !== undefined && currentStagingEltIdx !== -1) { - threadHistoryService.setStaging([ - ...currentStaging!.slice(0, currentStagingEltIdx), - selection, - ...currentStaging!.slice(currentStagingEltIdx + 1, Infinity) - ]) - } - // if no match, add - else { - threadHistoryService.setStaging([...(currentStaging ?? []), selection]) - } + const selectionStr = getContentInRange(model, selectionRange) + + const selection: CodeStagingSelection = !selectionRange || !selectionStr || (selectionRange.startLineNumber > selectionRange.endLineNumber) ? { + type: 'File', + fileURI: model.uri, + selectionStr: null, + range: null, + } : { + type: 'Selection', + fileURI: model.uri, + selectionStr: selectionStr, + range: selectionRange, + } + + // add selection to staging + const threadHistoryService = accessor.get(IThreadHistoryService) + const currentStaging = threadHistoryService.state._currentStagingSelections + const currentStagingEltIdx = currentStaging?.findIndex(s => + s.fileURI.fsPath === model.uri.fsPath + && s.range?.startLineNumber === selection.range?.startLineNumber + && s.range?.endLineNumber === selection.range?.endLineNumber + ) + + // if matches with existing selection, overwrite + if (currentStagingEltIdx !== undefined && currentStagingEltIdx !== -1) { + threadHistoryService.setStaging([ + ...currentStaging!.slice(0, currentStagingEltIdx), + selection, + ...currentStaging!.slice(currentStagingEltIdx + 1, Infinity) + ]) + } + // if no match, add + else { + threadHistoryService.setStaging([...(currentStaging ?? []), selection]) } }