diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46986234..f41b0348 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,19 +43,19 @@ First, run `npm install -g node-gyp`. Then: ### Building Void -To build Void, open `void/` inside VSCode. Then: +To build Void, open `void/` inside VSCode. Then open your terminal and run: 1. `npm install` to install all dependencies. 2. `npm run watchreact` to build Void's browser dependencies like React. -3. Build. +3. Build Void. - Press Cmd+Shift+B (Mac). - Press Ctrl+Shift+B (Windows/Linux). - This step can take ~5 min. The build is done when you see two check marks. -4. Run. +4. Run Void. - Run `./scripts/code.sh` (Mac/Linux). - Run `./scripts/code.bat` (Windows). - This command should open up the built IDE. You can always press Ctrl+R (Cmd+R) inside the new window to see changes without re-building, or press or Ctrl+Shift+P in the new window and run "Reload Window". - + - If you would like to reset Void back to its default settings, you can run `./scripts/code.sh --user-data-dir ./.tmp/user-data --extensions-dir ./.tmp/extensions` (mac). This will save all data and extensions to the `.tmp` folder. You can delete this folder to reset your settings. #### Building Void from Terminal diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index fce75bcd..64c74477 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -144,8 +144,8 @@ export const defaultProviderSettings = { export type ProviderName = keyof typeof defaultProviderSettings export const providerNames = Object.keys(defaultProviderSettings) as ProviderName[] -export const localProviderNames: ProviderName[] = ['ollama'] // all local names -export const nonlocalProviderNames = providerNames.filter((name) => !localProviderNames.includes(name)) // all non-local names +export const localProviderNames = ['ollama', 'openAICompatible'] satisfies ProviderName[] // all local names +export const nonlocalProviderNames = providerNames.filter((name) => !(localProviderNames as string[]).includes(name)) // all non-local names type CustomSettingName = UnionOfKeys type CustomProviderSettings = { @@ -379,7 +379,7 @@ export const featureNames = ['Ctrl+L', 'Ctrl+K', 'Autocomplete'] as const // the models of these can be refreshed (in theory all can, but not all should) -export const refreshableProviderNames = ['ollama', 'openAICompatible'] satisfies ProviderName[] +export const refreshableProviderNames = localProviderNames export type RefreshableProviderName = typeof refreshableProviderNames[number] @@ -405,7 +405,7 @@ type FeatureFlagDisplayInfo = { export const displayInfoOfFeatureFlag = (featureFlag: FeatureFlagName): FeatureFlagDisplayInfo => { if (featureFlag === 'autoRefreshModels') { return { - description: `Automatically detect local providers and models (like Ollama).`, // ${`refreshableProviderNames.map(providerName => titleOfProviderName(providerName)).join(', ')`} + description: `Automatically detect local providers and models (${refreshableProviderNames.map(providerName => displayInfoOfProviderName(providerName).title).join(', ')}).`, } } throw new Error(`featureFlagInfo: Unknown feature flag: "${featureFlag}"`) diff --git a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts index da288609..5ccb08ec 100644 --- a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts +++ b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts @@ -117,7 +117,7 @@ type CtrlKZone = { editorId: string; // the editor the input lives on _mountInfo: null | { - inputBox: InputBox | null; // the input box that lives in the zone + inputBoxRef: { current: InputBox | null }; // the input box that lives in the zone dispose: () => void; refresh: () => void; } @@ -136,7 +136,7 @@ type DiffZone = { } | { isStreaming: false; streamRequestIdRef?: undefined; - line: null; + line?: undefined; }; editorId?: undefined; } & CommonZoneProps @@ -210,9 +210,8 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { this._register( model.onDidChangeContent(e => { // it's as if we just called _write, now all we need to do is realign and refresh - const uri = model.uri - if (this.weAreWriting) return + const uri = model.uri this._onUserChangeContent(uri, e) }) ) @@ -244,6 +243,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { private _onInternalChangeContent(uri: URI, { shouldRealign }: { shouldRealign: false | { newText: string, oldRange: IRange } }) { if (shouldRealign) { const { newText, oldRange } = shouldRealign + console.log('realiging', newText, oldRange) this._realignAllDiffAreasLines(uri, newText, oldRange) } this._refreshStylesAndDiffsInURI(uri) @@ -329,7 +329,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { let zoneId: string | null = null let viewZone_: IViewZone | null = null - let inputBox_: InputBox | null = null + const inputBoxRef: { current: InputBox | null } = { current: null } const itemId = this._consistentEditorItemService.addToEditor(editor, () => { const domNode = document.createElement('div'); @@ -352,7 +352,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { mountCtrlK(domNode, accessor, { diffareaid: ctrlKZone.diffareaid, onGetInputBox: (inputBox) => { - inputBox_ = inputBox + inputBoxRef.current = inputBox // if it's mounting for the first time, focus it if (!(ctrlKZone.diffareaid in this.mostRecentTextOfCtrlKZoneId)) { // detect first mount this way (a hack) this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] = undefined @@ -381,10 +381,8 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { }) }) - - return { - inputBox: inputBox_, + inputBoxRef, refresh: () => editor.changeViewZones(accessor => { if (zoneId && viewZone_) { viewZone_.afterLineNumber = ctrlKZone.startLine - 1 @@ -394,7 +392,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { dispose: () => { this._consistentEditorItemService.removeFromEditor(itemId) }, - } + } satisfies CtrlKZone['_mountInfo'] } @@ -405,6 +403,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { if (diffArea.type !== 'CtrlKZone') continue if (!diffArea._mountInfo) { diffArea._mountInfo = this._addCtrlKZoneInput(diffArea) + console.log('MOUNTED', diffArea.diffareaid) } else { diffArea._mountInfo.refresh() @@ -579,10 +578,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { type: 'DiffZone', _diffOfId: {}, _URI: uri, - _streamState: { - isStreaming: false, - line: null, - } as const, + _streamState: { isStreaming: false }, _removeStylesFns: new Set(), } } @@ -740,17 +736,14 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { for (const diffareaid of this.diffAreasOfURI[model.uri.fsPath] || []) { const diffArea = this.diffAreaOfId[diffareaid] - console.log('DA', diffArea.startLine, diffArea.endLine) - console.log('CHANGE', startLine, endLine) - // if the diffArea is entirely above the range, it is not affected if (diffArea.endLine < startLine) { - // console.log('DA FULLY ABOVE (doing nothing)') + // console.log('CHANGE FULLY BELOW DA (doing nothing)') continue } // if a diffArea is entirely below the range, shift the diffArea up/down by the delta amount of newlines else if (endLine < diffArea.startLine) { - // console.log('DA FULLY BELOW') + // console.log('CHANGE FULLY ABOVE DA') const changedRangeHeight = endLine - startLine + 1 const deltaNewlines = newTextHeight - changedRangeHeight diffArea.startLine += deltaNewlines @@ -771,7 +764,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { } // if the change contains only the diffArea's top else if (startLine < diffArea.startLine && diffArea.startLine <= endLine) { - // console.log('TOP ONLY') + // console.log('CHANGE CONTAINS TOP OF DA ONLY') const numOverlappingLines = endLine - diffArea.startLine + 1 const numRemainingLinesInDA = diffArea.endLine - diffArea.startLine + 1 - numOverlappingLines const newHeight = (numRemainingLinesInDA - 1) + (newTextHeight - 1) + 1 @@ -780,7 +773,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { } // if the change contains only the diffArea's bottom else if (startLine <= diffArea.endLine && diffArea.endLine < endLine) { - // console.log('BOTTOM ONLY') + // console.log('CHANGE CONTAINS BOTTOM OF DA ONLY') const numOverlappingLines = diffArea.endLine - startLine + 1 diffArea.endLine += newTextHeight - numOverlappingLines } @@ -823,47 +816,41 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { // if streaming, use diffs to figure out where to write new code // these are two different coordinate systems - new and old line number - let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted - let originalCodeStartLine: number // get original[oldStartingPoint...] + let newCodeEndLine: number // get file[diffArea.startLine...newFileEndLine] with line=newFileEndLine highlighted + let originalCodeStartLine: number // get original[oldStartingPoint...] (line in the original code, so starts at 1) const lastDiff = computedDiffs.pop() if (!lastDiff) { + console.log('!lastDiff') // if the writing is identical so far, display no changes - newFileEndLine = diffZone.startLine originalCodeStartLine = 1 + newCodeEndLine = 1 } else { - if (lastDiff.type === 'insertion') { - newFileEndLine = lastDiff.endLine - originalCodeStartLine = lastDiff.originalStartLine - } - else if (lastDiff.type === 'deletion') { - newFileEndLine = lastDiff.startLine - originalCodeStartLine = lastDiff.originalStartLine - } - else if (lastDiff.type === 'edit') { - newFileEndLine = lastDiff.endLine - originalCodeStartLine = lastDiff.originalStartLine - } - else { + originalCodeStartLine = lastDiff.originalStartLine + if (lastDiff.type === 'insertion' || lastDiff.type === 'edit') + newCodeEndLine = lastDiff.endLine + else if (lastDiff.type === 'deletion') + newCodeEndLine = lastDiff.startLine + else throw new Error(`Void: diff.type not recognized on: ${lastDiff}`) - } } - diffZone._streamState.line = newFileEndLine // lines are 1-indexed - const newFileTop = llmText.split('\n').slice(diffZone.startLine, (newFileEndLine - 1)).join('\n') - const oldFileBottom = diffZone.originalCode.split('\n').slice((originalCodeStartLine - 1), Infinity).join('\n') + 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 = `${newFileTop}\n${oldFileBottom}` + const newCode = `${newCodeTop}\n${oldFileBottom}` this._writeText(uri, newCode, { startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, // 1-indexed { shouldRealignDiffAreas: true } ) + // add diffZone.startLine to convert to right coordinate system (line in file, not in diffarea) + diffZone._streamState.line = (diffZone.startLine - 1) + newCodeEndLine return computedDiffs @@ -871,6 +858,54 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { + // // if streaming, use diffs to figure out where to write new code + // // these are two different coordinate systems - new and old line number + // let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted + // let originalCodeStartLine: number // get original[oldStartingPoint...] + + // const lastDiff = computedDiffs.pop() + + // if (!lastDiff) { + // // if the writing is identical so far, display no changes + // newFileEndLine = diffZone.startLine + // originalCodeStartLine = 1 + // } + // else { + // if (lastDiff.type === 'insertion') { + // newFileEndLine = lastDiff.endLine + // originalCodeStartLine = lastDiff.originalStartLine + // } + // else if (lastDiff.type === 'deletion') { + // newFileEndLine = lastDiff.startLine + // originalCodeStartLine = lastDiff.originalStartLine + // } + // else if (lastDiff.type === 'edit') { + // newFileEndLine = lastDiff.endLine + // originalCodeStartLine = lastDiff.originalStartLine + // } + // else { + // throw new Error(`Void: diff.type not recognized on: ${lastDiff}`) + // } + // } + + // diffZone._streamState.line = newFileEndLine + + // // lines are 1-indexed + // const newFileTop = llmText.split('\n').slice(diffZone.startLine, (newFileEndLine - 1)).join('\n') + // const oldFileBottom = diffZone.originalCode.split('\n').slice((originalCodeStartLine - 1), Infinity).join('\n') + + // const newCode = `${newFileTop}\n${oldFileBottom}` + + // this._writeText(uri, newCode, + // { startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, // 1-indexed + // { shouldRealignDiffAreas: true } + // ) + + + // return computedDiffs + + + @@ -887,7 +922,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { if (diffArea.type !== 'CtrlKZone') continue const noOverlap = diffArea.startLine > endLine || diffArea.endLine < startLine if (!noOverlap) { - setTimeout(() => diffArea._mountInfo?.inputBox?.focus(), 0) + setTimeout(() => diffArea._mountInfo?.inputBoxRef.current?.focus(), 0) return } } @@ -904,7 +939,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { _mountInfo: null, } const ctrlKZone = this._addDiffArea(adding) - this._refreshStylesAndDiffsInURI(uri) onFinishEdit() @@ -919,6 +953,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { const uri = ctrlKZone._URI const { onFinishEdit } = this._addToHistory(uri) this._deleteCtrlKZone(ctrlKZone) + this._refreshStylesAndDiffsInURI(uri) onFinishEdit() } @@ -983,14 +1018,13 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { startLine = startLine_ endLine = endLine_ - if (!_mountInfo?.inputBox) return - userMessage = _mountInfo.inputBox?.value + if (!_mountInfo?.inputBoxRef.current) return + userMessage = _mountInfo.inputBoxRef.current?.value } else { throw new Error(`Void: diff.type not recognized on: ${featureName}`) } - const currentFileStr = this._readURI(uri) if (currentFileStr === null) return const originalCode = currentFileStr.split('\n').slice((startLine - 1), (endLine - 1) + 1).join('\n') @@ -1055,7 +1089,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { const latestOriginalFileStart: IPosition = { lineNumber: 1, column: 1 } const onDone = () => { - diffZone._streamState = { isStreaming: false, line: null } + diffZone._streamState = { isStreaming: false, } if (featureName === 'Ctrl+K') { const ctrlKZone = this.diffAreaOfId[opts.diffareaid] as CtrlKZone @@ -1111,11 +1145,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { this._llmMessageService.abort(streamRequestId) - diffZone._streamState = { - isStreaming: false, - streamRequestIdRef: undefined, - line: null - } + diffZone._streamState = { isStreaming: false, } } diff --git a/src/vs/workbench/contrib/void/browser/react/build.js b/src/vs/workbench/contrib/void/browser/react/build.js index 118e2eaa..56ace25e 100755 --- a/src/vs/workbench/contrib/void/browser/react/build.js +++ b/src/vs/workbench/contrib/void/browser/react/build.js @@ -4,13 +4,42 @@ *--------------------------------------------------------------------------------------------*/ import { spawn, execSync } from 'child_process'; +// Added lines below +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const __void_name = 'void' + +// hack to refresh styles automatically +function saveStylesFile() { + setTimeout(() => { + try { + // Find "void" in __dirname and use that as our base: + const voidIdx = __dirname.indexOf(__void_name); + const baseDir = __dirname.substring(0, voidIdx + __void_name.length); + const target = path.join( + baseDir, + 'src/vs/workbench/contrib/void/browser/react/src2/styles.css' + ); + + // Or re-write with the same content: + const content = fs.readFileSync(target, 'utf8'); + fs.writeFileSync(target, content, 'utf8'); + console.log('[scope-tailwind] Force-saved styles.css'); + } catch (err) { + console.error('[scope-tailwind] Error saving styles.css:', err); + } + }, 5000); +} const args = process.argv.slice(2); const isWatch = args.includes('--watch') || args.includes('-w'); if (isWatch) { // Watch mode - // Create a watcher for scope-tailwind using nodemon const scopeTailwindWatcher = spawn('npx', [ 'nodemon', '--watch', 'src', @@ -19,15 +48,17 @@ if (isWatch) { 'npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "void-"' ]); - // Create a watcher for tsup in watch mode const tsupWatcher = spawn('npx', [ 'tsup', '--watch' ]); - // Handle scope-tailwind watcher output scopeTailwindWatcher.stdout.on('data', (data) => { console.log(`[scope-tailwind] ${data}`); + // If the output mentions "styles.css", trigger the save: + if (data.toString().includes('styles.css')) { + saveStylesFile(); + } }); scopeTailwindWatcher.stderr.on('data', (data) => { 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 8ecc9e8f..e64c43c3 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 @@ -70,7 +70,6 @@ export const BlockCode = ({ text, buttonsOnHover, language }: { text: string, bu 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 62d24b1c..ef5221f7 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 @@ -45,19 +45,18 @@ const CodeButtonsOnHover = ({ text }: { text: string }) => { }) }, [inlineDiffService]) - const isSingleLine = !text.includes('\n') return <>