mirror of
https://github.com/voideditor/void
synced 2026-05-23 01:18:25 +00:00
big changes + react build pipeline (broken)
This commit is contained in:
parent
b52f0d8dd7
commit
8da76e72df
34 changed files with 25359 additions and 3340 deletions
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"type": "module"
|
||||
}
|
||||
|
|
@ -1,450 +1,450 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { findDiffs } from './src/extension/findDiffs';
|
||||
import { throttle } from 'lodash';
|
||||
import { DiffArea, BaseDiff, Diff } from '../common/shared_types';
|
||||
import { readFileContentOfUri } from './src/extension/extensionLib/readFileContentOfUri';
|
||||
import { AbortRef, sendLLMMessage } from '../common/sendLLMMessage';
|
||||
import { writeFileWithDiffInstructions } from '../common/systemPrompts';
|
||||
import { VoidConfig } from './src/webviews/common/contextForConfig';
|
||||
// import * as vscode from 'vscode';
|
||||
// import { findDiffs } from './src/extension/findDiffs';
|
||||
// import { throttle } from 'lodash';
|
||||
// import { DiffArea, BaseDiff, Diff } from '../common/shared_types';
|
||||
// import { readFileContentOfUri } from './src/extension/extensionLib/readFileContentOfUri';
|
||||
// import { AbortRef, sendLLMMessage } from '../common/sendLLMMessage';
|
||||
// import { writeFileWithDiffInstructions } from '../common/systemPrompts';
|
||||
// import { VoidConfig } from './src/webviews/common/contextForConfig';
|
||||
|
||||
|
||||
const THROTTLE_TIME = 100
|
||||
// const THROTTLE_TIME = 100
|
||||
|
||||
// TODO in theory this should be disposed
|
||||
const lightGrayDecoration = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: 'rgba(218 218 218 / .2)',
|
||||
isWholeLine: true,
|
||||
})
|
||||
const darkGrayDecoration = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: 'rgb(148 148 148 / .2)',
|
||||
isWholeLine: true,
|
||||
})
|
||||
// // TODO in theory this should be disposed
|
||||
// const lightGrayDecoration = vscode.window.createTextEditorDecorationType({
|
||||
// backgroundColor: 'rgba(218 218 218 / .2)',
|
||||
// isWholeLine: true,
|
||||
// })
|
||||
// const darkGrayDecoration = vscode.window.createTextEditorDecorationType({
|
||||
// backgroundColor: 'rgb(148 148 148 / .2)',
|
||||
// isWholeLine: true,
|
||||
// })
|
||||
|
||||
// responsible for displaying diffs and showing accept/reject buttons
|
||||
export class DiffProvider implements vscode.CodeLensProvider {
|
||||
// // responsible for displaying diffs and showing accept/reject buttons
|
||||
// export class DiffProvider implements vscode.CodeLensProvider {
|
||||
|
||||
private _originalFileOfDocument: { [docUriStr: string]: string } = {}
|
||||
private _diffAreasOfDocument: { [docUriStr: string]: DiffArea[] } = {}
|
||||
private _diffsOfDocument: { [docUriStr: string]: Diff[] } = {}
|
||||
// private _originalFileOfDocument: { [docUriStr: string]: string } = {}
|
||||
// private _diffAreasOfDocument: { [docUriStr: string]: DiffArea[] } = {}
|
||||
// private _diffsOfDocument: { [docUriStr: string]: Diff[] } = {}
|
||||
|
||||
private _diffareaidPool = 0
|
||||
private _diffidPool = 0
|
||||
// private _diffareaidPool = 0
|
||||
// private _diffidPool = 0
|
||||
|
||||
private _extensionUri: vscode.Uri
|
||||
// private _extensionUri: vscode.Uri
|
||||
|
||||
// used internally by vscode
|
||||
private _onDidChangeCodeLenses: vscode.EventEmitter<void> = new vscode.EventEmitter<void>(); // signals a UI refresh on .fire() events
|
||||
public readonly onDidChangeCodeLenses: vscode.Event<void> = this._onDidChangeCodeLenses.event;
|
||||
// // used internally by vscode
|
||||
// private _onDidChangeCodeLenses: vscode.EventEmitter<void> = new vscode.EventEmitter<void>(); // signals a UI refresh on .fire() events
|
||||
// public readonly onDidChangeCodeLenses: vscode.Event<void> = this._onDidChangeCodeLenses.event;
|
||||
|
||||
// used internally by vscode
|
||||
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens[]> {
|
||||
const docUriStr = document.uri.toString()
|
||||
return this._diffsOfDocument[docUriStr]?.flatMap(diff => diff.lenses) ?? []
|
||||
}
|
||||
// // used internally by vscode
|
||||
// public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens[]> {
|
||||
// const docUriStr = document.uri.toString()
|
||||
// return this._diffsOfDocument[docUriStr]?.flatMap(diff => diff.lenses) ?? []
|
||||
// }
|
||||
|
||||
// declared by us, registered with vscode.languages.registerCodeLensProvider()
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
this._extensionUri = context.extensionUri
|
||||
// // declared by us, registered with vscode.languages.registerCodeLensProvider()
|
||||
// constructor(context: vscode.ExtensionContext) {
|
||||
// this._extensionUri = context.extensionUri
|
||||
|
||||
console.log('Creating DisplayChangesProvider')
|
||||
// console.log('Creating DisplayChangesProvider')
|
||||
|
||||
// this acts as a useEffect every time text changes
|
||||
vscode.workspace.onDidChangeTextDocument((e) => {
|
||||
// // this acts as a useEffect every time text changes
|
||||
// vscode.workspace.onDidChangeTextDocument((e) => {
|
||||
|
||||
const editor = vscode.window.activeTextEditor
|
||||
// const editor = vscode.window.activeTextEditor
|
||||
|
||||
if (!editor) return
|
||||
// if (!editor) return
|
||||
|
||||
const docUriStr = editor.document.uri.toString()
|
||||
const changes = e.contentChanges.map(c => ({ startLine: c.range.start.line, endLine: c.range.end.line, text: c.text, }))
|
||||
// const docUriStr = editor.document.uri.toString()
|
||||
// const changes = e.contentChanges.map(c => ({ startLine: c.range.start.line, endLine: c.range.end.line, text: c.text, }))
|
||||
|
||||
// on user change, grow/shrink/merge/delete diff areas
|
||||
this.resizeDiffAreas(docUriStr, changes, 'currentFile')
|
||||
// // on user change, grow/shrink/merge/delete diff areas
|
||||
// this.resizeDiffAreas(docUriStr, changes, 'currentFile')
|
||||
|
||||
// refresh the diffAreas
|
||||
this.refreshStylesAndDiffs(docUriStr)
|
||||
// // refresh the diffAreas
|
||||
// this.refreshStylesAndDiffs(docUriStr)
|
||||
|
||||
})
|
||||
}
|
||||
// })
|
||||
// }
|
||||
|
||||
// used by us only
|
||||
public createDiffArea(uri: vscode.Uri, partialDiffArea: Omit<DiffArea, 'diffareaid'>, originalFile: string) {
|
||||
// // used by us only
|
||||
// public createDiffArea(uri: vscode.Uri, partialDiffArea: Omit<DiffArea, 'diffareaid'>, originalFile: string) {
|
||||
|
||||
const uriStr = uri.toString()
|
||||
// const uriStr = uri.toString()
|
||||
|
||||
this._originalFileOfDocument[uriStr] = originalFile
|
||||
// this._originalFileOfDocument[uriStr] = originalFile
|
||||
|
||||
// make sure array is defined
|
||||
if (!this._diffAreasOfDocument[uriStr]) this._diffAreasOfDocument[uriStr] = []
|
||||
// // make sure array is defined
|
||||
// if (!this._diffAreasOfDocument[uriStr]) this._diffAreasOfDocument[uriStr] = []
|
||||
|
||||
// remove all diffAreas that the new `diffArea` is overlapping with
|
||||
this._diffAreasOfDocument[uriStr] = this._diffAreasOfDocument[uriStr].filter(da => {
|
||||
const noOverlap = da.startLine > partialDiffArea.endLine || da.endLine < partialDiffArea.startLine
|
||||
if (!noOverlap) return false
|
||||
return true
|
||||
})
|
||||
// // remove all diffAreas that the new `diffArea` is overlapping with
|
||||
// this._diffAreasOfDocument[uriStr] = this._diffAreasOfDocument[uriStr].filter(da => {
|
||||
// const noOverlap = da.startLine > partialDiffArea.endLine || da.endLine < partialDiffArea.startLine
|
||||
// if (!noOverlap) return false
|
||||
// return true
|
||||
// })
|
||||
|
||||
// add `diffArea` to storage
|
||||
const diffArea = {
|
||||
...partialDiffArea,
|
||||
diffareaid: this._diffareaidPool
|
||||
}
|
||||
this._diffAreasOfDocument[uriStr].push(diffArea)
|
||||
this._diffareaidPool += 1
|
||||
// // add `diffArea` to storage
|
||||
// const diffArea = {
|
||||
// ...partialDiffArea,
|
||||
// diffareaid: this._diffareaidPool
|
||||
// }
|
||||
// this._diffAreasOfDocument[uriStr].push(diffArea)
|
||||
// this._diffareaidPool += 1
|
||||
|
||||
return diffArea
|
||||
}
|
||||
// return diffArea
|
||||
// }
|
||||
|
||||
// used by us only
|
||||
// changes the start/line locations based on the changes that were recently made. does not change any of the diffs in the diff areas
|
||||
// changes tells us how many lines were inserted/deleted so we can grow/shrink the diffAreas accordingly
|
||||
public resizeDiffAreas(docUriStr: string, changes: { text: string, startLine: number, endLine: number }[], changesTo: 'originalFile' | 'currentFile') {
|
||||
// // used by us only
|
||||
// // changes the start/line locations based on the changes that were recently made. does not change any of the diffs in the diff areas
|
||||
// // changes tells us how many lines were inserted/deleted so we can grow/shrink the diffAreas accordingly
|
||||
// public resizeDiffAreas(docUriStr: string, changes: { text: string, startLine: number, endLine: number }[], changesTo: 'originalFile' | 'currentFile') {
|
||||
|
||||
const diffAreas = this._diffAreasOfDocument[docUriStr] || []
|
||||
// const diffAreas = this._diffAreasOfDocument[docUriStr] || []
|
||||
|
||||
let endLine: 'originalEndLine' | 'endLine'
|
||||
let startLine: 'originalStartLine' | 'startLine'
|
||||
// let endLine: 'originalEndLine' | 'endLine'
|
||||
// let startLine: 'originalStartLine' | 'startLine'
|
||||
|
||||
if (changesTo === 'originalFile') {
|
||||
endLine = 'originalEndLine' as const
|
||||
startLine = 'originalStartLine' as const
|
||||
} else {
|
||||
endLine = 'endLine' as const
|
||||
startLine = 'startLine' as const
|
||||
}
|
||||
// if (changesTo === 'originalFile') {
|
||||
// endLine = 'originalEndLine' as const
|
||||
// startLine = 'originalStartLine' as const
|
||||
// } else {
|
||||
// endLine = 'endLine' as const
|
||||
// startLine = 'startLine' as const
|
||||
// }
|
||||
|
||||
for (const change of changes) {
|
||||
// for (const change of changes) {
|
||||
|
||||
// here, `change.range` is the range of the original file that gets replaced with `change.text`
|
||||
// // here, `change.range` is the range of the original file that gets replaced with `change.text`
|
||||
|
||||
|
||||
// compute net number of newlines lines that were added/removed
|
||||
const numNewLines = (change.text.match(/\n/g) || []).length
|
||||
const numLineDeletions = change.endLine - change.startLine
|
||||
const deltaNewlines = numNewLines - numLineDeletions
|
||||
// // compute net number of newlines lines that were added/removed
|
||||
// const numNewLines = (change.text.match(/\n/g) || []).length
|
||||
// const numLineDeletions = change.endLine - change.startLine
|
||||
// const deltaNewlines = numNewLines - numLineDeletions
|
||||
|
||||
// compute overlap with each diffArea and shrink/elongate the diffArea accordingly
|
||||
for (const diffArea of diffAreas) {
|
||||
// // compute overlap with each diffArea and shrink/elongate the diffArea accordingly
|
||||
// for (const diffArea of diffAreas) {
|
||||
|
||||
// if the change is fully within the diffArea, elongate it by the delta amount of newlines
|
||||
if (change.startLine >= diffArea[startLine] && change.endLine <= diffArea[endLine]) {
|
||||
diffArea[endLine] += deltaNewlines
|
||||
}
|
||||
// check if the `diffArea` was fully deleted and remove it if so
|
||||
if (diffArea[startLine] > diffArea[endLine]) {
|
||||
//remove it
|
||||
const index = diffAreas.findIndex(da => da === diffArea)
|
||||
diffAreas.splice(index, 1)
|
||||
}
|
||||
|
||||
// TODO handle other cases where eg. the change overlaps many diffAreas
|
||||
}
|
||||
// // if the change is fully within the diffArea, elongate it by the delta amount of newlines
|
||||
// if (change.startLine >= diffArea[startLine] && change.endLine <= diffArea[endLine]) {
|
||||
// diffArea[endLine] += deltaNewlines
|
||||
// }
|
||||
// // check if the `diffArea` was fully deleted and remove it if so
|
||||
// if (diffArea[startLine] > diffArea[endLine]) {
|
||||
// //remove it
|
||||
// const index = diffAreas.findIndex(da => da === diffArea)
|
||||
// diffAreas.splice(index, 1)
|
||||
// }
|
||||
|
||||
// // TODO handle other cases where eg. the change overlaps many diffAreas
|
||||
// }
|
||||
|
||||
|
||||
// if a diffArea is below the last character of the change, shift the diffArea up/down by the delta amount of newlines
|
||||
for (const diffArea of diffAreas) {
|
||||
if (diffArea[startLine] > change.endLine) {
|
||||
diffArea[startLine] += deltaNewlines
|
||||
diffArea[endLine] += deltaNewlines
|
||||
}
|
||||
}
|
||||
// // if a diffArea is below the last character of the change, shift the diffArea up/down by the delta amount of newlines
|
||||
// for (const diffArea of diffAreas) {
|
||||
// if (diffArea[startLine] > change.endLine) {
|
||||
// diffArea[startLine] += deltaNewlines
|
||||
// diffArea[endLine] += deltaNewlines
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO merge any diffAreas if they overlap with each other as a result from the shift
|
||||
// // TODO merge any diffAreas if they overlap with each other as a result from the shift
|
||||
|
||||
}
|
||||
}
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// used by us only
|
||||
// refreshes all the diffs inside each diff area, and refreshes the styles
|
||||
public refreshStylesAndDiffs(docUriStr: string) {
|
||||
// // used by us only
|
||||
// // refreshes all the diffs inside each diff area, and refreshes the styles
|
||||
// public refreshStylesAndDiffs(docUriStr: string) {
|
||||
|
||||
const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
|
||||
if (!editor) {
|
||||
console.log('Error: No active editor!')
|
||||
return;
|
||||
}
|
||||
const originalFile = this._originalFileOfDocument[docUriStr]
|
||||
if (!originalFile) {
|
||||
console.log('Error: No original file!')
|
||||
return;
|
||||
}
|
||||
// const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
|
||||
// if (!editor) {
|
||||
// console.log('Error: No active editor!')
|
||||
// return;
|
||||
// }
|
||||
// const originalFile = this._originalFileOfDocument[docUriStr]
|
||||
// if (!originalFile) {
|
||||
// console.log('Error: No original file!')
|
||||
// return;
|
||||
// }
|
||||
|
||||
const diffAreas = this._diffAreasOfDocument[docUriStr] || []
|
||||
|
||||
// reset all diffs (we update them below)
|
||||
this._diffsOfDocument[docUriStr] = []
|
||||
|
||||
// TODO!!!!
|
||||
// vscode.languages.clearInlineDiffs(editor)
|
||||
|
||||
// for each diffArea
|
||||
for (const diffArea of diffAreas) {
|
||||
|
||||
// get code inside of diffArea
|
||||
const originalCode = originalFile.split('\n').slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
const currentCode = editor.document.getText(new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)).replace(/\r\n/g, '\n')
|
||||
|
||||
// compute the diffs
|
||||
const diffs = findDiffs(originalCode, currentCode)
|
||||
|
||||
// add the diffs to `this._diffsOfDocument[docUriStr]`
|
||||
// const diffAreas = this._diffAreasOfDocument[docUriStr] || []
|
||||
|
||||
// // reset all diffs (we update them below)
|
||||
// this._diffsOfDocument[docUriStr] = []
|
||||
|
||||
// // TODO!!!!
|
||||
// // vscode.languages.clearInlineDiffs(editor)
|
||||
|
||||
// // for each diffArea
|
||||
// for (const diffArea of diffAreas) {
|
||||
|
||||
// // get code inside of diffArea
|
||||
// const originalCode = originalFile.split('\n').slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
// const currentCode = editor.document.getText(new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)).replace(/\r\n/g, '\n')
|
||||
|
||||
// // compute the diffs
|
||||
// const diffs = findDiffs(originalCode, currentCode)
|
||||
|
||||
// // add the diffs to `this._diffsOfDocument[docUriStr]`
|
||||
|
||||
// if no diffs, set diffs to []
|
||||
if (!this._diffsOfDocument[docUriStr])
|
||||
this._diffsOfDocument[docUriStr] = []
|
||||
// // if no diffs, set diffs to []
|
||||
// if (!this._diffsOfDocument[docUriStr])
|
||||
// this._diffsOfDocument[docUriStr] = []
|
||||
|
||||
// add each diff and its codelens to the document
|
||||
for (let i = diffs.length - 1; i > -1; i -= 1) {
|
||||
let suggestedDiff = diffs[i]
|
||||
// // add each diff and its codelens to the document
|
||||
// for (let i = diffs.length - 1; i > -1; i -= 1) {
|
||||
// let suggestedDiff = diffs[i]
|
||||
|
||||
this._diffsOfDocument[docUriStr].push({
|
||||
...suggestedDiff,
|
||||
diffid: this._diffidPool,
|
||||
// originalCode: suggestedDiff.deletedText,
|
||||
lenses: [
|
||||
new vscode.CodeLens(suggestedDiff.range, { title: 'Accept', command: 'void.acceptDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] }),
|
||||
new vscode.CodeLens(suggestedDiff.range, { title: 'Reject', command: 'void.rejectDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] })
|
||||
]
|
||||
});
|
||||
vscode.languages.addInlineDiff(editor, suggestedDiff.originalCode, suggestedDiff.range)
|
||||
this._diffidPool += 1
|
||||
}
|
||||
// this._diffsOfDocument[docUriStr].push({
|
||||
// ...suggestedDiff,
|
||||
// diffid: this._diffidPool,
|
||||
// // originalCode: suggestedDiff.deletedText,
|
||||
// lenses: [
|
||||
// new vscode.CodeLens(suggestedDiff.range, { title: 'Accept', command: 'void.acceptDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] }),
|
||||
// new vscode.CodeLens(suggestedDiff.range, { title: 'Reject', command: 'void.rejectDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] })
|
||||
// ]
|
||||
// });
|
||||
// vscode.languages.addInlineDiff(editor, suggestedDiff.originalCode, suggestedDiff.range)
|
||||
// this._diffidPool += 1
|
||||
// }
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
// for each diffArea, highlight its sweepIndex in dark gray
|
||||
editor.setDecorations(
|
||||
darkGrayDecoration,
|
||||
(this._diffAreasOfDocument[docUriStr]
|
||||
.filter(diffArea => diffArea.sweepIndex !== null)
|
||||
.map(diffArea => {
|
||||
let s = diffArea.sweepIndex!
|
||||
return new vscode.Range(s, 0, s, 0)
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
// for each diffArea, highlight sweepIndex+1...end in light gray
|
||||
editor.setDecorations(
|
||||
lightGrayDecoration,
|
||||
(this._diffAreasOfDocument[docUriStr]
|
||||
.filter(diffArea => diffArea.sweepIndex !== null)
|
||||
.map(diffArea => {
|
||||
return new vscode.Range(diffArea.sweepIndex! + 1, 0, diffArea.endLine, 0)
|
||||
})
|
||||
)
|
||||
)
|
||||
// // for each diffArea, highlight its sweepIndex in dark gray
|
||||
// editor.setDecorations(
|
||||
// darkGrayDecoration,
|
||||
// (this._diffAreasOfDocument[docUriStr]
|
||||
// .filter(diffArea => diffArea.sweepIndex !== null)
|
||||
// .map(diffArea => {
|
||||
// let s = diffArea.sweepIndex!
|
||||
// return new vscode.Range(s, 0, s, 0)
|
||||
// })
|
||||
// )
|
||||
// )
|
||||
|
||||
// // for each diffArea, highlight sweepIndex+1...end in light gray
|
||||
// editor.setDecorations(
|
||||
// lightGrayDecoration,
|
||||
// (this._diffAreasOfDocument[docUriStr]
|
||||
// .filter(diffArea => diffArea.sweepIndex !== null)
|
||||
// .map(diffArea => {
|
||||
// return new vscode.Range(diffArea.sweepIndex! + 1, 0, diffArea.endLine, 0)
|
||||
// })
|
||||
// )
|
||||
// )
|
||||
|
||||
|
||||
// update code lenses
|
||||
this._onDidChangeCodeLenses.fire()
|
||||
// // update code lenses
|
||||
// this._onDidChangeCodeLenses.fire()
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
// called on void.acceptDiff
|
||||
public async acceptDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor)
|
||||
return
|
||||
// // called on void.acceptDiff
|
||||
// public async acceptDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
|
||||
// const editor = vscode.window.activeTextEditor
|
||||
// if (!editor)
|
||||
// return
|
||||
|
||||
const docUriStr = editor.document.uri.toString()
|
||||
// const docUriStr = editor.document.uri.toString()
|
||||
|
||||
const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
|
||||
if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
|
||||
// const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
|
||||
// if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
|
||||
|
||||
const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
|
||||
if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
|
||||
// const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
|
||||
// if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
|
||||
|
||||
const diff = this._diffsOfDocument[docUriStr][diffIdx]
|
||||
const originalFile = this._originalFileOfDocument[docUriStr]
|
||||
const currentFile = await readFileContentOfUri(editor.document.uri)
|
||||
|
||||
// Fixed: Handle newlines properly by splitting into lines and joining with proper newlines
|
||||
const originalLines = originalFile.split('\n');
|
||||
const currentLines = currentFile.split('\n');
|
||||
|
||||
// Get the changed lines from current file
|
||||
const changedLines = currentLines.slice(diff.range.start.line, diff.range.end.line + 1);
|
||||
|
||||
// Create new original file content by replacing the affected lines
|
||||
const newOriginalLines = [
|
||||
...originalLines.slice(0, diff.originalRange.start.line),
|
||||
...changedLines,
|
||||
...originalLines.slice(diff.originalRange.end.line + 1)
|
||||
];
|
||||
|
||||
this._originalFileOfDocument[docUriStr] = newOriginalLines.join('\n');
|
||||
|
||||
// Update diff areas based on the change
|
||||
this.resizeDiffAreas(docUriStr, [{
|
||||
text: changedLines.join('\n'),
|
||||
startLine: diff.originalRange.start.line,
|
||||
endLine: diff.originalRange.end.line
|
||||
}], 'originalFile')
|
||||
|
||||
// Check if diffArea should be removed
|
||||
|
||||
const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
|
||||
|
||||
const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
|
||||
const originalArea = newOriginalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
|
||||
if (originalArea === currentArea) {
|
||||
const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
|
||||
this._diffAreasOfDocument[docUriStr].splice(index, 1)
|
||||
}
|
||||
|
||||
this.refreshStylesAndDiffs(docUriStr)
|
||||
}
|
||||
|
||||
// called on void.rejectDiff
|
||||
public async rejectDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor)
|
||||
return
|
||||
|
||||
const docUriStr = editor.document.uri.toString()
|
||||
|
||||
const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
|
||||
if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
|
||||
|
||||
const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
|
||||
if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
|
||||
|
||||
const diff = this._diffsOfDocument[docUriStr][diffIdx]
|
||||
|
||||
// Apply the rejection by replacing with original code
|
||||
// we don't have to edit the original or final file; just do a workspace edit so the code equals the original code
|
||||
const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
workspaceEdit.replace(editor.document.uri, diff.range, diff.originalCode)
|
||||
await vscode.workspace.applyEdit(workspaceEdit)
|
||||
|
||||
// Check if diffArea should be removed
|
||||
const originalFile = this._originalFileOfDocument[docUriStr]
|
||||
const currentFile = await readFileContentOfUri(editor.document.uri)
|
||||
const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
|
||||
const currentLines = currentFile.split('\n');
|
||||
const originalLines = originalFile.split('\n');
|
||||
|
||||
const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
|
||||
const originalArea = originalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
|
||||
if (originalArea === currentArea) {
|
||||
const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
|
||||
this._diffAreasOfDocument[docUriStr].splice(index, 1)
|
||||
}
|
||||
|
||||
this.refreshStylesAndDiffs(docUriStr)
|
||||
}
|
||||
|
||||
async startStreamingInDiffArea({ docUri, oldFileStr, diffRepr, diffArea, voidConfig, abortRef }: { docUri: vscode.Uri, oldFileStr: string, diffRepr: string, voidConfig: VoidConfig, diffArea: DiffArea, abortRef: AbortRef }) {
|
||||
|
||||
|
||||
const promptContent = `\
|
||||
ORIGINAL_FILE
|
||||
\`\`\`
|
||||
${oldFileStr}
|
||||
\`\`\`
|
||||
|
||||
DIFF
|
||||
\`\`\`
|
||||
${diffRepr}
|
||||
\`\`\`
|
||||
|
||||
INSTRUCTIONS
|
||||
Please finish writing the new file by applying the diff to the original file. Return ONLY the completion of the file, without any explanation.
|
||||
|
||||
`
|
||||
// make LLM complete the file to include the diff
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
sendLLMMessage({
|
||||
logging: { loggingName: 'streamChunk' },
|
||||
messages: [
|
||||
{ role: 'system', content: writeFileWithDiffInstructions, },
|
||||
// TODO include more context too
|
||||
{ role: 'user', content: promptContent, }
|
||||
],
|
||||
onText: (newText, fullText) => {
|
||||
this._updateStream(docUri.toString(), diffArea, fullText)
|
||||
},
|
||||
onFinalMessage: (fullText) => {
|
||||
this._updateStream(docUri.toString(), diffArea, fullText)
|
||||
resolve();
|
||||
},
|
||||
onError: (e) => {
|
||||
console.error('Error rewriting file with diff', e);
|
||||
resolve();
|
||||
},
|
||||
voidConfig,
|
||||
abortRef,
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
// used by us only
|
||||
private _updateStream = throttle(async (docUriStr: string, diffArea: DiffArea, newDiffAreaCode: string) => {
|
||||
|
||||
const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
|
||||
if (!editor) {
|
||||
console.log('Error: No active editor!')
|
||||
return;
|
||||
}
|
||||
|
||||
// original code all diffs are based on in the code
|
||||
const originalDiffAreaCode = (this._originalFileOfDocument[docUriStr] || '').split('\n').slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
|
||||
// figure out where to highlight based on where the AI is in the stream right now, use the last diff in findDiffs to figure that out
|
||||
const diffs = findDiffs(originalDiffAreaCode, newDiffAreaCode)
|
||||
const lastDiff = diffs?.[diffs.length - 1] ?? null
|
||||
|
||||
// these are two different coordinate systems - new and old line number
|
||||
let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted
|
||||
let oldFileStartLine: number // get original[oldStartingPoint...]
|
||||
|
||||
if (!lastDiff) {
|
||||
// if the writing is identical so far, display no changes
|
||||
newFileEndLine = 0
|
||||
oldFileStartLine = 0
|
||||
}
|
||||
else {
|
||||
if (lastDiff.type === 'insertion') {
|
||||
newFileEndLine = lastDiff.range.end.line
|
||||
oldFileStartLine = lastDiff.originalRange.start.line
|
||||
}
|
||||
else if (lastDiff.type === 'deletion') {
|
||||
newFileEndLine = lastDiff.range.start.line
|
||||
oldFileStartLine = lastDiff.originalRange.start.line
|
||||
}
|
||||
else if (lastDiff.type === 'edit') {
|
||||
newFileEndLine = lastDiff.range.end.line
|
||||
oldFileStartLine = lastDiff.originalRange.start.line
|
||||
}
|
||||
else {
|
||||
throw new Error(`updateStream: diff.type not recognized: ${lastDiff.type}`)
|
||||
}
|
||||
}
|
||||
|
||||
// display
|
||||
const newFileTop = newDiffAreaCode.split('\n').slice(0, newFileEndLine + 1).join('\n')
|
||||
const oldFileBottom = originalDiffAreaCode.split('\n').slice(oldFileStartLine + 1, Infinity).join('\n')
|
||||
|
||||
let newCode = `${newFileTop}\n${oldFileBottom}`
|
||||
diffArea.sweepIndex = newFileEndLine
|
||||
// replace oldDACode with newDACode with a vscode edit
|
||||
|
||||
const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
|
||||
const diffareaRange = new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)
|
||||
workspaceEdit.replace(editor.document.uri, diffareaRange, newCode)
|
||||
await vscode.workspace.applyEdit(workspaceEdit)
|
||||
}, THROTTLE_TIME)
|
||||
|
||||
}
|
||||
// const diff = this._diffsOfDocument[docUriStr][diffIdx]
|
||||
// const originalFile = this._originalFileOfDocument[docUriStr]
|
||||
// const currentFile = await readFileContentOfUri(editor.document.uri)
|
||||
|
||||
// // Fixed: Handle newlines properly by splitting into lines and joining with proper newlines
|
||||
// const originalLines = originalFile.split('\n');
|
||||
// const currentLines = currentFile.split('\n');
|
||||
|
||||
// // Get the changed lines from current file
|
||||
// const changedLines = currentLines.slice(diff.range.start.line, diff.range.end.line + 1);
|
||||
|
||||
// // Create new original file content by replacing the affected lines
|
||||
// const newOriginalLines = [
|
||||
// ...originalLines.slice(0, diff.originalRange.start.line),
|
||||
// ...changedLines,
|
||||
// ...originalLines.slice(diff.originalRange.end.line + 1)
|
||||
// ];
|
||||
|
||||
// this._originalFileOfDocument[docUriStr] = newOriginalLines.join('\n');
|
||||
|
||||
// // Update diff areas based on the change
|
||||
// this.resizeDiffAreas(docUriStr, [{
|
||||
// text: changedLines.join('\n'),
|
||||
// startLine: diff.originalRange.start.line,
|
||||
// endLine: diff.originalRange.end.line
|
||||
// }], 'originalFile')
|
||||
|
||||
// // Check if diffArea should be removed
|
||||
|
||||
// const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
|
||||
|
||||
// const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
|
||||
// const originalArea = newOriginalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
|
||||
// if (originalArea === currentArea) {
|
||||
// const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
|
||||
// this._diffAreasOfDocument[docUriStr].splice(index, 1)
|
||||
// }
|
||||
|
||||
// this.refreshStylesAndDiffs(docUriStr)
|
||||
// }
|
||||
|
||||
// // called on void.rejectDiff
|
||||
// public async rejectDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
|
||||
// const editor = vscode.window.activeTextEditor
|
||||
// if (!editor)
|
||||
// return
|
||||
|
||||
// const docUriStr = editor.document.uri.toString()
|
||||
|
||||
// const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
|
||||
// if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
|
||||
|
||||
// const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
|
||||
// if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
|
||||
|
||||
// const diff = this._diffsOfDocument[docUriStr][diffIdx]
|
||||
|
||||
// // Apply the rejection by replacing with original code
|
||||
// // we don't have to edit the original or final file; just do a workspace edit so the code equals the original code
|
||||
// const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
// workspaceEdit.replace(editor.document.uri, diff.range, diff.originalCode)
|
||||
// await vscode.workspace.applyEdit(workspaceEdit)
|
||||
|
||||
// // Check if diffArea should be removed
|
||||
// const originalFile = this._originalFileOfDocument[docUriStr]
|
||||
// const currentFile = await readFileContentOfUri(editor.document.uri)
|
||||
// const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
|
||||
// const currentLines = currentFile.split('\n');
|
||||
// const originalLines = originalFile.split('\n');
|
||||
|
||||
// const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
|
||||
// const originalArea = originalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
|
||||
// if (originalArea === currentArea) {
|
||||
// const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
|
||||
// this._diffAreasOfDocument[docUriStr].splice(index, 1)
|
||||
// }
|
||||
|
||||
// this.refreshStylesAndDiffs(docUriStr)
|
||||
// }
|
||||
|
||||
// async startStreamingInDiffArea({ docUri, oldFileStr, diffRepr, diffArea, voidConfig, abortRef }: { docUri: vscode.Uri, oldFileStr: string, diffRepr: string, voidConfig: VoidConfig, diffArea: DiffArea, abortRef: AbortRef }) {
|
||||
|
||||
|
||||
// const promptContent = `\
|
||||
// ORIGINAL_FILE
|
||||
// \`\`\`
|
||||
// ${oldFileStr}
|
||||
// \`\`\`
|
||||
|
||||
// DIFF
|
||||
// \`\`\`
|
||||
// ${diffRepr}
|
||||
// \`\`\`
|
||||
|
||||
// INSTRUCTIONS
|
||||
// Please finish writing the new file by applying the diff to the original file. Return ONLY the completion of the file, without any explanation.
|
||||
|
||||
// `
|
||||
// // make LLM complete the file to include the diff
|
||||
// await new Promise<void>((resolve, reject) => {
|
||||
// sendLLMMessage({
|
||||
// logging: { loggingName: 'streamChunk' },
|
||||
// messages: [
|
||||
// { role: 'system', content: writeFileWithDiffInstructions, },
|
||||
// // TODO include more context too
|
||||
// { role: 'user', content: promptContent, }
|
||||
// ],
|
||||
// onText: (newText, fullText) => {
|
||||
// this._updateStream(docUri.toString(), diffArea, fullText)
|
||||
// },
|
||||
// onFinalMessage: (fullText) => {
|
||||
// this._updateStream(docUri.toString(), diffArea, fullText)
|
||||
// resolve();
|
||||
// },
|
||||
// onError: (e) => {
|
||||
// console.error('Error rewriting file with diff', e);
|
||||
// resolve();
|
||||
// },
|
||||
// voidConfig,
|
||||
// abortRef,
|
||||
// })
|
||||
// })
|
||||
|
||||
// }
|
||||
|
||||
|
||||
// // used by us only
|
||||
// private _updateStream = throttle(async (docUriStr: string, diffArea: DiffArea, newDiffAreaCode: string) => {
|
||||
|
||||
// const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
|
||||
// if (!editor) {
|
||||
// console.log('Error: No active editor!')
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // original code all diffs are based on in the code
|
||||
// const originalDiffAreaCode = (this._originalFileOfDocument[docUriStr] || '').split('\n').slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
|
||||
|
||||
// // figure out where to highlight based on where the AI is in the stream right now, use the last diff in findDiffs to figure that out
|
||||
// const diffs = findDiffs(originalDiffAreaCode, newDiffAreaCode)
|
||||
// const lastDiff = diffs?.[diffs.length - 1] ?? null
|
||||
|
||||
// // these are two different coordinate systems - new and old line number
|
||||
// let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted
|
||||
// let oldFileStartLine: number // get original[oldStartingPoint...]
|
||||
|
||||
// if (!lastDiff) {
|
||||
// // if the writing is identical so far, display no changes
|
||||
// newFileEndLine = 0
|
||||
// oldFileStartLine = 0
|
||||
// }
|
||||
// else {
|
||||
// if (lastDiff.type === 'insertion') {
|
||||
// newFileEndLine = lastDiff.range.end.line
|
||||
// oldFileStartLine = lastDiff.originalRange.start.line
|
||||
// }
|
||||
// else if (lastDiff.type === 'deletion') {
|
||||
// newFileEndLine = lastDiff.range.start.line
|
||||
// oldFileStartLine = lastDiff.originalRange.start.line
|
||||
// }
|
||||
// else if (lastDiff.type === 'edit') {
|
||||
// newFileEndLine = lastDiff.range.end.line
|
||||
// oldFileStartLine = lastDiff.originalRange.start.line
|
||||
// }
|
||||
// else {
|
||||
// throw new Error(`updateStream: diff.type not recognized: ${lastDiff.type}`)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // display
|
||||
// const newFileTop = newDiffAreaCode.split('\n').slice(0, newFileEndLine + 1).join('\n')
|
||||
// const oldFileBottom = originalDiffAreaCode.split('\n').slice(oldFileStartLine + 1, Infinity).join('\n')
|
||||
|
||||
// let newCode = `${newFileTop}\n${oldFileBottom}`
|
||||
// diffArea.sweepIndex = newFileEndLine
|
||||
// // replace oldDACode with newDACode with a vscode edit
|
||||
|
||||
// const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
|
||||
// const diffareaRange = new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)
|
||||
// workspaceEdit.replace(editor.document.uri, diffareaRange, newCode)
|
||||
// await vscode.workspace.applyEdit(workspaceEdit)
|
||||
// }, THROTTLE_TIME)
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,184 +1,184 @@
|
|||
import * as vscode from 'vscode';
|
||||
// import * as vscode from 'vscode';
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { AbortRef } from '../common/sendLLMMessage';
|
||||
import { MessageToSidebar, MessageFromSidebar, DiffArea, ChatThreads } from '../common/shared_types';
|
||||
import { getVoidConfigFromPartial } from '../webviews/common/contextForConfig';
|
||||
import { DiffProvider } from '../../DiffProvider';
|
||||
import { readFileContentOfUri } from './extensionLib/readFileContentOfUri';
|
||||
import { SidebarWebviewProvider } from '../sidebar/SidebarWebviewProvider';
|
||||
import { CtrlKWebviewProvider } from './providers/CtrlKWebviewProvider';
|
||||
// import { v4 as uuidv4 } from 'uuid'
|
||||
// import { AbortRef } from '../common/sendLLMMessage';
|
||||
// import { MessageToSidebar, MessageFromSidebar, DiffArea, ChatThreads } from '../common/shared_types';
|
||||
// import { getVoidConfigFromPartial } from '../webviews/common/contextForConfig';
|
||||
// import { DiffProvider } from '../../DiffProvider';
|
||||
// import { readFileContentOfUri } from './extensionLib/readFileContentOfUri';
|
||||
// import { SidebarWebviewProvider } from '../sidebar/SidebarWebviewProvider';
|
||||
// import { CtrlKWebviewProvider } from './providers/CtrlKWebviewProvider';
|
||||
|
||||
const roundRangeToLines = (selection: vscode.Selection) => {
|
||||
let endLine = selection.end.character === 0 ? selection.end.line - 1 : selection.end.line // e.g. if the user triple clicks, it selects column=0, line=line -> column=0, line=line+1
|
||||
return new vscode.Range(selection.start.line, 0, endLine, Number.MAX_SAFE_INTEGER)
|
||||
}
|
||||
// const roundRangeToLines = (selection: vscode.Selection) => {
|
||||
// let endLine = selection.end.character === 0 ? selection.end.line - 1 : selection.end.line // e.g. if the user triple clicks, it selects column=0, line=line -> column=0, line=line+1
|
||||
// return new vscode.Range(selection.start.line, 0, endLine, Number.MAX_SAFE_INTEGER)
|
||||
// }
|
||||
|
||||
const getSelection = (editor: vscode.TextEditor) => {
|
||||
// get the range of the selection and the file the user is in
|
||||
const selectionRange = roundRangeToLines(editor.selection);
|
||||
const selectionStr = editor.document.getText(selectionRange).trim();
|
||||
const filePath = editor.document.uri;
|
||||
return { selectionStr, filePath }
|
||||
}
|
||||
// const getSelection = (editor: vscode.TextEditor) => {
|
||||
// // get the range of the selection and the file the user is in
|
||||
// const selectionRange = roundRangeToLines(editor.selection);
|
||||
// const selectionStr = editor.document.getText(selectionRange).trim();
|
||||
// const filePath = editor.document.uri;
|
||||
// return { selectionStr, filePath }
|
||||
// }
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
// export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
// 1. Mount the chat sidebar
|
||||
const sidebarWebviewProvider = new SidebarWebviewProvider(context);
|
||||
context.subscriptions.push(
|
||||
vscode.window.registerWebviewViewProvider(SidebarWebviewProvider.viewId, sidebarWebviewProvider, { webviewOptions: { retainContextWhenHidden: true } })
|
||||
);
|
||||
// // 1. Mount the chat sidebar
|
||||
// const sidebarWebviewProvider = new SidebarWebviewProvider(context);
|
||||
// context.subscriptions.push(
|
||||
// vscode.window.registerWebviewViewProvider(SidebarWebviewProvider.viewId, sidebarWebviewProvider, { webviewOptions: { retainContextWhenHidden: true } })
|
||||
// );
|
||||
|
||||
// 1.5
|
||||
const ctrlKWebviewProvider = new CtrlKWebviewProvider(context)
|
||||
// // 1.5
|
||||
// const ctrlKWebviewProvider = new CtrlKWebviewProvider(context)
|
||||
|
||||
|
||||
// 2. ctrl+l
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('void.ctrl+l', () => {
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor) return
|
||||
// // 2. ctrl+l
|
||||
// context.subscriptions.push(
|
||||
// vscode.commands.registerCommand('void.ctrl+l', () => {
|
||||
// const editor = vscode.window.activeTextEditor
|
||||
// if (!editor) return
|
||||
|
||||
// show the sidebar
|
||||
vscode.commands.executeCommand('workbench.view.extension.voidViewContainer');
|
||||
// vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar
|
||||
// // show the sidebar
|
||||
// vscode.commands.executeCommand('workbench.view.extension.voidViewContainer');
|
||||
// // vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar
|
||||
|
||||
const { selectionStr, filePath } = getSelection(editor)
|
||||
// const { selectionStr, filePath } = getSelection(editor)
|
||||
|
||||
// send message to the webview (Sidebar.tsx)
|
||||
sidebarWebviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+l', selection: { selectionStr, filePath } } satisfies MessageToSidebar));
|
||||
})
|
||||
);
|
||||
// // send message to the webview (Sidebar.tsx)
|
||||
// sidebarWebviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+l', selection: { selectionStr, filePath } } satisfies MessageToSidebar));
|
||||
// })
|
||||
// );
|
||||
|
||||
// 2.5: ctrl+k
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('void.ctrl+k', () => {
|
||||
console.log('CTRLK PRESSED')
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor) return
|
||||
// // 2.5: ctrl+k
|
||||
// context.subscriptions.push(
|
||||
// vscode.commands.registerCommand('void.ctrl+k', () => {
|
||||
// console.log('CTRLK PRESSED')
|
||||
// const editor = vscode.window.activeTextEditor
|
||||
// if (!editor) return
|
||||
|
||||
const { selectionStr, filePath } = getSelection(editor)
|
||||
// const { selectionStr, filePath } = getSelection(editor)
|
||||
|
||||
// send message to the webview (Sidebar.tsx)
|
||||
// ctrlKWebviewProvider.onPressCtrlK()
|
||||
// sidebarWebviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+k', selection: { selectionStr, filePath } } satisfies MessageToSidebar));
|
||||
})
|
||||
);
|
||||
// // send message to the webview (Sidebar.tsx)
|
||||
// // ctrlKWebviewProvider.onPressCtrlK()
|
||||
// // sidebarWebviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+k', selection: { selectionStr, filePath } } satisfies MessageToSidebar));
|
||||
// })
|
||||
// );
|
||||
|
||||
// 3. Show an approve/reject codelens above each change
|
||||
const diffProvider = new DiffProvider(context);
|
||||
context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', diffProvider));
|
||||
// // 3. Show an approve/reject codelens above each change
|
||||
// const diffProvider = new DiffProvider(context);
|
||||
// context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', diffProvider));
|
||||
|
||||
// 4. Add approve/reject commands
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.acceptDiff', async (params) => {
|
||||
diffProvider.acceptDiff(params)
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.rejectDiff', async (params) => {
|
||||
diffProvider.rejectDiff(params)
|
||||
}));
|
||||
// // 4. Add approve/reject commands
|
||||
// context.subscriptions.push(vscode.commands.registerCommand('void.acceptDiff', async (params) => {
|
||||
// diffProvider.acceptDiff(params)
|
||||
// }));
|
||||
// context.subscriptions.push(vscode.commands.registerCommand('void.rejectDiff', async (params) => {
|
||||
// diffProvider.rejectDiff(params)
|
||||
// }));
|
||||
|
||||
// 5. Receive messages from sidebar
|
||||
sidebarWebviewProvider.webview.then(
|
||||
webview => {
|
||||
// // 5. Receive messages from sidebar
|
||||
// sidebarWebviewProvider.webview.then(
|
||||
// webview => {
|
||||
|
||||
// top navigation bar commands
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.startNewThread', async () => {
|
||||
webview.postMessage({ type: 'startNewThread' } satisfies MessageToSidebar)
|
||||
}))
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.toggleThreadSelector', async () => {
|
||||
webview.postMessage({ type: 'toggleThreadSelector' } satisfies MessageToSidebar)
|
||||
}))
|
||||
context.subscriptions.push(vscode.commands.registerCommand('void.toggleSettings', async () => {
|
||||
webview.postMessage({ type: 'toggleSettings' } satisfies MessageToSidebar)
|
||||
}));
|
||||
// // top navigation bar commands
|
||||
// context.subscriptions.push(vscode.commands.registerCommand('void.startNewThread', async () => {
|
||||
// webview.postMessage({ type: 'startNewThread' } satisfies MessageToSidebar)
|
||||
// }))
|
||||
// context.subscriptions.push(vscode.commands.registerCommand('void.toggleThreadSelector', async () => {
|
||||
// webview.postMessage({ type: 'toggleThreadSelector' } satisfies MessageToSidebar)
|
||||
// }))
|
||||
// context.subscriptions.push(vscode.commands.registerCommand('void.toggleSettings', async () => {
|
||||
// webview.postMessage({ type: 'toggleSettings' } satisfies MessageToSidebar)
|
||||
// }));
|
||||
|
||||
// Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`)
|
||||
webview.onDidReceiveMessage(async (m: MessageFromSidebar) => {
|
||||
// // Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`)
|
||||
// webview.onDidReceiveMessage(async (m: MessageFromSidebar) => {
|
||||
|
||||
const abortApplyRef: AbortRef = { current: null }
|
||||
// const abortApplyRef: AbortRef = { current: null }
|
||||
|
||||
if (m.type === 'requestFiles') {
|
||||
// if (m.type === 'requestFiles') {
|
||||
|
||||
// get contents of all file paths
|
||||
const files = await Promise.all(
|
||||
m.filepaths.map(async (filepath) => ({ filepath, content: await readFileContentOfUri(filepath) }))
|
||||
)
|
||||
// // get contents of all file paths
|
||||
// const files = await Promise.all(
|
||||
// m.filepaths.map(async (filepath) => ({ filepath, content: await readFileContentOfUri(filepath) }))
|
||||
// )
|
||||
|
||||
// send contents to webview
|
||||
webview.postMessage({ type: 'files', files, } satisfies MessageToSidebar)
|
||||
// // send contents to webview
|
||||
// webview.postMessage({ type: 'files', files, } satisfies MessageToSidebar)
|
||||
|
||||
}
|
||||
else if (m.type === 'applyChanges') {
|
||||
// }
|
||||
// else if (m.type === 'applyChanges') {
|
||||
|
||||
const editor = vscode.window.activeTextEditor
|
||||
if (!editor) {
|
||||
vscode.window.showInformationMessage('No active editor!')
|
||||
return
|
||||
}
|
||||
// create an area to show diffs
|
||||
const partialDiffArea: Omit<DiffArea, 'diffareaid'> = {
|
||||
startLine: 0, // in ctrl+L the start and end lines are the full document
|
||||
endLine: editor.document.lineCount,
|
||||
originalStartLine: 0,
|
||||
originalEndLine: editor.document.lineCount,
|
||||
sweepIndex: null,
|
||||
}
|
||||
const diffArea = diffProvider.createDiffArea(editor.document.uri, partialDiffArea, await readFileContentOfUri(editor.document.uri))
|
||||
// const editor = vscode.window.activeTextEditor
|
||||
// if (!editor) {
|
||||
// vscode.window.showInformationMessage('No active editor!')
|
||||
// return
|
||||
// }
|
||||
// // create an area to show diffs
|
||||
// const partialDiffArea: Omit<DiffArea, 'diffareaid'> = {
|
||||
// startLine: 0, // in ctrl+L the start and end lines are the full document
|
||||
// endLine: editor.document.lineCount,
|
||||
// originalStartLine: 0,
|
||||
// originalEndLine: editor.document.lineCount,
|
||||
// sweepIndex: null,
|
||||
// }
|
||||
// const diffArea = diffProvider.createDiffArea(editor.document.uri, partialDiffArea, await readFileContentOfUri(editor.document.uri))
|
||||
|
||||
const docUri = editor.document.uri
|
||||
const fileStr = await readFileContentOfUri(docUri)
|
||||
const voidConfig = getVoidConfigFromPartial(context.globalState.get('partialVoidConfig') ?? {})
|
||||
// const docUri = editor.document.uri
|
||||
// const fileStr = await readFileContentOfUri(docUri)
|
||||
// const voidConfig = getVoidConfigFromPartial(context.globalState.get('partialVoidConfig') ?? {})
|
||||
|
||||
await diffProvider.startStreamingInDiffArea({ docUri, oldFileStr: fileStr, diffRepr: m.diffRepr, voidConfig, diffArea, abortRef: abortApplyRef })
|
||||
}
|
||||
else if (m.type === 'getPartialVoidConfig') {
|
||||
const partialVoidConfig = context.globalState.get('partialVoidConfig') ?? {}
|
||||
webview.postMessage({ type: 'partialVoidConfig', partialVoidConfig } satisfies MessageToSidebar)
|
||||
}
|
||||
else if (m.type === 'persistPartialVoidConfig') {
|
||||
const partialVoidConfig = m.partialVoidConfig
|
||||
context.globalState.update('partialVoidConfig', partialVoidConfig)
|
||||
}
|
||||
else if (m.type === 'getAllThreads') {
|
||||
const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {}
|
||||
webview.postMessage({ type: 'allThreads', threads } satisfies MessageToSidebar)
|
||||
}
|
||||
else if (m.type === 'persistThread') {
|
||||
const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {}
|
||||
const updatedThreads: ChatThreads = { ...threads, [m.thread.id]: m.thread }
|
||||
context.workspaceState.update('allThreads', updatedThreads)
|
||||
}
|
||||
else if (m.type === 'getDeviceId') {
|
||||
let deviceId = context.globalState.get('void_deviceid')
|
||||
if (!deviceId || typeof deviceId !== 'string') {
|
||||
deviceId = uuidv4()
|
||||
context.globalState.update('void_deviceid', deviceId)
|
||||
}
|
||||
webview.postMessage({ type: 'deviceId', deviceId: deviceId as string } satisfies MessageToSidebar)
|
||||
}
|
||||
else {
|
||||
console.error('unrecognized command', m)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
// await diffProvider.startStreamingInDiffArea({ docUri, oldFileStr: fileStr, diffRepr: m.diffRepr, voidConfig, diffArea, abortRef: abortApplyRef })
|
||||
// }
|
||||
// else if (m.type === 'getPartialVoidConfig') {
|
||||
// const partialVoidConfig = context.globalState.get('partialVoidConfig') ?? {}
|
||||
// webview.postMessage({ type: 'partialVoidConfig', partialVoidConfig } satisfies MessageToSidebar)
|
||||
// }
|
||||
// else if (m.type === 'persistPartialVoidConfig') {
|
||||
// const partialVoidConfig = m.partialVoidConfig
|
||||
// context.globalState.update('partialVoidConfig', partialVoidConfig)
|
||||
// }
|
||||
// else if (m.type === 'getAllThreads') {
|
||||
// const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {}
|
||||
// webview.postMessage({ type: 'allThreads', threads } satisfies MessageToSidebar)
|
||||
// }
|
||||
// else if (m.type === 'persistThread') {
|
||||
// const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {}
|
||||
// const updatedThreads: ChatThreads = { ...threads, [m.thread.id]: m.thread }
|
||||
// context.workspaceState.update('allThreads', updatedThreads)
|
||||
// }
|
||||
// else if (m.type === 'getDeviceId') {
|
||||
// let deviceId = context.globalState.get('void_deviceid')
|
||||
// if (!deviceId || typeof deviceId !== 'string') {
|
||||
// deviceId = uuidv4()
|
||||
// context.globalState.update('void_deviceid', deviceId)
|
||||
// }
|
||||
// webview.postMessage({ type: 'deviceId', deviceId: deviceId as string } satisfies MessageToSidebar)
|
||||
// }
|
||||
// else {
|
||||
// console.error('unrecognized command', m)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// )
|
||||
|
||||
|
||||
|
||||
|
||||
// Gets called when user presses ctrl + k (mounts ctrl+k-style codelens)
|
||||
// TODO need to build this
|
||||
// const ctrlKCodeLensProvider = new CtrlKCodeLensProvider();
|
||||
// context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', ctrlKCodeLensProvider));
|
||||
// context.subscriptions.push(
|
||||
// vscode.commands.registerCommand('void.ctrl+k', () => {
|
||||
// const editor = vscode.window.activeTextEditor;
|
||||
// if (!editor)
|
||||
// return
|
||||
// ctrlKCodeLensProvider.addNewCodeLens(editor.document, editor.selection);
|
||||
// // vscode.commands.executeCommand('editor.action.showHover'); // apparently this refreshes the codelenses by having the internals call provideCodeLenses
|
||||
// })
|
||||
// )
|
||||
// // Gets called when user presses ctrl + k (mounts ctrl+k-style codelens)
|
||||
// // TODO need to build this
|
||||
// // const ctrlKCodeLensProvider = new CtrlKCodeLensProvider();
|
||||
// // context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', ctrlKCodeLensProvider));
|
||||
// // context.subscriptions.push(
|
||||
// // vscode.commands.registerCommand('void.ctrl+k', () => {
|
||||
// // const editor = vscode.window.activeTextEditor;
|
||||
// // if (!editor)
|
||||
// // return
|
||||
// // ctrlKCodeLensProvider.addNewCodeLens(editor.document, editor.selection);
|
||||
// // // vscode.commands.executeCommand('editor.action.showHover'); // apparently this refreshes the codelenses by having the internals call provideCodeLenses
|
||||
// // })
|
||||
// // )
|
||||
|
||||
}
|
||||
// }
|
||||
|
|
|
|||
395
src/vs/workbench/contrib/void/browser/misc/sendLLMMessage.ts
Normal file
395
src/vs/workbench/contrib/void/browser/misc/sendLLMMessage.ts
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
// import Anthropic from '@anthropic-ai/sdk';
|
||||
// import OpenAI from 'openai';
|
||||
// import { Ollama } from 'ollama/browser'
|
||||
// import { Content, GoogleGenerativeAI, GoogleGenerativeAIError, GoogleGenerativeAIFetchError } from '@google/generative-ai';
|
||||
// // import { VoidConfig } from '../webviews/common/contextForConfig'
|
||||
// // import { captureEvent } from '../webviews/common/posthog';
|
||||
// // import { ChatMessage } from './shared_types';
|
||||
|
||||
// type VoidConfig = any
|
||||
|
||||
// export type AbortRef = { current: (() => void) | null }
|
||||
|
||||
// export type OnText = (newText: string, fullText: string) => void
|
||||
|
||||
// export type OnFinalMessage = (input: string) => void
|
||||
|
||||
// export type LLMMessageAnthropic = {
|
||||
// role: 'user' | 'assistant';
|
||||
// content: string;
|
||||
// }
|
||||
|
||||
// export type LLMMessage = {
|
||||
// role: 'system' | 'user' | 'assistant';
|
||||
// content: string;
|
||||
// }
|
||||
|
||||
// type SendLLMMessageFnTypeInternal = (params: {
|
||||
// messages: LLMMessage[];
|
||||
// onText: OnText;
|
||||
// onFinalMessage: OnFinalMessage;
|
||||
// onError: (error: string) => void;
|
||||
// voidConfig: VoidConfig;
|
||||
|
||||
// _setAborter: (aborter: () => void) => void;
|
||||
// }) => void
|
||||
|
||||
// type SendLLMMessageFnTypeExternal = (params: {
|
||||
// messages: LLMMessage[];
|
||||
// onText: OnText;
|
||||
// onFinalMessage: (fullText: string) => void;
|
||||
// onError: (error: string) => void;
|
||||
// voidConfig: VoidConfig | null;
|
||||
// abortRef: AbortRef;
|
||||
|
||||
// logging: {
|
||||
// loggingName: string,
|
||||
// };
|
||||
// }) => void
|
||||
|
||||
// const parseMaxTokensStr = (maxTokensStr: string) => {
|
||||
// // parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN
|
||||
// const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr)
|
||||
// if (Number.isNaN(int))
|
||||
// return undefined
|
||||
// return int
|
||||
// }
|
||||
|
||||
// // Anthropic
|
||||
// const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
// const anthropic = new Anthropic({ apiKey: voidConfig.anthropic.apikey, dangerouslyAllowBrowser: true }); // defaults to process.env["ANTHROPIC_API_KEY"]
|
||||
|
||||
// // find system messages and concatenate them
|
||||
// const systemMessage = messages
|
||||
// .filter(msg => msg.role === 'system')
|
||||
// .map(msg => msg.content)
|
||||
// .join('\n');
|
||||
|
||||
// // remove system messages for Anthropic
|
||||
// const anthropicMessages = messages.filter(msg => msg.role !== 'system') as LLMMessageAnthropic[]
|
||||
|
||||
// const stream = anthropic.messages.stream({
|
||||
// system: systemMessage,
|
||||
// messages: anthropicMessages,
|
||||
// model: voidConfig.anthropic.model,
|
||||
// max_tokens: parseMaxTokensStr(voidConfig.default.maxTokens)!, // this might be undefined, but it will just throw an error for the user
|
||||
// });
|
||||
|
||||
|
||||
// // when receive text
|
||||
// stream.on('text', (newText, fullText) => {
|
||||
// onText(newText, fullText)
|
||||
// })
|
||||
|
||||
// // when we get the final message on this stream (or when error/fail)
|
||||
// stream.on('finalMessage', (claude_response) => {
|
||||
// // stringify the response's content
|
||||
// const content = claude_response.content.map(c => c.type === 'text' ? c.text : c.type).join('\n');
|
||||
// onFinalMessage(content)
|
||||
// })
|
||||
|
||||
// stream.on('error', (error) => {
|
||||
// // the most common error will be invalid API key (401), so we handle this with a nice message
|
||||
// if (error instanceof Anthropic.APIError && error.status === 401) {
|
||||
// onError('Invalid API key.')
|
||||
// }
|
||||
// else {
|
||||
// onError(error.message)
|
||||
// }
|
||||
// })
|
||||
|
||||
// // TODO need to test this to make sure it works, it might throw an error
|
||||
// _setAborter(() => stream.controller.abort())
|
||||
|
||||
// };
|
||||
|
||||
// // Gemini
|
||||
// const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
// let fullText = ''
|
||||
|
||||
// const genAI = new GoogleGenerativeAI(voidConfig.gemini.apikey);
|
||||
// const model = genAI.getGenerativeModel({ model: voidConfig.gemini.model });
|
||||
|
||||
// // remove system messages that get sent to Gemini
|
||||
// // str of all system messages
|
||||
// const systemMessage = messages
|
||||
// .filter(msg => msg.role === 'system')
|
||||
// .map(msg => msg.content)
|
||||
// .join('\n');
|
||||
|
||||
// // Convert messages to Gemini format
|
||||
// const geminiMessages: Content[] = messages
|
||||
// .filter(msg => msg.role !== 'system')
|
||||
// .map((msg, i) => ({
|
||||
// parts: [{ text: msg.content }],
|
||||
// role: msg.role === 'assistant' ? 'model' : 'user'
|
||||
// }))
|
||||
|
||||
// model.generateContentStream({ contents: geminiMessages, systemInstruction: systemMessage, })
|
||||
// .then(async response => {
|
||||
// _setAborter(() => response.stream.return(fullText))
|
||||
|
||||
// for await (const chunk of response.stream) {
|
||||
// const newText = chunk.text();
|
||||
// fullText += newText;
|
||||
// onText(newText, fullText);
|
||||
// }
|
||||
// onFinalMessage(fullText);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// if (error instanceof GoogleGenerativeAIFetchError) {
|
||||
// if (error.status === 400) {
|
||||
// onError('Invalid API key.');
|
||||
// }
|
||||
// else {
|
||||
// onError(`${error.name}:\n${error.message}`);
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// onError(error);
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
// // OpenAI, OpenRouter, OpenAICompatible
|
||||
// const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
// let fullText = ''
|
||||
|
||||
// let openai: OpenAI
|
||||
// let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
||||
|
||||
// const maxTokens = parseMaxTokensStr(voidConfig.default.maxTokens)
|
||||
|
||||
// if (voidConfig.default.whichApi === 'openAI') {
|
||||
// openai = new OpenAI({ apiKey: voidConfig.openAI.apikey, dangerouslyAllowBrowser: true });
|
||||
// options = { model: voidConfig.openAI.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
// }
|
||||
// else if (voidConfig.default.whichApi === 'openRouter') {
|
||||
// openai = new OpenAI({
|
||||
// baseURL: 'https://openrouter.ai/api/v1', apiKey: voidConfig.openRouter.apikey, dangerouslyAllowBrowser: true,
|
||||
// defaultHeaders: {
|
||||
// 'HTTP-Referer': 'https://voideditor.com', // Optional, for including your app on openrouter.ai rankings.
|
||||
// 'X-Title': 'Void Editor', // Optional. Shows in rankings on openrouter.ai.
|
||||
// },
|
||||
// });
|
||||
// options = { model: voidConfig.openRouter.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
// }
|
||||
// else if (voidConfig.default.whichApi === 'openAICompatible') {
|
||||
// openai = new OpenAI({ baseURL: voidConfig.openAICompatible.endpoint, apiKey: voidConfig.openAICompatible.apikey, dangerouslyAllowBrowser: true })
|
||||
// options = { model: voidConfig.openAICompatible.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
// }
|
||||
// else {
|
||||
// console.error(`sendOpenAIMsg: invalid whichApi: ${voidConfig.default.whichApi}`)
|
||||
// throw new Error(`voidConfig.whichAPI was invalid: ${voidConfig.default.whichApi}`)
|
||||
// }
|
||||
|
||||
// openai.chat.completions
|
||||
// .create(options)
|
||||
// .then(async response => {
|
||||
// _setAborter(() => response.controller.abort())
|
||||
// // when receive text
|
||||
// for await (const chunk of response) {
|
||||
// const newText = chunk.choices[0]?.delta?.content || '';
|
||||
// fullText += newText;
|
||||
// onText(newText, fullText);
|
||||
// }
|
||||
// onFinalMessage(fullText);
|
||||
// })
|
||||
// // when error/fail - this catches errors of both .create() and .then(for await)
|
||||
// .catch(error => {
|
||||
// if (error instanceof OpenAI.APIError) {
|
||||
// if (error.status === 401) {
|
||||
// onError('Invalid API key.');
|
||||
// }
|
||||
// else {
|
||||
// onError(`${error.name}:\n${error.message}`);
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// onError(error);
|
||||
// }
|
||||
// })
|
||||
|
||||
// };
|
||||
|
||||
// // Ollama
|
||||
// export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
// let fullText = ''
|
||||
|
||||
// const ollama = new Ollama({ host: voidConfig.ollama.endpoint })
|
||||
|
||||
// ollama.chat({
|
||||
// model: voidConfig.ollama.model,
|
||||
// messages: messages,
|
||||
// stream: true,
|
||||
// options: { num_predict: parseMaxTokensStr(voidConfig.default.maxTokens) } // this is max_tokens
|
||||
// })
|
||||
// .then(async stream => {
|
||||
// _setAborter(() => stream.abort())
|
||||
// // iterate through the stream
|
||||
// for await (const chunk of stream) {
|
||||
// const newText = chunk.message.content;
|
||||
// fullText += newText;
|
||||
// onText(newText, fullText);
|
||||
// }
|
||||
// onFinalMessage(fullText);
|
||||
|
||||
// })
|
||||
// // when error/fail
|
||||
// .catch(error => {
|
||||
// onError(error)
|
||||
// })
|
||||
|
||||
// };
|
||||
|
||||
// // Greptile
|
||||
// // https://docs.greptile.com/api-reference/query
|
||||
// // https://docs.greptile.com/quickstart#sample-response-streamed
|
||||
|
||||
// const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
// let fullText = ''
|
||||
|
||||
// fetch('https://api.greptile.com/v2/query', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Authorization': `Bearer ${voidConfig.greptile.apikey}`,
|
||||
// 'X-Github-Token': `${voidConfig.greptile.githubPAT}`,
|
||||
// 'Content-Type': `application/json`,
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// messages,
|
||||
// stream: true,
|
||||
// repositories: [voidConfig.greptile.repoinfo],
|
||||
// }),
|
||||
// })
|
||||
// // this is {message}\n{message}\n{message}...\n
|
||||
// .then(async response => {
|
||||
// const text = await response.text()
|
||||
// console.log('got greptile', text)
|
||||
// return JSON.parse(`[${text.trim().split('\n').join(',')}]`)
|
||||
// })
|
||||
// // TODO make this actually stream, right now it just sends one message at the end
|
||||
// // TODO add _setAborter() when add streaming
|
||||
// .then(async responseArr => {
|
||||
|
||||
// for (const response of responseArr) {
|
||||
// const type: string = response['type']
|
||||
// const message = response['message']
|
||||
|
||||
// // when receive text
|
||||
// if (type === 'message') {
|
||||
// fullText += message
|
||||
// onText(message, fullText)
|
||||
// }
|
||||
// else if (type === 'sources') {
|
||||
// const { filepath, linestart: _, lineend: _2 } = message as { filepath: string; linestart: number | null; lineend: number | null }
|
||||
// fullText += filepath
|
||||
// onText(filepath, fullText)
|
||||
// }
|
||||
// // type: 'status' with an empty 'message' means last message
|
||||
// else if (type === 'status') {
|
||||
// if (!message) {
|
||||
// onFinalMessage(fullText)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// })
|
||||
// .catch(e => {
|
||||
// onError(e)
|
||||
// });
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
||||
// messages,
|
||||
// onText: onText_,
|
||||
// onFinalMessage: onFinalMessage_,
|
||||
// onError: onError_,
|
||||
// abortRef: abortRef_,
|
||||
// voidConfig,
|
||||
// logging: { loggingName }
|
||||
// }) => {
|
||||
// if (!voidConfig) return;
|
||||
|
||||
// // trim message content (Anthropic and other providers give an error if there is trailing whitespace)
|
||||
// messages = messages.map(m => ({ ...m, content: m.content.trim() }))
|
||||
|
||||
// // only captures number of messages and message "shape", no actual code, instructions, prompts, etc
|
||||
// const captureChatEvent = (eventId: string, extras?: object) => {
|
||||
// // captureEvent(eventId, {
|
||||
// // whichApi: voidConfig.default['whichApi'],
|
||||
// // numMessages: messages?.length,
|
||||
// // messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
||||
// // version: '2024-11-02',
|
||||
// // ...extras,
|
||||
// // })
|
||||
// }
|
||||
// const submit_time = new Date()
|
||||
|
||||
// let _fullTextSoFar = ''
|
||||
// let _aborter: (() => void) | null = null
|
||||
// let _setAborter = (fn: () => void) => { _aborter = fn }
|
||||
// let _didAbort = false
|
||||
|
||||
// const onText = (newText: string, fullText: string) => {
|
||||
// if (_didAbort) return
|
||||
// onText_(newText, fullText)
|
||||
// _fullTextSoFar = fullText
|
||||
// }
|
||||
|
||||
// const onFinalMessage = (fullText: string) => {
|
||||
// if (_didAbort) return
|
||||
// captureChatEvent(`${loggingName} - Received Full Message`, { messageLength: fullText.length, duration: new Date().getMilliseconds() - submit_time.getMilliseconds() })
|
||||
// onFinalMessage_(fullText)
|
||||
// }
|
||||
|
||||
// const onError = (error: string) => {
|
||||
// if (_didAbort) return
|
||||
// captureChatEvent(`${loggingName} - Error`, { error })
|
||||
// onError_(error)
|
||||
// }
|
||||
|
||||
// const onAbort = () => {
|
||||
// captureChatEvent(`${loggingName} - Abort`, { messageLengthSoFar: _fullTextSoFar.length })
|
||||
// _aborter?.()
|
||||
// _didAbort = true
|
||||
// }
|
||||
// abortRef_.current = onAbort
|
||||
|
||||
// captureChatEvent(`${loggingName} - Sending Message`, { messageLength: messages[messages.length - 1]?.content.length })
|
||||
|
||||
// switch (voidConfig.default.whichApi) {
|
||||
// case 'anthropic':
|
||||
// sendAnthropicMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
// break;
|
||||
// case 'openAI':
|
||||
// case 'openRouter':
|
||||
// case 'openAICompatible':
|
||||
// sendOpenAIMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
// break;
|
||||
// case 'gemini':
|
||||
// sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
// break;
|
||||
// case 'ollama':
|
||||
// sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
// break;
|
||||
// case 'greptile':
|
||||
// sendGreptileMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
// break;
|
||||
// default:
|
||||
// onError(`Error: whichApi was ${voidConfig.default.whichApi}, which is not recognized!`)
|
||||
// break;
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
4
src/vs/workbench/contrib/void/browser/react/README.js
Normal file
4
src/vs/workbench/contrib/void/browser/react/README.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// tsup to build all react
|
||||
|
||||
// build tailwind -> styles.css
|
||||
|
||||
23615
src/vs/workbench/contrib/void/browser/react/out/Sidebar.js
Normal file
23615
src/vs/workbench/contrib/void/browser/react/out/Sidebar.js
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,74 @@
|
|||
import React, { useState } from 'react'
|
||||
import { mountFnGenerator } from '../util/mountFnGenerator'
|
||||
import { VIEWPANE_FILTER_ACTION } from '../../../../../browser/parts/views/viewPane'
|
||||
// import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
// import { SidebarChat } from './SidebarChat.js';
|
||||
// import { SidebarSettings } from './SidebarSettings.js';
|
||||
console.log('!!filteraction', VIEWPANE_FILTER_ACTION)
|
||||
const Sidebar = () => {
|
||||
|
||||
// const chatInputRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
|
||||
const [tab, setTab] = useState<'threadSelector' | 'chat' | 'settings'>('chat')
|
||||
|
||||
// // if they pressed the + to add a new chat
|
||||
// useOnVSCodeMessage('startNewThread', (m) => {
|
||||
// setTab('chat');
|
||||
// chatInputRef.current?.focus();
|
||||
// })
|
||||
|
||||
// // ctrl+l should switch back to chat
|
||||
// useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// setTab('chat');
|
||||
// chatInputRef.current?.focus();
|
||||
// })
|
||||
|
||||
// // if they toggled thread selector
|
||||
// useOnVSCodeMessage('toggleThreadSelector', (m) => {
|
||||
// if (tab === 'threadSelector') {
|
||||
// setTab('chat')
|
||||
// chatInputRef.current?.blur();
|
||||
// } else
|
||||
// setTab('threadSelector')
|
||||
// })
|
||||
|
||||
// // if they toggled settings
|
||||
// useOnVSCodeMessage('toggleSettings', (m) => {
|
||||
// if (tab === 'settings') {
|
||||
// setTab('chat')
|
||||
// chatInputRef.current?.blur();
|
||||
// } else
|
||||
// setTab('settings')
|
||||
// })
|
||||
|
||||
return <>
|
||||
<div className={`flex flex-col h-screen w-full`}>
|
||||
|
||||
<span onClick={() => {
|
||||
const tabs = ['chat', 'settings', 'threadSelector']
|
||||
let index = tabs.indexOf(tab)
|
||||
setTab(tabs[(index + 1) % tabs.length] as any)
|
||||
}}>clickme {tab}</span>
|
||||
|
||||
<div className={`mb-2 h-[30vh] ${tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
hi
|
||||
{/* <SidebarThreadSelector onClose={() => setTab('chat')} /> */}
|
||||
</div>
|
||||
|
||||
<div className={`${tab !== 'chat' && tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
{/* <SidebarChat chatInputRef={chatInputRef} /> */}
|
||||
</div>
|
||||
|
||||
<div className={`${tab !== 'settings' ? 'hidden' : ''}`}>
|
||||
{/* <SidebarSettings /> */}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
|
||||
}
|
||||
|
||||
|
||||
const mountFn = mountFnGenerator(Sidebar)
|
||||
export default mountFn
|
||||
|
||||
|
|
@ -0,0 +1,350 @@
|
|||
// import React, { FormEvent, useCallback, useRef, useState } from "react";
|
||||
|
||||
|
||||
// import MarkdownRender from "../../sidebar/markdown/!MarkdownRender";
|
||||
// import BlockCode from "../../sidebar/markdown/!BlockCode";
|
||||
// import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation';
|
||||
|
||||
|
||||
// const filesStr = (fullFiles: File[]) => {
|
||||
// return fullFiles.map(({ filepath, content }) =>
|
||||
// `
|
||||
// ${filepath.fsPath}
|
||||
// \`\`\`
|
||||
// ${content}
|
||||
// \`\`\``).join('\n')
|
||||
// }
|
||||
|
||||
// const userInstructionsStr = (instructions: string, files: File[], selection: CodeSelection | null) => {
|
||||
// let str = '';
|
||||
|
||||
// if (files.length > 0) {
|
||||
// str += filesStr(files);
|
||||
// }
|
||||
|
||||
// if (selection) {
|
||||
// str += `
|
||||
// I am currently selecting this code:
|
||||
// \t\`\`\`${selection.selectionStr}\`\`\`
|
||||
// `;
|
||||
// }
|
||||
|
||||
// if (files.length > 0 && selection) {
|
||||
// str += `
|
||||
// Please edit the selected code or the entire file following these instructions:
|
||||
// `;
|
||||
// } else if (files.length > 0) {
|
||||
// str += `
|
||||
// Please edit the file following these instructions:
|
||||
// `;
|
||||
// } else if (selection) {
|
||||
// str += `
|
||||
// Please edit the selected code following these instructions:
|
||||
// `;
|
||||
// }
|
||||
|
||||
// str += `
|
||||
// \t${instructions}
|
||||
// `;
|
||||
// if (files.length > 0) {
|
||||
// str += `
|
||||
// \tIf you make a change, rewrite the entire file.
|
||||
// `; // TODO don't rewrite the whole file on prompt, instead rewrite it when click Apply
|
||||
// }
|
||||
// return str;
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// const getBasename = (pathStr: string) => {
|
||||
// // "unixify" path
|
||||
// pathStr = pathStr.replace(/[/\\]+/g, "/") // replace any / or \ or \\ with /
|
||||
// const parts = pathStr.split("/") // split on /
|
||||
// return parts[parts.length - 1]
|
||||
// }
|
||||
|
||||
// export const SelectedFiles = ({ files, setFiles, }: { files: vscode.Uri[], setFiles: null | ((files: vscode.Uri[]) => void) }) => {
|
||||
// return (
|
||||
// files.length !== 0 && (
|
||||
// <div className="flex flex-wrap -mx-1 -mb-1">
|
||||
// {files.map((filename, i) => (
|
||||
// <button
|
||||
// key={filename.path}
|
||||
// disabled={!setFiles}
|
||||
// className={`btn btn-secondary btn-sm border border-vscode-input-border rounded flex items-center space-x-2 mx-1 mb-1 disabled:cursor-default`}
|
||||
// type="button"
|
||||
// onClick={() => setFiles?.([...files.slice(0, i), ...files.slice(i + 1, Infinity)])}
|
||||
// >
|
||||
// <span>{getBasename(filename.fsPath)}</span>
|
||||
|
||||
// {/* X button */}
|
||||
// {!!setFiles && <span className="">
|
||||
// <svg
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// fill="none"
|
||||
// viewBox="0 0 24 24"
|
||||
// stroke="currentColor"
|
||||
// className="size-4"
|
||||
// >
|
||||
// <path
|
||||
// strokeLinecap="round"
|
||||
// strokeLinejoin="round"
|
||||
// d="M6 18 18 6M6 6l12 12"
|
||||
// />
|
||||
// </svg>
|
||||
// </span>}
|
||||
// </button>
|
||||
// ))}
|
||||
// </div>
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
|
||||
|
||||
// const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
||||
|
||||
// const role = chatMessage.role
|
||||
// const children = chatMessage.displayContent
|
||||
|
||||
// if (!children)
|
||||
// return null
|
||||
|
||||
// let chatbubbleContents: React.ReactNode
|
||||
|
||||
// if (role === 'user') {
|
||||
// chatbubbleContents = <>
|
||||
// <SelectedFiles files={chatMessage.files} setFiles={null} />
|
||||
// {chatMessage.selection?.selectionStr && <BlockCode
|
||||
// text={chatMessage.selection.selectionStr}
|
||||
// buttonsOnHover={null}
|
||||
// />}
|
||||
// {children}
|
||||
// </>
|
||||
// }
|
||||
// else if (role === 'assistant') {
|
||||
// chatbubbleContents = <MarkdownRender string={children} /> // sectionsHTML
|
||||
// }
|
||||
|
||||
// return <div className={`${role === 'user' ? 'text-right' : 'text-left'}`}>
|
||||
// <div className={`inline-block p-2 rounded-lg space-y-2 ${role === 'user' ? 'bg-vscode-input-bg text-vscode-input-fg' : ''} max-w-full`}>
|
||||
// {chatbubbleContents}
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// export const SidebarChat = ({ chatInputRef }: { chatInputRef: React.RefObject<HTMLTextAreaElement> }) => {
|
||||
|
||||
// // // if they pressed the + to add a new chat
|
||||
// // useOnVSCodeMessage('startNewThread', (m) => {
|
||||
// // const allThreads = getAllThreads()
|
||||
// // // find a thread with 0 messages and switch to it
|
||||
// // for (let threadId in allThreads) {
|
||||
// // if (allThreads[threadId].messages.length === 0) {
|
||||
// // switchToThread(threadId)
|
||||
// // return
|
||||
// // }
|
||||
// // }
|
||||
// // // start a new thread
|
||||
// // startNewThread()
|
||||
// // })
|
||||
|
||||
// // // if user pressed ctrl+l, add their selection to the sidebar
|
||||
// // useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// // setSelection(m.selection)
|
||||
// // const filepath = m.selection.filePath
|
||||
|
||||
// // // add current file to the context if it's not already in the files array
|
||||
// // if (!files.find(f => f.fsPath === filepath.fsPath))
|
||||
// // setFiles(files => [...files, filepath])
|
||||
// // })
|
||||
|
||||
// // state of current message
|
||||
// const [selection, setSelection] = useState<CodeSelection | null>(null) // the code the user is selecting
|
||||
// const [files, setFiles] = useState<vscode.Uri[]>([]) // the names of the files in the chat
|
||||
// const [instructions, setInstructions] = useState('') // the user's instructions
|
||||
|
||||
// // state of chat
|
||||
// const [messageStream, setMessageStream] = useState('')
|
||||
// const [isLoading, setIsLoading] = useState(false)
|
||||
// const abortFnRef = useRef<(() => void) | null>(null)
|
||||
|
||||
// const [latestError, setLatestError] = useState('')
|
||||
|
||||
// // higher level state
|
||||
// const { getAllThreads, getCurrentThread, addMessageToHistory, startNewThread, switchToThread } = useThreads()
|
||||
|
||||
// const { voidConfig } = useVoidConfig()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// const isDisabled = !instructions
|
||||
|
||||
// const formRef = useRef<HTMLFormElement | null>(null)
|
||||
// const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
|
||||
// e.preventDefault()
|
||||
// if (isDisabled) return
|
||||
// if (isLoading) return
|
||||
|
||||
// setIsLoading(true)
|
||||
// setInstructions('');
|
||||
// formRef.current?.reset(); // reset the form's text when clear instructions or unexpected behavior happens
|
||||
// setSelection(null)
|
||||
// setFiles([])
|
||||
// setLatestError('')
|
||||
|
||||
// // request file content from vscode and await response
|
||||
// getVSCodeAPI().postMessage({ type: 'requestFiles', filepaths: files })
|
||||
// const relevantFiles = await awaitVSCodeResponse('files')
|
||||
|
||||
// // add system message to chat history
|
||||
// const systemPromptElt: ChatMessage = { role: 'system', content: generateDiffInstructions }
|
||||
// addMessageToHistory(systemPromptElt)
|
||||
|
||||
// const userContent = userInstructionsStr(instructions, relevantFiles.files, selection)
|
||||
// const newHistoryElt: ChatMessage = { role: 'user', content: userContent, displayContent: instructions, selection, files }
|
||||
// addMessageToHistory(newHistoryElt)
|
||||
|
||||
// // send message to LLM
|
||||
// sendLLMMessage({
|
||||
// logging: { loggingName: 'Chat' },
|
||||
// messages: [...(getCurrentThread()?.messages ?? []).map(m => ({ role: m.role, content: m.content })),],
|
||||
// onText: (newText, fullText) => setMessageStream(fullText),
|
||||
// onFinalMessage: (content) => {
|
||||
|
||||
// // add assistant's message to chat history, and clear selection
|
||||
// const newHistoryElt: ChatMessage = { role: 'assistant', content, displayContent: content }
|
||||
// addMessageToHistory(newHistoryElt)
|
||||
// setMessageStream('')
|
||||
// setIsLoading(false)
|
||||
// },
|
||||
// onError: (error) => {
|
||||
// // add assistant's message to chat history, and clear selection
|
||||
// let content = messageStream; // just use the current content
|
||||
// const newHistoryElt: ChatMessage = { role: 'assistant', content, displayContent: content, }
|
||||
// addMessageToHistory(newHistoryElt)
|
||||
// setMessageStream('')
|
||||
// setIsLoading(false)
|
||||
|
||||
// setLatestError(error)
|
||||
// },
|
||||
// voidConfig,
|
||||
// abortRef: abortFnRef,
|
||||
// })
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// const onAbort = useCallback(() => {
|
||||
// // abort claude
|
||||
// abortFnRef.current?.()
|
||||
|
||||
// // if messageStream was not empty, add it to the history
|
||||
// const llmContent = messageStream || '(null)'
|
||||
// const newHistoryElt: ChatMessage = { role: 'assistant', content: llmContent, displayContent: messageStream, }
|
||||
// addMessageToHistory(newHistoryElt)
|
||||
|
||||
// setMessageStream('')
|
||||
// setIsLoading(false)
|
||||
|
||||
// }, [messageStream, addMessageToHistory])
|
||||
|
||||
|
||||
// return <>
|
||||
// <div className="overflow-x-hidden space-y-4">
|
||||
// {/* previous messages */}
|
||||
// {getCurrentThread() !== null && getCurrentThread()?.messages.map((message, i) =>
|
||||
// <ChatBubble key={i} chatMessage={message} />
|
||||
// )}
|
||||
// {/* message stream */}
|
||||
// <ChatBubble chatMessage={{ role: 'assistant', content: messageStream, displayContent: messageStream }} />
|
||||
// </div>
|
||||
// {/* chatbar */}
|
||||
// <div className="shrink-0 py-4">
|
||||
// {/* selection */}
|
||||
// <div className="text-left">
|
||||
// <div className="relative">
|
||||
// <div className="input">
|
||||
// {/* selection */}
|
||||
// {(files.length || selection?.selectionStr) && <div className="p-2 pb-0 space-y-2">
|
||||
// {/* selected files */}
|
||||
// <SelectedFiles files={files} setFiles={setFiles} />
|
||||
// {/* selected code */}
|
||||
// {!!selection?.selectionStr && (
|
||||
// <BlockCode text={selection.selectionStr}
|
||||
// buttonsOnHover={(
|
||||
// <button
|
||||
// onClick={() => setSelection(null)}
|
||||
// className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
// >
|
||||
// Remove
|
||||
// </button>
|
||||
// )} />
|
||||
// )}
|
||||
// </div>}
|
||||
|
||||
// <form
|
||||
// ref={formRef}
|
||||
// className="flex flex-row items-center rounded-md p-2"
|
||||
// onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }}
|
||||
|
||||
// onSubmit={(e) => {
|
||||
// console.log('submit!')
|
||||
// onSubmit(e)
|
||||
// }}>
|
||||
// {/* input */}
|
||||
|
||||
// <textarea
|
||||
// ref={chatInputRef}
|
||||
// onChange={(e) => { setInstructions(e.target.value) }}
|
||||
// className="w-full p-2 leading-tight resize-none max-h-[50vh] overflow-hidden bg-transparent border-none !outline-none"
|
||||
// placeholder="Ctrl+L to select"
|
||||
// rows={1}
|
||||
// onInput={e => { e.currentTarget.style.height = 'auto'; e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px' }} // Adjust height dynamically
|
||||
// />
|
||||
// {isLoading ?
|
||||
// // stop button
|
||||
// <button
|
||||
// onClick={onAbort}
|
||||
// type='button'
|
||||
// className="btn btn-primary font-bold size-8 flex justify-center items-center rounded-full p-2 max-h-10"
|
||||
// >
|
||||
// <svg
|
||||
// className='scale-50'
|
||||
// stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 24 24" height="24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||
// <path d="M24 24H0V0h24v24z"></path>
|
||||
// </svg>
|
||||
// </button>
|
||||
// :
|
||||
// // submit button (up arrow)
|
||||
// <button
|
||||
// className="btn btn-primary font-bold size-8 flex justify-center items-center rounded-full p-2 max-h-10"
|
||||
// disabled={isDisabled}
|
||||
// type='submit'
|
||||
// >
|
||||
// <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
// <line x1="12" y1="19" x2="12" y2="5"></line>
|
||||
// <polyline points="5 12 12 5 19 12"></polyline>
|
||||
// </svg>
|
||||
// </button>
|
||||
// }
|
||||
// </form>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// {/* error message */}
|
||||
// {!latestError ? null : <div>
|
||||
// {latestError}
|
||||
// </div>}
|
||||
// </div>
|
||||
// </>
|
||||
// }
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
// import React, { useState } from "react";
|
||||
// import { configFields, useVoidConfig, VoidConfigField } from "../util/contextForConfig";
|
||||
|
||||
|
||||
// const SettingOfFieldAndParam = ({ field, param }: { field: VoidConfigField, param: string }) => {
|
||||
// const { voidConfig, partialVoidConfig, voidConfigInfo, setConfigParam } = useVoidConfig()
|
||||
// const { enumArr, defaultVal, description } = voidConfigInfo[field][param]
|
||||
// const val = partialVoidConfig[field]?.[param] ?? defaultVal // current value of this item
|
||||
|
||||
// const updateState = (newValue: string) => { setConfigParam(field, param, newValue) }
|
||||
|
||||
// const resetButton = <button
|
||||
// disabled={val === defaultVal}
|
||||
// title={val === defaultVal ? 'This is the default value.' : `Revert value to '${defaultVal}'?`}
|
||||
// className='group btn btn-sm disabled:opacity-75 disabled:cursor-default'
|
||||
// onClick={() => updateState(defaultVal)}
|
||||
// >
|
||||
// <svg
|
||||
// className='size-5 group-disabled:stroke-current group-disabled:fill-current group-hover:stroke-red-600 group-hover:fill-red-600 duration-200'
|
||||
// fill="currentColor" strokeWidth="0" viewBox="0 0 16 16" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path fillRule="evenodd" clipRule="evenodd" d="M3.5 2v3.5L4 6h3.5V5H4.979l.941-.941a3.552 3.552 0 1 1 5.023 5.023L5.746 14.28l.72.72 5.198-5.198A4.57 4.57 0 0 0 5.2 3.339l-.7.7V2h-1z"></path>
|
||||
// </svg>
|
||||
// </button>
|
||||
|
||||
// const inputElement = enumArr === undefined ?
|
||||
// // string
|
||||
// (<input
|
||||
// className='input p-1 w-full'
|
||||
// type="text"
|
||||
// value={val}
|
||||
// onChange={(e) => updateState(e.target.value)}
|
||||
// />)
|
||||
// :
|
||||
// // enum
|
||||
// (<select
|
||||
// className='dropdown p-1 w-full'
|
||||
// value={val}
|
||||
// onChange={(e) => updateState(e.target.value)}
|
||||
// >
|
||||
// {enumArr.map((option) => (
|
||||
// <option key={option} value={option}>
|
||||
// {option}
|
||||
// </option>
|
||||
// ))}
|
||||
// </select>)
|
||||
|
||||
// return <div>
|
||||
// <label className='hidden'>{param}</label>
|
||||
// <span>{description}</span>
|
||||
// <div className='flex items-center'>
|
||||
// {inputElement}
|
||||
// {resetButton}
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
|
||||
// export const SidebarSettings = () => {
|
||||
|
||||
// const { voidConfig, voidConfigInfo } = useVoidConfig()
|
||||
|
||||
// const current_field = voidConfig.default['whichApi'] as VoidConfigField
|
||||
|
||||
|
||||
// return (
|
||||
// <div className='space-y-4 py-2 overflow-y-auto'>
|
||||
|
||||
// {/* choose the field */}
|
||||
// <div className='outline-vscode-input-bg'>
|
||||
// <SettingOfFieldAndParam
|
||||
// field='default'
|
||||
// param='whichApi'
|
||||
// />
|
||||
// <SettingOfFieldAndParam
|
||||
// field='default'
|
||||
// param='maxTokens'
|
||||
// />
|
||||
// </div>
|
||||
|
||||
// <hr />
|
||||
|
||||
// {/* render all fields, but hide the ones not visible for fast tab switching */}
|
||||
// {configFields.map(field => {
|
||||
// return <div
|
||||
// key={field}
|
||||
// className={`flex flex-col gap-y-2 ${field !== current_field ? 'hidden' : ''}`}
|
||||
// >
|
||||
// {Object.keys(voidConfigInfo[field]).map((param) => (
|
||||
// <SettingOfFieldAndParam
|
||||
// key={param}
|
||||
// field={field}
|
||||
// param={param}
|
||||
// />
|
||||
// ))}
|
||||
// </div>
|
||||
// })}
|
||||
|
||||
// {/* Remove this after 10/21/24, this is just to give developers a heads up about the recent change */}
|
||||
// <div className='pt-20'>
|
||||
// {`We recently updated Settings. To copy your old Void settings over, press Ctrl+Shift+P, `}
|
||||
// {`type 'Open User Settings (JSON)',`}
|
||||
// {` and look for 'void.'. `}
|
||||
// </div>
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// import React from "react";
|
||||
// import { ThreadsProvider, useThreads } from "../util/contextForThreads";
|
||||
|
||||
|
||||
// const truncate = (s: string) => {
|
||||
// let len = s.length
|
||||
// const TRUNC_AFTER = 16
|
||||
// if (len >= TRUNC_AFTER)
|
||||
// s = s.substring(0, TRUNC_AFTER) + '...'
|
||||
// return s
|
||||
// }
|
||||
|
||||
|
||||
// export const SidebarThreadSelector = ({ onClose }: { onClose: () => void }) => {
|
||||
// const { getAllThreads, getCurrentThread, switchToThread } = useThreads()
|
||||
|
||||
// const allThreads = getAllThreads()
|
||||
|
||||
// // sorted by most recent to least recent
|
||||
// const sortedThreadIds = Object.keys(allThreads ?? {}).sort((threadId1, threadId2) => allThreads![threadId1].lastModified > allThreads![threadId2].lastModified ? 1 : -1)
|
||||
|
||||
// return (
|
||||
// <div className="flex flex-col gap-y-1">
|
||||
|
||||
// {/* X button at top right */}
|
||||
// <div className="text-right">
|
||||
// <button className="btn btn-sm" onClick={onClose}>
|
||||
// <svg
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// fill="none"
|
||||
// viewBox="0 0 24 24"
|
||||
// stroke="currentColor"
|
||||
// className="size-4"
|
||||
// >
|
||||
// <path
|
||||
// strokeLinecap="round"
|
||||
// strokeLinejoin="round"
|
||||
// d="M6 18 18 6M6 6l12 12"
|
||||
// />
|
||||
// </svg>
|
||||
// </button>
|
||||
// </div>
|
||||
|
||||
// {/* a list of all the past threads */}
|
||||
// <div className='flex flex-col gap-y-1 max-h-80 overflow-y-auto'>
|
||||
// {sortedThreadIds.map((threadId) => {
|
||||
// if (!allThreads)
|
||||
// return <>Error: Threads not found.</>
|
||||
// const pastThread = allThreads[threadId]
|
||||
|
||||
// let btnStringArr = []
|
||||
|
||||
// let msg1 = truncate(allThreads[threadId].messages[0]?.displayContent ?? '(empty)')
|
||||
// btnStringArr.push(msg1)
|
||||
|
||||
// let msg2 = truncate(allThreads[threadId].messages[1]?.displayContent ?? '')
|
||||
// if (msg2)
|
||||
// btnStringArr.push(msg2)
|
||||
|
||||
// btnStringArr.push(allThreads[threadId].messages.length)
|
||||
|
||||
// const btnString = btnStringArr.join(' / ')
|
||||
|
||||
// return (
|
||||
// <button
|
||||
// key={pastThread.id}
|
||||
// className={`btn btn-sm rounded-sm ${pastThread.id === getCurrentThread()?.id ? "btn-primary" : "btn-secondary"}`}
|
||||
// onClick={() => switchToThread(pastThread.id)}
|
||||
// title={new Date(pastThread.createdAt).toLocaleString()}
|
||||
// >
|
||||
// {btnString}
|
||||
// </button>
|
||||
// )
|
||||
// })}
|
||||
// </div>
|
||||
|
||||
// </div>
|
||||
// )
|
||||
// }
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
// import * as vscode from 'vscode';
|
||||
// import { PartialVoidConfig } from '../webviews/common/contextForConfig'
|
||||
|
||||
// // type CodeSelection = { selectionStr: string, filePath: vscode.Uri }
|
||||
|
||||
// // type File = { filepath: vscode.Uri, content: string }
|
||||
|
||||
// // an area that is currently being diffed
|
||||
// type DiffArea = {
|
||||
// diffareaid: number,
|
||||
// startLine: number,
|
||||
// endLine: number,
|
||||
// originalStartLine: number,
|
||||
// originalEndLine: number,
|
||||
// sweepIndex: number | null // null iff not sweeping
|
||||
// }
|
||||
|
||||
// // the return type of diff creator
|
||||
// type BaseDiff = {
|
||||
// type: 'edit' | 'insertion' | 'deletion';
|
||||
// // repr: string; // representation of the diff in text
|
||||
// originalRange: vscode.Range;
|
||||
// originalCode: string;
|
||||
// range: vscode.Range;
|
||||
// code: string;
|
||||
// }
|
||||
|
||||
// // each diff on the user's screen
|
||||
// type Diff = {
|
||||
// diffid: number,
|
||||
// lenses: vscode.CodeLens[],
|
||||
// } & BaseDiff
|
||||
|
||||
// // editor -> sidebar
|
||||
// type MessageToSidebar = (
|
||||
// | { type: 'ctrl+l', selection: CodeSelection } // user presses ctrl+l in the editor. selection and path are frozen snapshots
|
||||
// | { type: 'ctrl+k', selection: CodeSelection }
|
||||
// | { type: 'files', files: { filepath: vscode.Uri, content: string }[] }
|
||||
// | { type: 'partialVoidConfig', partialVoidConfig: PartialVoidConfig }
|
||||
// | { type: 'allThreads', threads: ChatThreads }
|
||||
// | { type: 'startNewThread' }
|
||||
// | { type: 'toggleThreadSelector' }
|
||||
// | { type: 'toggleSettings' }
|
||||
// | { type: 'deviceId', deviceId: string }
|
||||
// )
|
||||
|
||||
// // sidebar -> editor
|
||||
// type MessageFromSidebar = (
|
||||
// | { type: 'applyChanges', diffRepr: string } // user clicks "apply" in the sidebar
|
||||
// | { type: 'requestFiles', filepaths: vscode.Uri[] }
|
||||
// | { type: 'getPartialVoidConfig' }
|
||||
// | { type: 'persistPartialVoidConfig', partialVoidConfig: PartialVoidConfig }
|
||||
// | { type: 'getAllThreads' }
|
||||
// | { type: 'persistThread', thread: ChatThreads[string] }
|
||||
// | { type: 'getDeviceId' }
|
||||
// )
|
||||
|
||||
|
||||
// // type ChatThreads = {
|
||||
// // [id: string]: {
|
||||
// // id: string; // store the id here too
|
||||
// // createdAt: string; // ISO string
|
||||
// // lastModified: string; // ISO string
|
||||
// // messages: ChatMessage[];
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // type ChatMessage =
|
||||
// // | {
|
||||
// // role: "user";
|
||||
// // content: string; // content sent to the llm
|
||||
// // displayContent: string; // content displayed to user
|
||||
// // selection: CodeSelection | null; // the user's selection
|
||||
// // files: vscode.Uri[]; // the files sent in the message
|
||||
// // }
|
||||
// // | {
|
||||
// // role: "assistant";
|
||||
// // content: string; // content received from LLM
|
||||
// // displayContent: string | undefined; // content displayed to user (this is the same as content for now)
|
||||
// // }
|
||||
// // | {
|
||||
// // role: "system";
|
||||
// // content: string;
|
||||
// // displayContent?: undefined;
|
||||
// // }
|
||||
|
||||
// export {
|
||||
// BaseDiff, Diff,
|
||||
// DiffArea,
|
||||
// CodeSelection,
|
||||
// File,
|
||||
// MessageFromSidebar,
|
||||
// MessageToSidebar,
|
||||
// ChatThreads,
|
||||
// ChatMessage,
|
||||
// }
|
||||
17
src/vs/workbench/contrib/void/browser/react/tsup.config.js
Normal file
17
src/vs/workbench/contrib/void/browser/react/tsup.config.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { defineConfig } from 'tsup'
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['./sidebar-tsx/Sidebar.tsx'], // You'll need to create this index file
|
||||
outDir: './out',
|
||||
format: ['esm'],
|
||||
// dts: true,
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
platform: 'browser',
|
||||
target: 'esnext',
|
||||
outExtension: () => ({ js: '.js' }),
|
||||
external: [/\.\.\/\.\.\/.*/],
|
||||
noExternal: ['react', 'react-dom'],
|
||||
treeshake: true,
|
||||
})
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// import React, { useEffect } from "react";
|
||||
// import * as ReactDOM from "react-dom/client"
|
||||
import React from "react";
|
||||
import * as ReactDOM from "react-dom/client"
|
||||
import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation';
|
||||
// import { initPosthog, identifyUser } from "./posthog";
|
||||
|
||||
// const ListenersAndTracking = () => {
|
||||
|
|
@ -32,30 +33,11 @@
|
|||
|
||||
|
||||
|
||||
// export const mount = (children: React.ReactNode) => {
|
||||
|
||||
// if (typeof document === "undefined") {
|
||||
// console.error("index.tsx error: document was undefined")
|
||||
// return
|
||||
// }
|
||||
|
||||
// // mount the sidebar on the id="root" element
|
||||
// const rootElement = document.getElementById("root")!
|
||||
// // console.log("Void root Element:", rootElement)
|
||||
|
||||
// const content = (<>
|
||||
// <ListenersAndTracking />
|
||||
|
||||
// <PropsProvider rootElement={rootElement}>
|
||||
// <ThreadsProvider>
|
||||
// <ConfigProvider>
|
||||
// {children}
|
||||
// </ConfigProvider>
|
||||
// </ThreadsProvider>
|
||||
// </PropsProvider>
|
||||
// </>)
|
||||
|
||||
// const root = ReactDOM.createRoot(rootElement)
|
||||
// root.render(content);
|
||||
|
||||
// }
|
||||
export const mountFnGenerator = (Component: React.FC<{ accessor: ServicesAccessor }>) => (rootElement: HTMLElement, accessor: ServicesAccessor) => {
|
||||
if (typeof document === "undefined") {
|
||||
console.error("index.tsx error: document was undefined")
|
||||
return
|
||||
}
|
||||
const root = ReactDOM.createRoot(rootElement)
|
||||
root.render(<Component accessor={accessor} />);
|
||||
}
|
||||
|
|
@ -1,43 +1,43 @@
|
|||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry';
|
||||
// import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
// import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions';
|
||||
// import { createDecorator } from '../../../../platform/instantiation/common/instantiation';
|
||||
// import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry';
|
||||
|
||||
interface IMetricsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
}
|
||||
// interface IMetricsService {
|
||||
// readonly _serviceBrand: undefined;
|
||||
// }
|
||||
|
||||
const IMetricsService = createDecorator<IMetricsService>('inlineDiffService');
|
||||
class MetricsService extends Disposable implements IMetricsService {
|
||||
_serviceBrand: undefined;
|
||||
// const IMetricsService = createDecorator<IMetricsService>('inlineDiffService');
|
||||
// class MetricsService extends Disposable implements IMetricsService {
|
||||
// _serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService
|
||||
) {
|
||||
super()
|
||||
}
|
||||
// constructor(
|
||||
// @ITelemetryService private readonly _telemetryService: ITelemetryService
|
||||
// ) {
|
||||
// super()
|
||||
// }
|
||||
|
||||
init() {
|
||||
// init() {
|
||||
|
||||
posthog.init('phc_UanIdujHiLp55BkUTjB1AuBXcasVkdqRwgnwRlWESH2',
|
||||
{
|
||||
api_host: 'https://us.i.posthog.com',
|
||||
person_profiles: 'identified_only' // we only track events from identified users. We identify them in Sidebar
|
||||
}
|
||||
)
|
||||
// posthog.init('phc_UanIdujHiLp55BkUTjB1AuBXcasVkdqRwgnwRlWESH2',
|
||||
// {
|
||||
// api_host: 'https://us.i.posthog.com',
|
||||
// person_profiles: 'identified_only' // we only track events from identified users. We identify them in Sidebar
|
||||
// }
|
||||
// )
|
||||
|
||||
const deviceId = this._telemetryService.devDeviceId
|
||||
console.debug('deviceId', deviceId)
|
||||
// const deviceId = this._telemetryService.devDeviceId
|
||||
// console.debug('deviceId', deviceId)
|
||||
|
||||
posthog.identify(deviceId)
|
||||
// posthog.identify(deviceId)
|
||||
|
||||
|
||||
// export const captureEvent = (eventId: string, properties: object) => {
|
||||
// posthog.capture(eventId, properties)
|
||||
// }
|
||||
// // export const captureEvent = (eventId: string, properties: object) => {
|
||||
// // posthog.capture(eventId, properties)
|
||||
// // }
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
registerSingleton(IMetricsService, MetricsService, InstantiationType.Eager);
|
||||
// registerSingleton(IMetricsService, MetricsService, InstantiationType.Eager);
|
||||
|
|
|
|||
|
|
@ -37,9 +37,11 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js';
|
|||
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
|
||||
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
|
||||
// import { IVoidSettingsService } from './registerSettings.js';
|
||||
import { IEditorService } from '../../../services/editor/common/editorService.js';
|
||||
// import { IEditorService } from '../../../services/editor/common/editorService.js';
|
||||
// import mountFn from './react/out/Sidebar.js';
|
||||
// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
||||
|
||||
const mountFn = (...params: any) => { }
|
||||
|
||||
|
||||
// compare against search.contribution.ts and https://app.greptile.com/chat/w1nsmt3lauwzculipycpn?repo=github%3Amain%3Amicrosoft%2Fvscode
|
||||
|
|
@ -68,8 +70,8 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IHoverService hoverService: IHoverService,
|
||||
// Void:
|
||||
@IVoidSidebarStateService private readonly _voidSidebarStateService: IVoidSidebarStateService,
|
||||
@IThreadHistoryService private readonly _threadHistoryService: IThreadHistoryService,
|
||||
// @IVoidSidebarStateService private readonly _voidSidebarStateService: IVoidSidebarStateService,
|
||||
// @IThreadHistoryService private readonly _threadHistoryService: IThreadHistoryService,
|
||||
// TODO chat service
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService)
|
||||
|
|
@ -83,61 +85,54 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
|
||||
// <div className={`flex flex-col h-screen w-full`}>
|
||||
|
||||
const { root, chat, history, settings } = dom.h('div@root', [
|
||||
dom.h('div@chat', []),
|
||||
dom.h('div@history', []),
|
||||
dom.h('div@settings', []),
|
||||
])
|
||||
root.style.display = 'flex';
|
||||
root.style.flexDirection = 'column';
|
||||
root.style.height = '100vh';
|
||||
root.style.width = '100%';
|
||||
const { root } = dom.h('div@root')
|
||||
dom.append(parent, root);
|
||||
|
||||
this._renderChat(chat);
|
||||
this._renderHistory(history);
|
||||
this._renderSettings(settings);
|
||||
// gets set immediately
|
||||
let accessor_: ServicesAccessor = null as unknown as ServicesAccessor
|
||||
this.instantiationService.invokeFunction(accessor => { accessor_ = accessor });
|
||||
|
||||
mountFn(root, accessor_);
|
||||
}
|
||||
|
||||
|
||||
private _renderChat(element: HTMLElement) {
|
||||
// <div className={`${tab !== 'chat' && tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// <SidebarChat chatInputRef={chatInputRef} />
|
||||
// </div>
|
||||
|
||||
// private _renderChat(element: HTMLElement) {
|
||||
|
||||
// // useEffect(() => {
|
||||
// // this._voidSidebarStateService.onDidChange(() => {
|
||||
// // })
|
||||
// // this._voidSidebarStateService.onFocusChat(() => {
|
||||
// // })
|
||||
// // this._voidSidebarStateService.onBlurChat(() => {
|
||||
// // })
|
||||
// // })
|
||||
|
||||
|
||||
|
||||
this._voidSidebarStateService.onDidChange(() => {
|
||||
})
|
||||
// }
|
||||
|
||||
|
||||
this._voidSidebarStateService.onFocusChat(() => {
|
||||
})
|
||||
this._voidSidebarStateService.onBlurChat(() => {
|
||||
})
|
||||
|
||||
}
|
||||
// private _renderHistory(element: HTMLElement) {
|
||||
// // <div className={`mb-2 h-[30vh] ${tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// // <SidebarThreadSelector onClose={() => setTab('chat')} />
|
||||
// // </div>
|
||||
|
||||
|
||||
private _renderHistory(element: HTMLElement) {
|
||||
// <div className={`mb-2 h-[30vh] ${tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// <SidebarThreadSelector onClose={() => setTab('chat')} />
|
||||
// </div>
|
||||
this._voidSidebarStateService.onDidChange(() => {
|
||||
})
|
||||
// this._voidSidebarStateService.onDidChange(() => {
|
||||
// })
|
||||
|
||||
this._threadHistoryService.onDidChangeCurrentThread(() => {
|
||||
// this._threadHistoryService.onDidChangeCurrentThread(() => {
|
||||
|
||||
})
|
||||
// })
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
private _renderSettings(element: HTMLElement) {
|
||||
// <div className={`${tab !== 'settings' ? 'hidden' : ''}`}>
|
||||
// <SidebarSettings />
|
||||
// </div>
|
||||
// private _renderSettings(element: HTMLElement) {
|
||||
// // <div className={`${tab !== 'settings' ? 'hidden' : ''}`}>
|
||||
// // <SidebarSettings />
|
||||
// // </div>
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -146,7 +141,7 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
|
||||
// ---------- Register viewpane inside the void container ----------
|
||||
|
||||
const voidThemeIcon = Codicon.search;
|
||||
const voidThemeIcon = Codicon.array;
|
||||
const voidViewIcon = registerIcon('void-view-icon', voidThemeIcon, localize('voidViewIcon', 'View icon of the Void chat view.'));
|
||||
|
||||
// called VIEWLET_ID in other places for some reason
|
||||
|
|
@ -265,7 +260,7 @@ registerAction2(class extends Action2 {
|
|||
stateService.setState({ isHistoryOpen: false, currentTab: 'chat' })
|
||||
stateService.focusChat()
|
||||
|
||||
const selection = accessor.get(IEditorService).activeTextEditorControl?.getSelection()
|
||||
// const selection = accessor.get(IEditorService).activeTextEditorControl?.getSelection()
|
||||
|
||||
|
||||
// chat state:
|
||||
|
|
|
|||
|
|
@ -1,359 +0,0 @@
|
|||
import React, { FormEvent, useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
|
||||
import { marked } from 'marked';
|
||||
import MarkdownRender from "./markdown/MarkdownRender";
|
||||
import BlockCode from "./markdown/BlockCode";
|
||||
import { File, ChatMessage, CodeSelection } from "../../common/shared_types";
|
||||
import * as vscode from 'vscode'
|
||||
import { awaitVSCodeResponse, getVSCodeAPI, onMessageFromVSCode, useOnVSCodeMessage } from "../util/getVscodeApi";
|
||||
import { useThreads } from "../util/contextForThreads";
|
||||
import { sendLLMMessage } from "../../common/sendLLMMessage";
|
||||
import { useVoidConfig } from "../util/contextForConfig";
|
||||
import { captureEvent } from "../util/posthog";
|
||||
import { generateDiffInstructions } from "../../common/systemPrompts";
|
||||
|
||||
|
||||
|
||||
const filesStr = (fullFiles: File[]) => {
|
||||
return fullFiles.map(({ filepath, content }) =>
|
||||
`
|
||||
${filepath.fsPath}
|
||||
\`\`\`
|
||||
${content}
|
||||
\`\`\``).join('\n')
|
||||
}
|
||||
|
||||
const userInstructionsStr = (instructions: string, files: File[], selection: CodeSelection | null) => {
|
||||
let str = '';
|
||||
|
||||
if (files.length > 0) {
|
||||
str += filesStr(files);
|
||||
}
|
||||
|
||||
if (selection) {
|
||||
str += `
|
||||
I am currently selecting this code:
|
||||
\t\`\`\`${selection.selectionStr}\`\`\`
|
||||
`;
|
||||
}
|
||||
|
||||
if (files.length > 0 && selection) {
|
||||
str += `
|
||||
Please edit the selected code or the entire file following these instructions:
|
||||
`;
|
||||
} else if (files.length > 0) {
|
||||
str += `
|
||||
Please edit the file following these instructions:
|
||||
`;
|
||||
} else if (selection) {
|
||||
str += `
|
||||
Please edit the selected code following these instructions:
|
||||
`;
|
||||
}
|
||||
|
||||
str += `
|
||||
\t${instructions}
|
||||
`;
|
||||
if (files.length > 0) {
|
||||
str += `
|
||||
\tIf you make a change, rewrite the entire file.
|
||||
`; // TODO don't rewrite the whole file on prompt, instead rewrite it when click Apply
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const getBasename = (pathStr: string) => {
|
||||
// "unixify" path
|
||||
pathStr = pathStr.replace(/[/\\]+/g, "/") // replace any / or \ or \\ with /
|
||||
const parts = pathStr.split("/") // split on /
|
||||
return parts[parts.length - 1]
|
||||
}
|
||||
|
||||
export const SelectedFiles = ({ files, setFiles, }: { files: vscode.Uri[], setFiles: null | ((files: vscode.Uri[]) => void) }) => {
|
||||
return (
|
||||
files.length !== 0 && (
|
||||
<div className="flex flex-wrap -mx-1 -mb-1">
|
||||
{files.map((filename, i) => (
|
||||
<button
|
||||
key={filename.path}
|
||||
disabled={!setFiles}
|
||||
className={`btn btn-secondary btn-sm border border-vscode-input-border rounded flex items-center space-x-2 mx-1 mb-1 disabled:cursor-default`}
|
||||
type="button"
|
||||
onClick={() => setFiles?.([...files.slice(0, i), ...files.slice(i + 1, Infinity)])}
|
||||
>
|
||||
<span>{getBasename(filename.fsPath)}</span>
|
||||
|
||||
{/* X button */}
|
||||
{!!setFiles && <span className="">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="size-4"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M6 18 18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</span>}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
||||
|
||||
const role = chatMessage.role
|
||||
const children = chatMessage.displayContent
|
||||
|
||||
if (!children)
|
||||
return null
|
||||
|
||||
let chatbubbleContents: React.ReactNode
|
||||
|
||||
if (role === 'user') {
|
||||
chatbubbleContents = <>
|
||||
<SelectedFiles files={chatMessage.files} setFiles={null} />
|
||||
{chatMessage.selection?.selectionStr && <BlockCode
|
||||
text={chatMessage.selection.selectionStr}
|
||||
buttonsOnHover={null}
|
||||
/>}
|
||||
{children}
|
||||
</>
|
||||
}
|
||||
else if (role === 'assistant') {
|
||||
chatbubbleContents = <MarkdownRender string={children} /> // sectionsHTML
|
||||
}
|
||||
|
||||
return <div className={`${role === 'user' ? 'text-right' : 'text-left'}`}>
|
||||
<div className={`inline-block p-2 rounded-lg space-y-2 ${role === 'user' ? 'bg-vscode-input-bg text-vscode-input-fg' : ''} max-w-full`}>
|
||||
{chatbubbleContents}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const SidebarChat = ({ chatInputRef }: { chatInputRef: React.RefObject<HTMLTextAreaElement> }) => {
|
||||
|
||||
// // if they pressed the + to add a new chat
|
||||
// useOnVSCodeMessage('startNewThread', (m) => {
|
||||
// const allThreads = getAllThreads()
|
||||
// // find a thread with 0 messages and switch to it
|
||||
// for (let threadId in allThreads) {
|
||||
// if (allThreads[threadId].messages.length === 0) {
|
||||
// switchToThread(threadId)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// // start a new thread
|
||||
// startNewThread()
|
||||
// })
|
||||
|
||||
// // if user pressed ctrl+l, add their selection to the sidebar
|
||||
// useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// setSelection(m.selection)
|
||||
// const filepath = m.selection.filePath
|
||||
|
||||
// // add current file to the context if it's not already in the files array
|
||||
// if (!files.find(f => f.fsPath === filepath.fsPath))
|
||||
// setFiles(files => [...files, filepath])
|
||||
// })
|
||||
|
||||
// state of current message
|
||||
const [selection, setSelection] = useState<CodeSelection | null>(null) // the code the user is selecting
|
||||
const [files, setFiles] = useState<vscode.Uri[]>([]) // the names of the files in the chat
|
||||
const [instructions, setInstructions] = useState('') // the user's instructions
|
||||
|
||||
// state of chat
|
||||
const [messageStream, setMessageStream] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const abortFnRef = useRef<(() => void) | null>(null)
|
||||
|
||||
const [latestError, setLatestError] = useState('')
|
||||
|
||||
// higher level state
|
||||
const { getAllThreads, getCurrentThread, addMessageToHistory, startNewThread, switchToThread } = useThreads()
|
||||
|
||||
const { voidConfig } = useVoidConfig()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const isDisabled = !instructions
|
||||
|
||||
const formRef = useRef<HTMLFormElement | null>(null)
|
||||
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
|
||||
e.preventDefault()
|
||||
if (isDisabled) return
|
||||
if (isLoading) return
|
||||
|
||||
setIsLoading(true)
|
||||
setInstructions('');
|
||||
formRef.current?.reset(); // reset the form's text when clear instructions or unexpected behavior happens
|
||||
setSelection(null)
|
||||
setFiles([])
|
||||
setLatestError('')
|
||||
|
||||
// request file content from vscode and await response
|
||||
getVSCodeAPI().postMessage({ type: 'requestFiles', filepaths: files })
|
||||
const relevantFiles = await awaitVSCodeResponse('files')
|
||||
|
||||
// add system message to chat history
|
||||
const systemPromptElt: ChatMessage = { role: 'system', content: generateDiffInstructions }
|
||||
addMessageToHistory(systemPromptElt)
|
||||
|
||||
const userContent = userInstructionsStr(instructions, relevantFiles.files, selection)
|
||||
const newHistoryElt: ChatMessage = { role: 'user', content: userContent, displayContent: instructions, selection, files }
|
||||
addMessageToHistory(newHistoryElt)
|
||||
|
||||
// send message to LLM
|
||||
sendLLMMessage({
|
||||
logging: { loggingName: 'Chat' },
|
||||
messages: [...(getCurrentThread()?.messages ?? []).map(m => ({ role: m.role, content: m.content })),],
|
||||
onText: (newText, fullText) => setMessageStream(fullText),
|
||||
onFinalMessage: (content) => {
|
||||
|
||||
// add assistant's message to chat history, and clear selection
|
||||
const newHistoryElt: ChatMessage = { role: 'assistant', content, displayContent: content }
|
||||
addMessageToHistory(newHistoryElt)
|
||||
setMessageStream('')
|
||||
setIsLoading(false)
|
||||
},
|
||||
onError: (error) => {
|
||||
// add assistant's message to chat history, and clear selection
|
||||
let content = messageStream; // just use the current content
|
||||
const newHistoryElt: ChatMessage = { role: 'assistant', content, displayContent: content, }
|
||||
addMessageToHistory(newHistoryElt)
|
||||
setMessageStream('')
|
||||
setIsLoading(false)
|
||||
|
||||
setLatestError(error)
|
||||
},
|
||||
voidConfig,
|
||||
abortRef: abortFnRef,
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
const onAbort = useCallback(() => {
|
||||
// abort claude
|
||||
abortFnRef.current?.()
|
||||
|
||||
// if messageStream was not empty, add it to the history
|
||||
const llmContent = messageStream || '(null)'
|
||||
const newHistoryElt: ChatMessage = { role: 'assistant', content: llmContent, displayContent: messageStream, }
|
||||
addMessageToHistory(newHistoryElt)
|
||||
|
||||
setMessageStream('')
|
||||
setIsLoading(false)
|
||||
|
||||
}, [messageStream, addMessageToHistory])
|
||||
|
||||
|
||||
return <>
|
||||
<div className="overflow-x-hidden space-y-4">
|
||||
{/* previous messages */}
|
||||
{getCurrentThread() !== null && getCurrentThread()?.messages.map((message, i) =>
|
||||
<ChatBubble key={i} chatMessage={message} />
|
||||
)}
|
||||
{/* message stream */}
|
||||
<ChatBubble chatMessage={{ role: 'assistant', content: messageStream, displayContent: messageStream }} />
|
||||
</div>
|
||||
{/* chatbar */}
|
||||
<div className="shrink-0 py-4">
|
||||
{/* selection */}
|
||||
<div className="text-left">
|
||||
<div className="relative">
|
||||
<div className="input">
|
||||
{/* selection */}
|
||||
{(files.length || selection?.selectionStr) && <div className="p-2 pb-0 space-y-2">
|
||||
{/* selected files */}
|
||||
<SelectedFiles files={files} setFiles={setFiles} />
|
||||
{/* selected code */}
|
||||
{!!selection?.selectionStr && (
|
||||
<BlockCode text={selection.selectionStr}
|
||||
buttonsOnHover={(
|
||||
<button
|
||||
onClick={() => setSelection(null)}
|
||||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
)} />
|
||||
)}
|
||||
</div>}
|
||||
|
||||
<form
|
||||
ref={formRef}
|
||||
className="flex flex-row items-center rounded-md p-2"
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }}
|
||||
|
||||
onSubmit={(e) => {
|
||||
console.log('submit!')
|
||||
onSubmit(e)
|
||||
}}>
|
||||
{/* input */}
|
||||
|
||||
<textarea
|
||||
ref={chatInputRef}
|
||||
onChange={(e) => { setInstructions(e.target.value) }}
|
||||
className="w-full p-2 leading-tight resize-none max-h-[50vh] overflow-hidden bg-transparent border-none !outline-none"
|
||||
placeholder="Ctrl+L to select"
|
||||
rows={1}
|
||||
onInput={e => { e.currentTarget.style.height = 'auto'; e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px' }} // Adjust height dynamically
|
||||
/>
|
||||
{isLoading ?
|
||||
// stop button
|
||||
<button
|
||||
onClick={onAbort}
|
||||
type='button'
|
||||
className="btn btn-primary font-bold size-8 flex justify-center items-center rounded-full p-2 max-h-10"
|
||||
>
|
||||
<svg
|
||||
className='scale-50'
|
||||
stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 24 24" height="24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M24 24H0V0h24v24z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
:
|
||||
// submit button (up arrow)
|
||||
<button
|
||||
className="btn btn-primary font-bold size-8 flex justify-center items-center rounded-full p-2 max-h-10"
|
||||
disabled={isDisabled}
|
||||
type='submit'
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="12" y1="19" x2="12" y2="5"></line>
|
||||
<polyline points="5 12 12 5 19 12"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* error message */}
|
||||
{!latestError ? null : <div>
|
||||
{latestError}
|
||||
</div>}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
import React, { useState } from "react";
|
||||
import { configFields, useVoidConfig, VoidConfigField } from "../util/contextForConfig";
|
||||
|
||||
|
||||
const SettingOfFieldAndParam = ({ field, param }: { field: VoidConfigField, param: string }) => {
|
||||
const { voidConfig, partialVoidConfig, voidConfigInfo, setConfigParam } = useVoidConfig()
|
||||
const { enumArr, defaultVal, description } = voidConfigInfo[field][param]
|
||||
const val = partialVoidConfig[field]?.[param] ?? defaultVal // current value of this item
|
||||
|
||||
const updateState = (newValue: string) => { setConfigParam(field, param, newValue) }
|
||||
|
||||
const resetButton = <button
|
||||
disabled={val === defaultVal}
|
||||
title={val === defaultVal ? 'This is the default value.' : `Revert value to '${defaultVal}'?`}
|
||||
className='group btn btn-sm disabled:opacity-75 disabled:cursor-default'
|
||||
onClick={() => updateState(defaultVal)}
|
||||
>
|
||||
<svg
|
||||
className='size-5 group-disabled:stroke-current group-disabled:fill-current group-hover:stroke-red-600 group-hover:fill-red-600 duration-200'
|
||||
fill="currentColor" strokeWidth="0" viewBox="0 0 16 16" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path fillRule="evenodd" clipRule="evenodd" d="M3.5 2v3.5L4 6h3.5V5H4.979l.941-.941a3.552 3.552 0 1 1 5.023 5.023L5.746 14.28l.72.72 5.198-5.198A4.57 4.57 0 0 0 5.2 3.339l-.7.7V2h-1z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
const inputElement = enumArr === undefined ?
|
||||
// string
|
||||
(<input
|
||||
className='input p-1 w-full'
|
||||
type="text"
|
||||
value={val}
|
||||
onChange={(e) => updateState(e.target.value)}
|
||||
/>)
|
||||
:
|
||||
// enum
|
||||
(<select
|
||||
className='dropdown p-1 w-full'
|
||||
value={val}
|
||||
onChange={(e) => updateState(e.target.value)}
|
||||
>
|
||||
{enumArr.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>)
|
||||
|
||||
return <div>
|
||||
<label className='hidden'>{param}</label>
|
||||
<span>{description}</span>
|
||||
<div className='flex items-center'>
|
||||
{inputElement}
|
||||
{resetButton}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
export const SidebarSettings = () => {
|
||||
|
||||
const { voidConfig, voidConfigInfo } = useVoidConfig()
|
||||
|
||||
const current_field = voidConfig.default['whichApi'] as VoidConfigField
|
||||
|
||||
|
||||
return (
|
||||
<div className='space-y-4 py-2 overflow-y-auto'>
|
||||
|
||||
{/* choose the field */}
|
||||
<div className='outline-vscode-input-bg'>
|
||||
<SettingOfFieldAndParam
|
||||
field='default'
|
||||
param='whichApi'
|
||||
/>
|
||||
<SettingOfFieldAndParam
|
||||
field='default'
|
||||
param='maxTokens'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
{/* render all fields, but hide the ones not visible for fast tab switching */}
|
||||
{configFields.map(field => {
|
||||
return <div
|
||||
key={field}
|
||||
className={`flex flex-col gap-y-2 ${field !== current_field ? 'hidden' : ''}`}
|
||||
>
|
||||
{Object.keys(voidConfigInfo[field]).map((param) => (
|
||||
<SettingOfFieldAndParam
|
||||
key={param}
|
||||
field={field}
|
||||
param={param}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
})}
|
||||
|
||||
{/* Remove this after 10/21/24, this is just to give developers a heads up about the recent change */}
|
||||
<div className='pt-20'>
|
||||
{`We recently updated Settings. To copy your old Void settings over, press Ctrl+Shift+P, `}
|
||||
{`type 'Open User Settings (JSON)',`}
|
||||
{` and look for 'void.'. `}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
import React from "react";
|
||||
import { ThreadsProvider, useThreads } from "../util/contextForThreads";
|
||||
|
||||
|
||||
const truncate = (s: string) => {
|
||||
let len = s.length
|
||||
const TRUNC_AFTER = 16
|
||||
if (len >= TRUNC_AFTER)
|
||||
s = s.substring(0, TRUNC_AFTER) + '...'
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
export const SidebarThreadSelector = ({ onClose }: { onClose: () => void }) => {
|
||||
const { getAllThreads, getCurrentThread, switchToThread } = useThreads()
|
||||
|
||||
const allThreads = getAllThreads()
|
||||
|
||||
// sorted by most recent to least recent
|
||||
const sortedThreadIds = Object.keys(allThreads ?? {}).sort((threadId1, threadId2) => allThreads![threadId1].lastModified > allThreads![threadId2].lastModified ? 1 : -1)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-1">
|
||||
|
||||
{/* X button at top right */}
|
||||
<div className="text-right">
|
||||
<button className="btn btn-sm" onClick={onClose}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="size-4"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M6 18 18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* a list of all the past threads */}
|
||||
<div className='flex flex-col gap-y-1 max-h-80 overflow-y-auto'>
|
||||
{sortedThreadIds.map((threadId) => {
|
||||
if (!allThreads)
|
||||
return <>Error: Threads not found.</>
|
||||
const pastThread = allThreads[threadId]
|
||||
|
||||
let btnStringArr = []
|
||||
|
||||
let msg1 = truncate(allThreads[threadId].messages[0]?.displayContent ?? '(empty)')
|
||||
btnStringArr.push(msg1)
|
||||
|
||||
let msg2 = truncate(allThreads[threadId].messages[1]?.displayContent ?? '')
|
||||
if (msg2)
|
||||
btnStringArr.push(msg2)
|
||||
|
||||
btnStringArr.push(allThreads[threadId].messages.length)
|
||||
|
||||
const btnString = btnStringArr.join(' / ')
|
||||
|
||||
return (
|
||||
<button
|
||||
key={pastThread.id}
|
||||
className={`btn btn-sm rounded-sm ${pastThread.id === getCurrentThread()?.id ? "btn-primary" : "btn-secondary"}`}
|
||||
onClick={() => switchToThread(pastThread.id)}
|
||||
title={new Date(pastThread.createdAt).toLocaleString()}
|
||||
>
|
||||
{btnString}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
// import React, { useState, useRef } from '../void-imports/react.js'
|
||||
// import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
// import { SidebarChat } from './SidebarChat.js';
|
||||
// import { SidebarSettings } from './SidebarSettings.js';
|
||||
|
||||
|
||||
// const Sidebar = () => {
|
||||
|
||||
// const chatInputRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
|
||||
// const [tab, setTab] = useState<'threadSelector' | 'chat' | 'settings'>('chat')
|
||||
|
||||
// // // if they pressed the + to add a new chat
|
||||
// // useOnVSCodeMessage('startNewThread', (m) => {
|
||||
// // setTab('chat');
|
||||
// // chatInputRef.current?.focus();
|
||||
// // })
|
||||
|
||||
// // // ctrl+l should switch back to chat
|
||||
// // useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// // setTab('chat');
|
||||
// // chatInputRef.current?.focus();
|
||||
// // })
|
||||
|
||||
// // // if they toggled thread selector
|
||||
// // useOnVSCodeMessage('toggleThreadSelector', (m) => {
|
||||
// // if (tab === 'threadSelector') {
|
||||
// // setTab('chat')
|
||||
// // chatInputRef.current?.blur();
|
||||
// // } else
|
||||
// // setTab('threadSelector')
|
||||
// // })
|
||||
|
||||
// // // if they toggled settings
|
||||
// // useOnVSCodeMessage('toggleSettings', (m) => {
|
||||
// // if (tab === 'settings') {
|
||||
// // setTab('chat')
|
||||
// // chatInputRef.current?.blur();
|
||||
// // } else
|
||||
// // setTab('settings')
|
||||
// // })
|
||||
|
||||
// return <>
|
||||
// <div className={`flex flex-col h-screen w-full`}>
|
||||
|
||||
// <div className={`mb-2 h-[30vh] ${tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// <SidebarThreadSelector onClose={() => setTab('chat')} />
|
||||
// </div>
|
||||
|
||||
// <div className={`${tab !== 'chat' && tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// <SidebarChat chatInputRef={chatInputRef} />
|
||||
// </div>
|
||||
|
||||
// <div className={`${tab !== 'settings' ? 'hidden' : ''}`}>
|
||||
// <SidebarSettings />
|
||||
// </div>
|
||||
|
||||
// </div>
|
||||
// </>
|
||||
|
||||
// }
|
||||
|
||||
// export default Sidebar
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
// // renders the code from `src/sidebar`
|
||||
|
||||
// import * as vscode from 'vscode';
|
||||
// import { updateWebviewHTML as _updateWebviewHTML } from './src/extension/extensionLib/updateWebviewHTML';
|
||||
|
||||
// export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||
// public static readonly viewId = 'void.viewnumberone';
|
||||
|
||||
// public webview: Promise<vscode.Webview> // used to send messages to the webview, resolved by _res in resolveWebviewView
|
||||
// private _res: (c: vscode.Webview) => void // used to resolve the webview
|
||||
|
||||
// private readonly _extensionUri: vscode.Uri
|
||||
|
||||
// constructor(context: vscode.ExtensionContext) {
|
||||
// // const extensionPath = context.extensionPath // the directory where the extension is installed, might be useful later... was included in webviewProvider code
|
||||
// this._extensionUri = context.extensionUri
|
||||
|
||||
// let temp_res: typeof this._res | undefined = undefined
|
||||
// this.webview = new Promise((res, rej) => { temp_res = res })
|
||||
// if (!temp_res) throw new Error("Void sidebar provider: resolver was undefined")
|
||||
// this._res = temp_res
|
||||
// }
|
||||
|
||||
// // called internally by vscode
|
||||
// resolveWebviewView(webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, token: vscode.CancellationToken,) {
|
||||
// const webview = webviewView.webview;
|
||||
// _updateWebviewHTML(webview, this._extensionUri, { jsOutLocation: 'dist/webviews/sidebar/index.js', cssOutLocation: 'dist/webviews/styles.css' })
|
||||
// this._res(webview); // resolve webview and _webviewView
|
||||
// }
|
||||
// }
|
||||
|
|
@ -1,289 +0,0 @@
|
|||
// import React, { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState, } from "react"
|
||||
// import { awaitVSCodeResponse, getVSCodeAPI, useOnVSCodeMessage } from "./getVscodeApi"
|
||||
|
||||
// const configEnum = <EnumArr extends readonly string[]>(description: string, defaultVal: EnumArr[number], enumArr: EnumArr) => {
|
||||
// return {
|
||||
// description,
|
||||
// defaultVal,
|
||||
// enumArr,
|
||||
// }
|
||||
// }
|
||||
|
||||
// const configString = (description: string, defaultVal: string) => {
|
||||
// return {
|
||||
// description,
|
||||
// defaultVal,
|
||||
// enumArr: undefined,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // fields you can customize (don't forget 'default' - it isn't included here!)
|
||||
// export const configFields = [
|
||||
// 'anthropic',
|
||||
// 'openAI',
|
||||
// 'gemini',
|
||||
// 'greptile',
|
||||
// 'ollama',
|
||||
// 'openRouter',
|
||||
// 'openAICompatible',
|
||||
// 'azure',
|
||||
// ] as const
|
||||
|
||||
|
||||
|
||||
// const voidConfigInfo: Record<
|
||||
// typeof configFields[number] | 'default', {
|
||||
// [prop: string]: {
|
||||
// description: string,
|
||||
// enumArr?: readonly string[] | undefined,
|
||||
// defaultVal: string,
|
||||
// },
|
||||
// }
|
||||
// > = {
|
||||
// default: {
|
||||
// whichApi: configEnum(
|
||||
// "API Provider.",
|
||||
// 'anthropic',
|
||||
// configFields,
|
||||
// ),
|
||||
|
||||
// maxTokens: configEnum(
|
||||
// "Max number of tokens to output.",
|
||||
// '1024',
|
||||
// [
|
||||
// "default", // this will be parseInt'd into NaN and ignored by the API. Anything that's not a number has this behavior.
|
||||
// "1024",
|
||||
// "2048",
|
||||
// "4096",
|
||||
// "8192"
|
||||
// ] as const,
|
||||
// ),
|
||||
|
||||
// },
|
||||
// anthropic: {
|
||||
// apikey: configString('Anthropic API key.', ''),
|
||||
// model: configEnum(
|
||||
// "Anthropic model to use.",
|
||||
// 'claude-3-5-sonnet-20240620',
|
||||
// [
|
||||
// "claude-3-5-sonnet-20240620",
|
||||
// "claude-3-opus-20240229",
|
||||
// "claude-3-sonnet-20240229",
|
||||
// "claude-3-haiku-20240307"
|
||||
// ] as const,
|
||||
// ),
|
||||
// },
|
||||
// openAI: {
|
||||
// apikey: configString('OpenAI API key.', ''),
|
||||
// model: configEnum(
|
||||
// 'OpenAI model to use.',
|
||||
// 'gpt-4o',
|
||||
// [
|
||||
// "o1-preview",
|
||||
// "o1-mini",
|
||||
// "gpt-4o",
|
||||
// "gpt-4o-2024-05-13",
|
||||
// "gpt-4o-2024-08-06",
|
||||
// "gpt-4o-mini",
|
||||
// "gpt-4o-mini-2024-07-18",
|
||||
// "gpt-4-turbo",
|
||||
// "gpt-4-turbo-2024-04-09",
|
||||
// "gpt-4-turbo-preview",
|
||||
// "gpt-4-0125-preview",
|
||||
// "gpt-4-1106-preview",
|
||||
// "gpt-4",
|
||||
// "gpt-4-0613",
|
||||
// "gpt-3.5-turbo-0125",
|
||||
// "gpt-3.5-turbo",
|
||||
// "gpt-3.5-turbo-1106"
|
||||
// ] as const
|
||||
// ),
|
||||
// },
|
||||
// greptile: {
|
||||
// apikey: configString('Greptile API key.', ''),
|
||||
// githubPAT: configString('Github PAT that Greptile uses to access your repository', ''),
|
||||
// remote: configEnum(
|
||||
// 'Repo location',
|
||||
// 'github',
|
||||
// [
|
||||
// 'github',
|
||||
// 'gitlab'
|
||||
// ] as const
|
||||
// ),
|
||||
// repository: configString('Repository identifier in "owner/repository" format.', ''),
|
||||
// branch: configString('Name of the branch to use.', 'main'),
|
||||
// },
|
||||
// ollama: {
|
||||
// endpoint: configString(
|
||||
// 'The endpoint of your Ollama instance. Start Ollama by running `OLLAMA_ORIGINS="vscode-webview://*" ollama serve`.',
|
||||
// 'http://127.0.0.1:11434'
|
||||
// ),
|
||||
// // TODO we should allow user to select model inside Void, but for now we'll just let them handle the Ollama setup on their own
|
||||
// // model: configEnum(
|
||||
// // 'Ollama model to use.',
|
||||
// // 'llama3.1',
|
||||
// // ["codegemma", "codegemma:2b", "codegemma:7b", "codellama", "codellama:7b", "codellama:13b", "codellama:34b", "codellama:70b", "codellama:code", "codellama:python", "command-r", "command-r:35b", "command-r-plus", "command-r-plus:104b", "deepseek-coder-v2", "deepseek-coder-v2:16b", "deepseek-coder-v2:236b", "falcon2", "falcon2:11b", "firefunction-v2", "firefunction-v2:70b", "gemma", "gemma:2b", "gemma:7b", "gemma2", "gemma2:2b", "gemma2:9b", "gemma2:27b", "llama2", "llama2:7b", "llama2:13b", "llama2:70b", "llama3", "llama3:8b", "llama3:70b", "llama3-chatqa", "llama3-chatqa:8b", "llama3-chatqa:70b", "llama3-gradient", "llama3-gradient:8b", "llama3-gradient:70b", "llama3.1", "llama3.1:8b", "llama3.1:70b", "llama3.1:405b", "llava", "llava:7b", "llava:13b", "llava:34b", "llava-llama3", "llava-llama3:8b", "llava-phi3", "llava-phi3:3.8b", "mistral", "mistral:7b", "mistral-large", "mistral-large:123b", "mistral-nemo", "mistral-nemo:12b", "mixtral", "mixtral:8x7b", "mixtral:8x22b", "moondream", "moondream:1.8b", "openhermes", "openhermes:v2.5", "phi3", "phi3:3.8b", "phi3:14b", "phi3.5", "phi3.5:3.8b", "qwen", "qwen:7b", "qwen:14b", "qwen:32b", "qwen:72b", "qwen:110b", "qwen2", "qwen2:0.5b", "qwen2:1.5b", "qwen2:7b", "qwen2:72b", "smollm", "smollm:135m", "smollm:360m", "smollm:1.7b"] as const
|
||||
// // ),
|
||||
// },
|
||||
// openRouter: {
|
||||
// model: configString(
|
||||
// 'OpenRouter model to use.',
|
||||
// 'openai/gpt-4o'
|
||||
// ),
|
||||
// apikey: configString('OpenRouter API key.', ''),
|
||||
// },
|
||||
// openAICompatible: {
|
||||
// endpoint: configString('The baseUrl (exluding /chat/completions).', 'http://127.0.0.1:11434/v1'),
|
||||
// model: configString('The name of the model to use.', 'gpt-4o'),
|
||||
// apikey: configString('Your API key.', ''),
|
||||
// },
|
||||
// azure: {
|
||||
// // "void.azure.apiKey": {
|
||||
// // "type": "string",
|
||||
// // "description": "Azure API key."
|
||||
// // },
|
||||
// // "void.azure.deploymentId": {
|
||||
// // "type": "string",
|
||||
// // "description": "Azure API deployment ID."
|
||||
// // },
|
||||
// // "void.azure.resourceName": {
|
||||
// // "type": "string",
|
||||
// // "description": "Name of the Azure OpenAI resource. Either this or `baseURL` can be used. \nThe resource name is used in the assembled URL: `https://{resourceName}.openai.azure.com/openai/deployments/{modelId}{path}`"
|
||||
// // },
|
||||
// // "void.azure.providerSettings": {
|
||||
// // "type": "object",
|
||||
// // "properties": {
|
||||
// // "baseURL": {
|
||||
// // "type": "string",
|
||||
// // "default": "https://${resourceName}.openai.azure.com/openai/deployments",
|
||||
// // "description": "Azure API base URL."
|
||||
// // },
|
||||
// // "headers": {
|
||||
// // "type": "object",
|
||||
// // "description": "Custom headers to include in the requests."
|
||||
// // }
|
||||
// // }
|
||||
// // },
|
||||
// },
|
||||
// gemini: {
|
||||
// apikey: configString('Google API key.', ''),
|
||||
// model: configEnum(
|
||||
// 'Gemini model to use.',
|
||||
// 'gemini-1.5-flash',
|
||||
// [
|
||||
// "gemini-1.5-flash",
|
||||
// "gemini-1.5-pro",
|
||||
// "gemini-1.5-flash-8b",
|
||||
// "gemini-1.0-pro"
|
||||
// ] as const
|
||||
// ),
|
||||
// },
|
||||
// }
|
||||
|
||||
|
||||
// // this is the type that comes with metadata like desc, default val, etc
|
||||
// type VoidConfigInfo = typeof voidConfigInfo
|
||||
// export type VoidConfigField = keyof typeof voidConfigInfo // typeof configFields[number]
|
||||
|
||||
// // this is the type that specifies the user's actual config
|
||||
// export type PartialVoidConfig = {
|
||||
// [K in keyof typeof voidConfigInfo]?: {
|
||||
// [P in keyof typeof voidConfigInfo[K]]?: typeof voidConfigInfo[K][P]['defaultVal']
|
||||
// }
|
||||
// }
|
||||
|
||||
// export type VoidConfig = {
|
||||
// [K in keyof typeof voidConfigInfo]: {
|
||||
// [P in keyof typeof voidConfigInfo[K]]: typeof voidConfigInfo[K][P]['defaultVal']
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// export const getVoidConfigFromPartial = (partialVoidConfig: PartialVoidConfig): VoidConfig => {
|
||||
// const config = {} as PartialVoidConfig
|
||||
// for (let field of [...configFields, 'default'] as const) {
|
||||
// config[field] = {}
|
||||
// for (let prop in voidConfigInfo[field]) {
|
||||
// config[field][prop] = partialVoidConfig[field]?.[prop]?.trim() || voidConfigInfo[field][prop].defaultVal
|
||||
// }
|
||||
// }
|
||||
// return config as VoidConfig
|
||||
// }
|
||||
|
||||
// const defaultVoidConfig: VoidConfig = getVoidConfigFromPartial({})
|
||||
|
||||
// // const [stateRef, setState] = useInstantState(initVal)
|
||||
// // setState instantly changes the value of stateRef instead of having to wait until the next render
|
||||
// const useInstantState = <T,>(initVal: T) => {
|
||||
// const stateRef = useRef<T>(initVal)
|
||||
// const [_, setS] = useState<T>(initVal)
|
||||
// const setState = useCallback((newVal: T) => {
|
||||
// setS(newVal);
|
||||
// stateRef.current = newVal;
|
||||
// }, [])
|
||||
// return [stateRef as React.RefObject<T>, setState] as const // make s.current readonly - setState handles all changes
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// type SetConfigParamType = <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => void
|
||||
|
||||
// type ConfigValueType = {
|
||||
// voidConfig: VoidConfig,
|
||||
// voidConfigInfo: VoidConfigInfo,
|
||||
// partialVoidConfig: PartialVoidConfig,
|
||||
// setConfigParam: SetConfigParamType
|
||||
// }
|
||||
|
||||
|
||||
// const ConfigContext = createContext<ConfigValueType>(undefined as unknown as ConfigValueType)
|
||||
|
||||
// export function ConfigProvider({ children }: { children: ReactNode }) {
|
||||
// const [partialVoidConfig, setPartialVoidConfig] = useInstantState<PartialVoidConfig>({}) // the user's selections
|
||||
// const [voidConfig, setVoidConfig] = useState<VoidConfig>(defaultVoidConfig)
|
||||
|
||||
|
||||
// // get the config on mount
|
||||
// useEffect(() => {
|
||||
// getVSCodeAPI().postMessage({ type: 'getPartialVoidConfig' })
|
||||
// awaitVSCodeResponse('partialVoidConfig').then((m) => {
|
||||
// setPartialVoidConfig(m.partialVoidConfig)
|
||||
// const newFullConfig = getVoidConfigFromPartial(m.partialVoidConfig)
|
||||
// setVoidConfig(newFullConfig)
|
||||
// })
|
||||
// }, [setPartialVoidConfig])
|
||||
|
||||
// // return the provider
|
||||
// return (<ConfigContext.Provider
|
||||
// value={{
|
||||
// voidConfig,
|
||||
// voidConfigInfo,
|
||||
// partialVoidConfig: partialVoidConfig.current ?? {},
|
||||
// setConfigParam: (field, param, newVal) => {
|
||||
// const newPartialConfig: PartialVoidConfig = {
|
||||
// ...partialVoidConfig.current,
|
||||
// [field]: {
|
||||
// ...partialVoidConfig.current?.[field],
|
||||
// [param]: newVal
|
||||
// }
|
||||
// }
|
||||
// setPartialVoidConfig(newPartialConfig)
|
||||
// const newFullConfig = getVoidConfigFromPartial(newPartialConfig)
|
||||
// setVoidConfig(newFullConfig)
|
||||
// getVSCodeAPI().postMessage({ type: 'persistPartialVoidConfig', partialVoidConfig: newPartialConfig })
|
||||
// }
|
||||
// }}
|
||||
// >
|
||||
// {children}
|
||||
// </ConfigContext.Provider>
|
||||
// )
|
||||
// }
|
||||
|
||||
// export function useVoidConfig(): ConfigValueType {
|
||||
// const context = useContext<ConfigValueType>(ConfigContext)
|
||||
// if (context === undefined) {
|
||||
// throw new Error("useVoidConfig missing Provider")
|
||||
// }
|
||||
// return context
|
||||
// }
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
// import React, { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState, } from "react"
|
||||
// import { ChatMessage, ChatThreads } from "../../common/shared_types"
|
||||
// import { awaitVSCodeResponse, getVSCodeAPI } from "./getVscodeApi"
|
||||
|
||||
|
||||
// // a "thread" means a chat message history
|
||||
// type ConfigForThreadsValueType = {
|
||||
// readonly getAllThreads: () => ChatThreads;
|
||||
// readonly getCurrentThread: () => ChatThreads[string] | null;
|
||||
// addMessageToHistory: (message: ChatMessage) => void;
|
||||
// switchToThread: (threadId: string) => void;
|
||||
// startNewThread: () => void;
|
||||
// }
|
||||
|
||||
// const ThreadsContext = createContext<ConfigForThreadsValueType>(undefined as unknown as ConfigForThreadsValueType)
|
||||
|
||||
// const createNewThread = () => {
|
||||
// const now = new Date().toISOString()
|
||||
// return {
|
||||
// id: new Date().getTime().toString(),
|
||||
// createdAt: now,
|
||||
// lastModified: now,
|
||||
// messages: [],
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// // const [stateRef, setState] = useInstantState(initVal)
|
||||
// // setState instantly changes the value of stateRef instead of having to wait until the next render
|
||||
// const useInstantState = <T,>(initVal: T) => {
|
||||
// const stateRef = useRef<T>(initVal)
|
||||
// const [_, setS] = useState<T>(initVal)
|
||||
// const setState = useCallback((newVal: T) => {
|
||||
// setS(newVal);
|
||||
// stateRef.current = newVal;
|
||||
// }, [])
|
||||
// return [stateRef as React.RefObject<T>, setState] as const // make s.current readonly - setState handles all changes
|
||||
// }
|
||||
|
||||
|
||||
// export function ThreadsProvider({ children }: { children: ReactNode }) {
|
||||
// const [allThreadsRef, setAllThreads] = useInstantState<ChatThreads>({})
|
||||
// const [currentThreadIdRef, setCurrentThreadId] = useInstantState<string | null>(null)
|
||||
|
||||
// // this loads allThreads in on mount
|
||||
// useEffect(() => {
|
||||
// getVSCodeAPI().postMessage({ type: 'getAllThreads' })
|
||||
// awaitVSCodeResponse('allThreads')
|
||||
// .then(response => {
|
||||
// setAllThreads(response.threads)
|
||||
// })
|
||||
// }, [setAllThreads])
|
||||
|
||||
|
||||
// return (
|
||||
// <ThreadsContext.Provider
|
||||
// value={{
|
||||
// getAllThreads: () => allThreadsRef.current ?? {},
|
||||
// getCurrentThread: () => currentThreadIdRef.current ? allThreadsRef.current?.[currentThreadIdRef.current] ?? null : null,
|
||||
// addMessageToHistory: (message: ChatMessage) => {
|
||||
// let currentThread: ChatThreads[string]
|
||||
// if (!(currentThreadIdRef.current === null || allThreadsRef.current === null)) {
|
||||
// currentThread = allThreadsRef.current[currentThreadIdRef.current]
|
||||
// }
|
||||
// else {
|
||||
// currentThread = createNewThread()
|
||||
// setCurrentThreadId(currentThread.id)
|
||||
// }
|
||||
|
||||
// setAllThreads({
|
||||
// ...allThreadsRef.current,
|
||||
// [currentThread.id]: {
|
||||
// ...currentThread,
|
||||
// lastModified: new Date().toISOString(),
|
||||
// messages: [...currentThread.messages, message],
|
||||
// }
|
||||
// })
|
||||
|
||||
// getVSCodeAPI().postMessage({ type: "persistThread", thread: currentThread })
|
||||
// },
|
||||
// switchToThread: (threadId: string) => {
|
||||
// setCurrentThreadId(threadId);
|
||||
// },
|
||||
// startNewThread: () => {
|
||||
// const newThread = createNewThread()
|
||||
// setAllThreads({
|
||||
// ...allThreadsRef.current,
|
||||
// [newThread.id]: newThread
|
||||
// })
|
||||
// setCurrentThreadId(newThread.id)
|
||||
// },
|
||||
// }}
|
||||
// >
|
||||
// {children}
|
||||
// </ThreadsContext.Provider>
|
||||
// )
|
||||
// }
|
||||
|
||||
// export function useThreads(): ConfigForThreadsValueType {
|
||||
// const context = useContext<ConfigForThreadsValueType>(ThreadsContext)
|
||||
// if (context === undefined) {
|
||||
// throw new Error("useThreads missing Provider")
|
||||
// }
|
||||
// return context
|
||||
// }
|
||||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import React, { ReactNode, useCallback, useEffect, useState } from "react"
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import { atomOneDarkReasonable } from "react-syntax-highlighter/dist/esm/styles/hljs";
|
||||
|
||||
|
||||
const BlockCode = ({ text, buttonsOnHover, language }: { text: string, buttonsOnHover?: ReactNode, language?: string }) => {
|
||||
|
||||
const customStyle = {
|
||||
...atomOneDarkReasonable,
|
||||
'code[class*="language-"]': {
|
||||
...atomOneDarkReasonable['code[class*="language-"]'],
|
||||
background: "none",
|
||||
},
|
||||
}
|
||||
|
||||
return (<>
|
||||
<div className={`relative group w-full bg-vscode-sidebar-bg overflow-hidden isolate`}>
|
||||
|
||||
{!toolbar ? null : (
|
||||
<div className="absolute top-0 right-0 opacity-0 group-hover:opacity-100 duration-200">
|
||||
<div className="flex space-x-2 p-2">{buttonsOnHover === null ? null : buttonsOnHover}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={`overflow-x-auto rounded-sm text-vscode-editor-fg bg-vscode-editor-bg`}
|
||||
>
|
||||
<SyntaxHighlighter
|
||||
language={language ?? 'plaintext'} // TODO must auto detect language
|
||||
style={customStyle}
|
||||
className={"rounded-sm"}
|
||||
>
|
||||
{text}
|
||||
</SyntaxHighlighter>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BlockCode
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
import React, { JSX, useCallback, useEffect, useState } from "react"
|
||||
import { marked, MarkedToken, Token, TokensList } from "marked"
|
||||
import BlockCode from "./BlockCode"
|
||||
import { getVSCodeAPI } from "../../util/getVscodeApi"
|
||||
|
||||
|
||||
enum CopyButtonState {
|
||||
Copy = "Copy",
|
||||
Copied = "Copied!",
|
||||
Error = "Could not copy",
|
||||
}
|
||||
|
||||
const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
|
||||
|
||||
const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => {
|
||||
const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy)
|
||||
|
||||
useEffect(() => {
|
||||
if (copyButtonState !== CopyButtonState.Copy) {
|
||||
setTimeout(() => {
|
||||
setCopyButtonState(CopyButtonState.Copy)
|
||||
}, COPY_FEEDBACK_TIMEOUT)
|
||||
}
|
||||
}, [copyButtonState])
|
||||
|
||||
const onCopy = useCallback(() => {
|
||||
navigator.clipboard.writeText(text).then(
|
||||
() => {
|
||||
setCopyButtonState(CopyButtonState.Copied)
|
||||
},
|
||||
() => {
|
||||
setCopyButtonState(CopyButtonState.Error)
|
||||
}
|
||||
)
|
||||
}, [text])
|
||||
|
||||
return <>
|
||||
<button
|
||||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
onClick={onCopy}
|
||||
>
|
||||
{copyButtonState}
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||
onClick={async () => {
|
||||
getVSCodeAPI().postMessage({ type: "applyChanges", diffRepr: text })
|
||||
}}
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
const RenderToken = ({ token, nested = false }: { token: Token | string, nested?: boolean }): JSX.Element => {
|
||||
|
||||
// deal with built-in tokens first (assume marked token)
|
||||
const t = token as MarkedToken
|
||||
|
||||
if (t.type === "space") {
|
||||
return <span>{t.raw}</span>
|
||||
}
|
||||
|
||||
if (t.type === "code") {
|
||||
return <BlockCode
|
||||
text={t.text}
|
||||
language={t.lang}
|
||||
buttonsOnHover={<CodeButtonsOnHover diffRepr={t.text} />}
|
||||
/>
|
||||
}
|
||||
|
||||
if (t.type === "heading") {
|
||||
const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements
|
||||
return <HeadingTag>{t.text}</HeadingTag>
|
||||
}
|
||||
|
||||
if (t.type === "table") {
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{t.header.map((cell: any, index: number) => (
|
||||
<th key={index} style={{ textAlign: t.align[index] || "left" }}>
|
||||
{cell.raw}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{t.rows.map((row: any[], rowIndex: number) => (
|
||||
<tr key={rowIndex}>
|
||||
{row.map((cell: any, cellIndex: number) => (
|
||||
<td
|
||||
key={cellIndex}
|
||||
style={{ textAlign: t.align[cellIndex] || "left" }}
|
||||
>
|
||||
{cell.raw}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
if (t.type === "hr") {
|
||||
return <hr />
|
||||
}
|
||||
|
||||
if (t.type === "blockquote") {
|
||||
return <blockquote>{t.text}</blockquote>
|
||||
}
|
||||
|
||||
if (t.type === "list") {
|
||||
const ListTag = t.ordered ? "ol" : "ul"
|
||||
return (
|
||||
<ListTag
|
||||
start={t.start ? t.start : undefined}
|
||||
className={`list-inside ${t.ordered ? "list-decimal" : "list-disc"}`}
|
||||
>
|
||||
{t.items.map((item, index) => (
|
||||
<li key={index}>
|
||||
{item.task && (
|
||||
<input type="checkbox" checked={item.checked} readOnly />
|
||||
)}
|
||||
<MarkdownRender string={item.text} nested={true} />
|
||||
</li>
|
||||
))}
|
||||
</ListTag>
|
||||
)
|
||||
}
|
||||
|
||||
if (t.type === "paragraph") {
|
||||
const contents = <>
|
||||
{t.tokens.map((token, index) => (
|
||||
<RenderToken key={index} token={token} />
|
||||
))}
|
||||
</>
|
||||
if (nested)
|
||||
return contents
|
||||
return <p>{contents}</p>
|
||||
}
|
||||
|
||||
// don't actually render <html> tags, just render strings of them
|
||||
if (t.type === "html") {
|
||||
return (
|
||||
<pre>
|
||||
{`<html>`}
|
||||
{t.raw}
|
||||
{`</html>`}
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
if (t.type === "text" || t.type === "escape") {
|
||||
return <span>{t.raw}</span>
|
||||
}
|
||||
|
||||
if (t.type === "def") {
|
||||
return <></> // Definitions are typically not rendered
|
||||
}
|
||||
|
||||
if (t.type === "link") {
|
||||
return (
|
||||
<a href={t.href} title={t.title ?? undefined}>
|
||||
{t.text}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
if (t.type === "image") {
|
||||
return <img src={t.href} alt={t.text} title={t.title ?? undefined} />
|
||||
}
|
||||
|
||||
if (t.type === "strong") {
|
||||
return <strong>{t.text}</strong>
|
||||
}
|
||||
|
||||
if (t.type === "em") {
|
||||
return <em>{t.text}</em>
|
||||
}
|
||||
|
||||
// inline code
|
||||
if (t.type === "codespan") {
|
||||
return (
|
||||
<code className="text-vscode-editor-fg bg-vscode-editor-bg px-1 rounded-sm font-mono">
|
||||
{t.text}
|
||||
</code>
|
||||
)
|
||||
}
|
||||
|
||||
if (t.type === "br") {
|
||||
return <br />
|
||||
}
|
||||
|
||||
// strikethrough
|
||||
if (t.type === "del") {
|
||||
return <del>{t.text}</del>
|
||||
}
|
||||
|
||||
// default
|
||||
return (
|
||||
<div className="bg-orange-50 rounded-sm overflow-hidden">
|
||||
<span className="text-xs text-orange-500">Unknown type:</span>
|
||||
{t.raw}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const MarkdownRender = ({ string, nested = false }: { string: string, nested?: boolean }) => {
|
||||
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
|
||||
return (
|
||||
<>
|
||||
{tokens.map((token, index) => (
|
||||
<RenderToken key={index} token={token} nested={nested} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MarkdownRender
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import { PartialVoidConfig } from '../webviews/common/contextForConfig'
|
||||
|
||||
// type CodeSelection = { selectionStr: string, filePath: vscode.Uri }
|
||||
|
||||
// type File = { filepath: vscode.Uri, content: string }
|
||||
|
||||
// an area that is currently being diffed
|
||||
type DiffArea = {
|
||||
diffareaid: number,
|
||||
startLine: number,
|
||||
endLine: number,
|
||||
originalStartLine: number,
|
||||
originalEndLine: number,
|
||||
sweepIndex: number | null // null iff not sweeping
|
||||
}
|
||||
|
||||
// the return type of diff creator
|
||||
type BaseDiff = {
|
||||
type: 'edit' | 'insertion' | 'deletion';
|
||||
// repr: string; // representation of the diff in text
|
||||
originalRange: vscode.Range;
|
||||
originalCode: string;
|
||||
range: vscode.Range;
|
||||
code: string;
|
||||
}
|
||||
|
||||
// each diff on the user's screen
|
||||
type Diff = {
|
||||
diffid: number,
|
||||
lenses: vscode.CodeLens[],
|
||||
} & BaseDiff
|
||||
|
||||
// editor -> sidebar
|
||||
type MessageToSidebar = (
|
||||
| { type: 'ctrl+l', selection: CodeSelection } // user presses ctrl+l in the editor. selection and path are frozen snapshots
|
||||
| { type: 'ctrl+k', selection: CodeSelection }
|
||||
| { type: 'files', files: { filepath: vscode.Uri, content: string }[] }
|
||||
| { type: 'partialVoidConfig', partialVoidConfig: PartialVoidConfig }
|
||||
| { type: 'allThreads', threads: ChatThreads }
|
||||
| { type: 'startNewThread' }
|
||||
| { type: 'toggleThreadSelector' }
|
||||
| { type: 'toggleSettings' }
|
||||
| { type: 'deviceId', deviceId: string }
|
||||
)
|
||||
|
||||
// sidebar -> editor
|
||||
type MessageFromSidebar = (
|
||||
| { type: 'applyChanges', diffRepr: string } // user clicks "apply" in the sidebar
|
||||
| { type: 'requestFiles', filepaths: vscode.Uri[] }
|
||||
| { type: 'getPartialVoidConfig' }
|
||||
| { type: 'persistPartialVoidConfig', partialVoidConfig: PartialVoidConfig }
|
||||
| { type: 'getAllThreads' }
|
||||
| { type: 'persistThread', thread: ChatThreads[string] }
|
||||
| { type: 'getDeviceId' }
|
||||
)
|
||||
|
||||
|
||||
// type ChatThreads = {
|
||||
// [id: string]: {
|
||||
// id: string; // store the id here too
|
||||
// createdAt: string; // ISO string
|
||||
// lastModified: string; // ISO string
|
||||
// messages: ChatMessage[];
|
||||
// }
|
||||
// }
|
||||
|
||||
// type ChatMessage =
|
||||
// | {
|
||||
// role: "user";
|
||||
// content: string; // content sent to the llm
|
||||
// displayContent: string; // content displayed to user
|
||||
// selection: CodeSelection | null; // the user's selection
|
||||
// files: vscode.Uri[]; // the files sent in the message
|
||||
// }
|
||||
// | {
|
||||
// role: "assistant";
|
||||
// content: string; // content received from LLM
|
||||
// displayContent: string | undefined; // content displayed to user (this is the same as content for now)
|
||||
// }
|
||||
// | {
|
||||
// role: "system";
|
||||
// content: string;
|
||||
// displayContent?: undefined;
|
||||
// }
|
||||
|
||||
export {
|
||||
BaseDiff, Diff,
|
||||
DiffArea,
|
||||
CodeSelection,
|
||||
File,
|
||||
MessageFromSidebar,
|
||||
MessageToSidebar,
|
||||
ChatThreads,
|
||||
ChatMessage,
|
||||
}
|
||||
1
void-imports/.gitignore
vendored
1
void-imports/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
to_be_built/
|
||||
|
|
@ -1 +0,0 @@
|
|||
Use the command 'tsx' to run the main file here
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as tsup from 'tsup'
|
||||
|
||||
const createFiles = (imports: string[], to_be_built_folder: string) => {
|
||||
for (const importName of imports) {
|
||||
const content = `\
|
||||
export * from '${importName}';
|
||||
`;
|
||||
|
||||
const dir = path.dirname(importName);
|
||||
const file = path.basename(importName);
|
||||
|
||||
const fullPath = path.join(to_be_built_folder, dir, `${file}.ts`);
|
||||
|
||||
// Create all necessary directories before writing the file
|
||||
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
||||
fs.writeFileSync(fullPath, content, 'utf8');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const compileFiles = async (imports: string[], to_be_built_folder: string, outDir: string) => {
|
||||
const fileEntries = imports.map((importName) => path.join(to_be_built_folder, `${importName}.ts`))
|
||||
await tsup.build({
|
||||
entry: fileEntries,
|
||||
format: ['esm'],
|
||||
sourcemap: false,
|
||||
bundle: true,
|
||||
clean: true,
|
||||
// minify: true, // no need to minify since it all gets bundled later
|
||||
outDir: path.join(outDir),
|
||||
dts: true,
|
||||
noExternal: [/.*/], // This bundles everything
|
||||
platform: 'browser', // Important for browser compatibility
|
||||
target: 'es2020',
|
||||
outExtension: () => ({ js: '.js' })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const to_be_built_folder = 'to_be_built'
|
||||
// const imports = ['openai', '@anthropic-ai/sdk', 'react', 'react-dom']
|
||||
const imports = ['sendLLMMessage']
|
||||
|
||||
// fs.rmSync(to_be_built_folder, { recursive: true, force: true });
|
||||
// createFiles(imports, to_be_built_folder)
|
||||
|
||||
const OUT_DIR = '../src/vs/workbench/contrib/void/browser/void-imports'
|
||||
compileFiles(imports, to_be_built_folder, OUT_DIR)
|
||||
|
||||
817
void-imports/package-lock.json
generated
817
void-imports/package-lock.json
generated
|
|
@ -1,817 +0,0 @@
|
|||
{
|
||||
"name": "void-imports",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "void-imports",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.32.1",
|
||||
"@google/generative-ai": "^0.21.0",
|
||||
"ollama": "^0.5.9",
|
||||
"openai": "^4.71.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsx": "^4.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/sdk": {
|
||||
"version": "0.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.32.1.tgz",
|
||||
"integrity": "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/node-fetch": "^2.6.4",
|
||||
"abort-controller": "^3.0.0",
|
||||
"agentkeepalive": "^4.2.1",
|
||||
"form-data-encoder": "1.7.2",
|
||||
"formdata-node": "^4.3.2",
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
|
||||
"integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
|
||||
"integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
|
||||
"integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
|
||||
"integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
|
||||
"integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
|
||||
"integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
|
||||
"integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
|
||||
"integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
|
||||
"integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
|
||||
"integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@google/generative-ai": {
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz",
|
||||
"integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.19.64",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.64.tgz",
|
||||
"integrity": "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node-fetch": {
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz",
|
||||
"integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/agentkeepalive": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
|
||||
"integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"humanize-ms": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
|
||||
"integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.23.1",
|
||||
"@esbuild/android-arm": "0.23.1",
|
||||
"@esbuild/android-arm64": "0.23.1",
|
||||
"@esbuild/android-x64": "0.23.1",
|
||||
"@esbuild/darwin-arm64": "0.23.1",
|
||||
"@esbuild/darwin-x64": "0.23.1",
|
||||
"@esbuild/freebsd-arm64": "0.23.1",
|
||||
"@esbuild/freebsd-x64": "0.23.1",
|
||||
"@esbuild/linux-arm": "0.23.1",
|
||||
"@esbuild/linux-arm64": "0.23.1",
|
||||
"@esbuild/linux-ia32": "0.23.1",
|
||||
"@esbuild/linux-loong64": "0.23.1",
|
||||
"@esbuild/linux-mips64el": "0.23.1",
|
||||
"@esbuild/linux-ppc64": "0.23.1",
|
||||
"@esbuild/linux-riscv64": "0.23.1",
|
||||
"@esbuild/linux-s390x": "0.23.1",
|
||||
"@esbuild/linux-x64": "0.23.1",
|
||||
"@esbuild/netbsd-x64": "0.23.1",
|
||||
"@esbuild/openbsd-arm64": "0.23.1",
|
||||
"@esbuild/openbsd-x64": "0.23.1",
|
||||
"@esbuild/sunos-x64": "0.23.1",
|
||||
"@esbuild/win32-arm64": "0.23.1",
|
||||
"@esbuild/win32-ia32": "0.23.1",
|
||||
"@esbuild/win32-x64": "0.23.1"
|
||||
}
|
||||
},
|
||||
"node_modules/event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data-encoder": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
|
||||
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/formdata-node": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
|
||||
"integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-domexception": "1.0.0",
|
||||
"web-streams-polyfill": "4.0.0-beta.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.20"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
|
||||
"integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/humanize-ms": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
||||
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ollama": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ollama/-/ollama-0.5.9.tgz",
|
||||
"integrity": "sha512-F/KZuDRC+ZsVCuMvcOYuQ6zj42/idzCkkuknGyyGVmNStMZ/sU3jQpvhnl4SyC0+zBzLiKNZJnJeuPFuieWZvQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-fetch": "^3.6.20"
|
||||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "4.71.1",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-4.71.1.tgz",
|
||||
"integrity": "sha512-C6JNMaQ1eijM0lrjiRUL3MgThVP5RdwNAghpbJFdW0t11LzmyqON8Eh8MuUuEZ+CeD6bgYl2Fkn2BoptVxv9Ug==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/node-fetch": "^2.6.4",
|
||||
"abort-controller": "^3.0.0",
|
||||
"agentkeepalive": "^4.2.1",
|
||||
"form-data-encoder": "1.7.2",
|
||||
"formdata-node": "^4.3.2",
|
||||
"node-fetch": "^2.6.7"
|
||||
},
|
||||
"bin": {
|
||||
"openai": "bin/cli"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"zod": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz",
|
||||
"integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.23.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "4.0.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
|
||||
"integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-fetch": {
|
||||
"version": "3.6.20",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
|
||||
"integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "void-imports",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tdx index.ts"
|
||||
},
|
||||
"author": "",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.32.1",
|
||||
"@google/generative-ai": "^0.21.0",
|
||||
"ollama": "^0.5.9",
|
||||
"openai": "^4.71.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsx": "^4.19.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,395 +0,0 @@
|
|||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import OpenAI from 'openai';
|
||||
import { Ollama } from 'ollama/browser'
|
||||
import { Content, GoogleGenerativeAI, GoogleGenerativeAIError, GoogleGenerativeAIFetchError } from '@google/generative-ai';
|
||||
// import { VoidConfig } from '../webviews/common/contextForConfig'
|
||||
// import { captureEvent } from '../webviews/common/posthog';
|
||||
// import { ChatMessage } from './shared_types';
|
||||
|
||||
type VoidConfig = any
|
||||
|
||||
export type AbortRef = { current: (() => void) | null }
|
||||
|
||||
export type OnText = (newText: string, fullText: string) => void
|
||||
|
||||
export type OnFinalMessage = (input: string) => void
|
||||
|
||||
export type LLMMessageAnthropic = {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type LLMMessage = {
|
||||
role: 'system' | 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
type SendLLMMessageFnTypeInternal = (params: {
|
||||
messages: LLMMessage[];
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: (error: string) => void;
|
||||
voidConfig: VoidConfig;
|
||||
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
}) => void
|
||||
|
||||
type SendLLMMessageFnTypeExternal = (params: {
|
||||
messages: LLMMessage[];
|
||||
onText: OnText;
|
||||
onFinalMessage: (fullText: string) => void;
|
||||
onError: (error: string) => void;
|
||||
voidConfig: VoidConfig | null;
|
||||
abortRef: AbortRef;
|
||||
|
||||
logging: {
|
||||
loggingName: string,
|
||||
};
|
||||
}) => void
|
||||
|
||||
const parseMaxTokensStr = (maxTokensStr: string) => {
|
||||
// parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN
|
||||
const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr)
|
||||
if (Number.isNaN(int))
|
||||
return undefined
|
||||
return int
|
||||
}
|
||||
|
||||
// Anthropic
|
||||
const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
const anthropic = new Anthropic({ apiKey: voidConfig.anthropic.apikey, dangerouslyAllowBrowser: true }); // defaults to process.env["ANTHROPIC_API_KEY"]
|
||||
|
||||
// find system messages and concatenate them
|
||||
const systemMessage = messages
|
||||
.filter(msg => msg.role === 'system')
|
||||
.map(msg => msg.content)
|
||||
.join('\n');
|
||||
|
||||
// remove system messages for Anthropic
|
||||
const anthropicMessages = messages.filter(msg => msg.role !== 'system') as LLMMessageAnthropic[]
|
||||
|
||||
const stream = anthropic.messages.stream({
|
||||
system: systemMessage,
|
||||
messages: anthropicMessages,
|
||||
model: voidConfig.anthropic.model,
|
||||
max_tokens: parseMaxTokensStr(voidConfig.default.maxTokens)!, // this might be undefined, but it will just throw an error for the user
|
||||
});
|
||||
|
||||
|
||||
// when receive text
|
||||
stream.on('text', (newText, fullText) => {
|
||||
onText(newText, fullText)
|
||||
})
|
||||
|
||||
// when we get the final message on this stream (or when error/fail)
|
||||
stream.on('finalMessage', (claude_response) => {
|
||||
// stringify the response's content
|
||||
const content = claude_response.content.map(c => c.type === 'text' ? c.text : c.type).join('\n');
|
||||
onFinalMessage(content)
|
||||
})
|
||||
|
||||
stream.on('error', (error) => {
|
||||
// the most common error will be invalid API key (401), so we handle this with a nice message
|
||||
if (error instanceof Anthropic.APIError && error.status === 401) {
|
||||
onError('Invalid API key.')
|
||||
}
|
||||
else {
|
||||
onError(error.message)
|
||||
}
|
||||
})
|
||||
|
||||
// TODO need to test this to make sure it works, it might throw an error
|
||||
_setAborter(() => stream.controller.abort())
|
||||
|
||||
};
|
||||
|
||||
// Gemini
|
||||
const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
const genAI = new GoogleGenerativeAI(voidConfig.gemini.apikey);
|
||||
const model = genAI.getGenerativeModel({ model: voidConfig.gemini.model });
|
||||
|
||||
// remove system messages that get sent to Gemini
|
||||
// str of all system messages
|
||||
const systemMessage = messages
|
||||
.filter(msg => msg.role === 'system')
|
||||
.map(msg => msg.content)
|
||||
.join('\n');
|
||||
|
||||
// Convert messages to Gemini format
|
||||
const geminiMessages: Content[] = messages
|
||||
.filter(msg => msg.role !== 'system')
|
||||
.map((msg, i) => ({
|
||||
parts: [{ text: msg.content }],
|
||||
role: msg.role === 'assistant' ? 'model' : 'user'
|
||||
}))
|
||||
|
||||
model.generateContentStream({ contents: geminiMessages, systemInstruction: systemMessage, })
|
||||
.then(async response => {
|
||||
_setAborter(() => response.stream.return(fullText))
|
||||
|
||||
for await (const chunk of response.stream) {
|
||||
const newText = chunk.text();
|
||||
fullText += newText;
|
||||
onText(newText, fullText);
|
||||
}
|
||||
onFinalMessage(fullText);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error instanceof GoogleGenerativeAIFetchError) {
|
||||
if (error.status === 400) {
|
||||
onError('Invalid API key.');
|
||||
}
|
||||
else {
|
||||
onError(`${error.name}:\n${error.message}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
onError(error);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// OpenAI, OpenRouter, OpenAICompatible
|
||||
const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
let openai: OpenAI
|
||||
let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
||||
|
||||
const maxTokens = parseMaxTokensStr(voidConfig.default.maxTokens)
|
||||
|
||||
if (voidConfig.default.whichApi === 'openAI') {
|
||||
openai = new OpenAI({ apiKey: voidConfig.openAI.apikey, dangerouslyAllowBrowser: true });
|
||||
options = { model: voidConfig.openAI.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
}
|
||||
else if (voidConfig.default.whichApi === 'openRouter') {
|
||||
openai = new OpenAI({
|
||||
baseURL: 'https://openrouter.ai/api/v1', apiKey: voidConfig.openRouter.apikey, dangerouslyAllowBrowser: true,
|
||||
defaultHeaders: {
|
||||
'HTTP-Referer': 'https://voideditor.com', // Optional, for including your app on openrouter.ai rankings.
|
||||
'X-Title': 'Void Editor', // Optional. Shows in rankings on openrouter.ai.
|
||||
},
|
||||
});
|
||||
options = { model: voidConfig.openRouter.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
}
|
||||
else if (voidConfig.default.whichApi === 'openAICompatible') {
|
||||
openai = new OpenAI({ baseURL: voidConfig.openAICompatible.endpoint, apiKey: voidConfig.openAICompatible.apikey, dangerouslyAllowBrowser: true })
|
||||
options = { model: voidConfig.openAICompatible.model, messages: messages, stream: true, max_completion_tokens: maxTokens }
|
||||
}
|
||||
else {
|
||||
console.error(`sendOpenAIMsg: invalid whichApi: ${voidConfig.default.whichApi}`)
|
||||
throw new Error(`voidConfig.whichAPI was invalid: ${voidConfig.default.whichApi}`)
|
||||
}
|
||||
|
||||
openai.chat.completions
|
||||
.create(options)
|
||||
.then(async response => {
|
||||
_setAborter(() => response.controller.abort())
|
||||
// when receive text
|
||||
for await (const chunk of response) {
|
||||
const newText = chunk.choices[0]?.delta?.content || '';
|
||||
fullText += newText;
|
||||
onText(newText, fullText);
|
||||
}
|
||||
onFinalMessage(fullText);
|
||||
})
|
||||
// when error/fail - this catches errors of both .create() and .then(for await)
|
||||
.catch(error => {
|
||||
if (error instanceof OpenAI.APIError) {
|
||||
if (error.status === 401) {
|
||||
onError('Invalid API key.');
|
||||
}
|
||||
else {
|
||||
onError(`${error.name}:\n${error.message}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
onError(error);
|
||||
}
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
// Ollama
|
||||
export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
const ollama = new Ollama({ host: voidConfig.ollama.endpoint })
|
||||
|
||||
ollama.chat({
|
||||
model: voidConfig.ollama.model,
|
||||
messages: messages,
|
||||
stream: true,
|
||||
options: { num_predict: parseMaxTokensStr(voidConfig.default.maxTokens) } // this is max_tokens
|
||||
})
|
||||
.then(async stream => {
|
||||
_setAborter(() => stream.abort())
|
||||
// iterate through the stream
|
||||
for await (const chunk of stream) {
|
||||
const newText = chunk.message.content;
|
||||
fullText += newText;
|
||||
onText(newText, fullText);
|
||||
}
|
||||
onFinalMessage(fullText);
|
||||
|
||||
})
|
||||
// when error/fail
|
||||
.catch(error => {
|
||||
onError(error)
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
// Greptile
|
||||
// https://docs.greptile.com/api-reference/query
|
||||
// https://docs.greptile.com/quickstart#sample-response-streamed
|
||||
|
||||
const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
let fullText = ''
|
||||
|
||||
fetch('https://api.greptile.com/v2/query', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${voidConfig.greptile.apikey}`,
|
||||
'X-Github-Token': `${voidConfig.greptile.githubPAT}`,
|
||||
'Content-Type': `application/json`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages,
|
||||
stream: true,
|
||||
repositories: [voidConfig.greptile.repoinfo],
|
||||
}),
|
||||
})
|
||||
// this is {message}\n{message}\n{message}...\n
|
||||
.then(async response => {
|
||||
const text = await response.text()
|
||||
console.log('got greptile', text)
|
||||
return JSON.parse(`[${text.trim().split('\n').join(',')}]`)
|
||||
})
|
||||
// TODO make this actually stream, right now it just sends one message at the end
|
||||
// TODO add _setAborter() when add streaming
|
||||
.then(async responseArr => {
|
||||
|
||||
for (const response of responseArr) {
|
||||
const type: string = response['type']
|
||||
const message = response['message']
|
||||
|
||||
// when receive text
|
||||
if (type === 'message') {
|
||||
fullText += message
|
||||
onText(message, fullText)
|
||||
}
|
||||
else if (type === 'sources') {
|
||||
const { filepath, linestart: _, lineend: _2 } = message as { filepath: string; linestart: number | null; lineend: number | null }
|
||||
fullText += filepath
|
||||
onText(filepath, fullText)
|
||||
}
|
||||
// type: 'status' with an empty 'message' means last message
|
||||
else if (type === 'status') {
|
||||
if (!message) {
|
||||
onFinalMessage(fullText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.catch(e => {
|
||||
onError(e)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
||||
messages,
|
||||
onText: onText_,
|
||||
onFinalMessage: onFinalMessage_,
|
||||
onError: onError_,
|
||||
abortRef: abortRef_,
|
||||
voidConfig,
|
||||
logging: { loggingName }
|
||||
}) => {
|
||||
if (!voidConfig) return;
|
||||
|
||||
// trim message content (Anthropic and other providers give an error if there is trailing whitespace)
|
||||
messages = messages.map(m => ({ ...m, content: m.content.trim() }))
|
||||
|
||||
// only captures number of messages and message "shape", no actual code, instructions, prompts, etc
|
||||
const captureChatEvent = (eventId: string, extras?: object) => {
|
||||
// captureEvent(eventId, {
|
||||
// whichApi: voidConfig.default['whichApi'],
|
||||
// numMessages: messages?.length,
|
||||
// messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
||||
// version: '2024-11-02',
|
||||
// ...extras,
|
||||
// })
|
||||
}
|
||||
const submit_time = new Date()
|
||||
|
||||
let _fullTextSoFar = ''
|
||||
let _aborter: (() => void) | null = null
|
||||
let _setAborter = (fn: () => void) => { _aborter = fn }
|
||||
let _didAbort = false
|
||||
|
||||
const onText = (newText: string, fullText: string) => {
|
||||
if (_didAbort) return
|
||||
onText_(newText, fullText)
|
||||
_fullTextSoFar = fullText
|
||||
}
|
||||
|
||||
const onFinalMessage = (fullText: string) => {
|
||||
if (_didAbort) return
|
||||
captureChatEvent(`${loggingName} - Received Full Message`, { messageLength: fullText.length, duration: new Date().getMilliseconds() - submit_time.getMilliseconds() })
|
||||
onFinalMessage_(fullText)
|
||||
}
|
||||
|
||||
const onError = (error: string) => {
|
||||
if (_didAbort) return
|
||||
captureChatEvent(`${loggingName} - Error`, { error })
|
||||
onError_(error)
|
||||
}
|
||||
|
||||
const onAbort = () => {
|
||||
captureChatEvent(`${loggingName} - Abort`, { messageLengthSoFar: _fullTextSoFar.length })
|
||||
_aborter?.()
|
||||
_didAbort = true
|
||||
}
|
||||
abortRef_.current = onAbort
|
||||
|
||||
captureChatEvent(`${loggingName} - Sending Message`, { messageLength: messages[messages.length - 1]?.content.length })
|
||||
|
||||
switch (voidConfig.default.whichApi) {
|
||||
case 'anthropic':
|
||||
sendAnthropicMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
break;
|
||||
case 'openAI':
|
||||
case 'openRouter':
|
||||
case 'openAICompatible':
|
||||
sendOpenAIMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
break;
|
||||
case 'gemini':
|
||||
sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
break;
|
||||
case 'ollama':
|
||||
sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
break;
|
||||
case 'greptile':
|
||||
sendGreptileMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
break;
|
||||
default:
|
||||
onError(`Error: whichApi was ${voidConfig.default.whichApi}, which is not recognized!`)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "NodeNext",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["to_be_built/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Loading…
Reference in a new issue