mirror of
https://github.com/voideditor/void
synced 2026-05-23 09:28:23 +00:00
better model seln UI + error handling works much better!
This commit is contained in:
parent
62cdaba44b
commit
07fcb3a72c
24 changed files with 291 additions and 332 deletions
|
|
@ -273,7 +273,7 @@ function createServerHost(
|
|||
try {
|
||||
fs.createDirectory(pathMapper.toResource(path));
|
||||
} catch (error) {
|
||||
logger.logNormal('Error fs.createDirectory', { path, error: error + '' });
|
||||
logger.logNormal('Error fs.createDirectory', { path, error });
|
||||
}
|
||||
},
|
||||
getExecutingFilePath(): string {
|
||||
|
|
@ -323,7 +323,7 @@ function createServerHost(
|
|||
try {
|
||||
fs.delete(pathMapper.toResource(path));
|
||||
} catch (error) {
|
||||
logger.logNormal('Error fs.deleteFile', { path, error: error + '' });
|
||||
logger.logNormal('Error fs.deleteFile', { path, error });
|
||||
}
|
||||
},
|
||||
createHash: generateDjb2Hash,
|
||||
|
|
|
|||
12
package-lock.json
generated
12
package-lock.json
generated
|
|
@ -72,6 +72,7 @@
|
|||
"@types/cookie": "^0.3.3",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/gulp-svgmin": "^1.2.1",
|
||||
"@types/http-proxy-agent": "^2.0.1",
|
||||
"@types/kerberos": "^1.1.2",
|
||||
|
|
@ -3017,6 +3018,17 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"main": "./out/main",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"react": "cd ./src/vs/workbench/contrib/void/browser/react/ && node build.js && cd ../../../../../../../",
|
||||
"buildreact": "cd ./src/vs/workbench/contrib/void/browser/react/ && node build.js && cd ../../../../../../../",
|
||||
"test": "echo Please run any of the test scripts from the scripts folder.",
|
||||
"test-browser": "npx playwright install && node test/unit/browser/index.js",
|
||||
"test-browser-amd": "npx playwright install && node test/unit/browser/index.amd.js",
|
||||
|
|
@ -135,6 +135,7 @@
|
|||
"@types/cookie": "^0.3.3",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/gulp-svgmin": "^1.2.1",
|
||||
"@types/http-proxy-agent": "^2.0.1",
|
||||
"@types/kerberos": "^1.1.2",
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export class SendLLMMessageService extends Disposable implements ISendLLMMessage
|
|||
const modelSelection = this.voidConfigStateService.state.modelSelectionOfFeature[featureName]
|
||||
if (modelSelection === null) {
|
||||
this.notificationService.warn('Please add a Provider in Settings!')
|
||||
setTimeout(() => onError({ error: 'Please add a Provider in Settings!' }), 100)
|
||||
onError({ message: 'Please add a Provider in Settings!', fullError: null })
|
||||
return null
|
||||
}
|
||||
const { providerName, modelName } = modelSelection
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export type OnText = (p: { newText: string, fullText: string }) => void
|
|||
|
||||
export type OnFinalMessage = (p: { fullText: string }) => void
|
||||
|
||||
export type OnError = (p: { error: string }) => void
|
||||
export type OnError = (p: { message: string, fullError: Error | null }) => void
|
||||
|
||||
export type AbortRef = { current: (() => void) | null }
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../storage/comm
|
|||
import { defaultVoidProviderState, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider } from './voidConfigTypes.js';
|
||||
|
||||
|
||||
const CONFIG_STORAGE_KEY = 'void.voidConfigStateII'
|
||||
const STORAGE_KEY = 'void.voidConfigStateII'
|
||||
|
||||
type SetSettingOfProviderFn = <K extends ProviderName>(
|
||||
providerName: K,
|
||||
|
|
@ -36,6 +36,7 @@ export interface IVoidConfigStateService {
|
|||
readonly _serviceBrand: undefined;
|
||||
readonly state: VoidConfigState;
|
||||
onDidChangeState: Event<void>;
|
||||
onDidGetInitState: Event<void>;
|
||||
setSettingOfProvider: SetSettingOfProviderFn;
|
||||
setModelSelectionOfFeature: SetModelSelectionOfFeature;
|
||||
}
|
||||
|
|
@ -57,6 +58,9 @@ class VoidConfigStateService extends Disposable implements IVoidConfigStateServi
|
|||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes
|
||||
|
||||
private readonly _onDidGetInitState = new Emitter<void>();
|
||||
readonly onDidGetInitState: Event<void> = this._onDidGetInitState.event;
|
||||
|
||||
state: VoidConfigState;
|
||||
|
||||
constructor(
|
||||
|
|
@ -71,12 +75,12 @@ class VoidConfigStateService extends Disposable implements IVoidConfigStateServi
|
|||
this.state = defaultState()
|
||||
|
||||
// read and update the actual state immediately
|
||||
this._readVoidConfigState().then(voidConfigState => { this._setState(voidConfigState) })
|
||||
this._readVoidConfigState().then(voidConfigState => { this._setState(voidConfigState, 'initialState') })
|
||||
|
||||
}
|
||||
|
||||
private async _readVoidConfigState(): Promise<VoidConfigState> {
|
||||
const encryptedPartialConfig = this._storageService.get(CONFIG_STORAGE_KEY, StorageScope.APPLICATION)
|
||||
const encryptedPartialConfig = this._storageService.get(STORAGE_KEY, StorageScope.APPLICATION)
|
||||
|
||||
if (!encryptedPartialConfig)
|
||||
return defaultState()
|
||||
|
|
@ -88,7 +92,7 @@ class VoidConfigStateService extends Disposable implements IVoidConfigStateServi
|
|||
|
||||
private async _storeVoidConfigState(voidConfigState: VoidConfigState) {
|
||||
const encryptedVoidConfigStr = await this._encryptionService.encrypt(JSON.stringify(voidConfigState))
|
||||
this._storageService.store(CONFIG_STORAGE_KEY, encryptedVoidConfigStr, StorageScope.APPLICATION, StorageTarget.USER);
|
||||
this._storageService.store(STORAGE_KEY, encryptedVoidConfigStr, StorageScope.APPLICATION, StorageTarget.USER);
|
||||
}
|
||||
|
||||
setSettingOfProvider: SetSettingOfProviderFn = async (providerName, option, newVal) => {
|
||||
|
|
@ -125,9 +129,12 @@ class VoidConfigStateService extends Disposable implements IVoidConfigStateServi
|
|||
|
||||
|
||||
// internal function to update state, should be called every time state changes
|
||||
private async _setState(voidConfigState: VoidConfigState) {
|
||||
private async _setState(voidConfigState: VoidConfigState, type: 'usual' | 'initialState' = 'usual') {
|
||||
this.state = voidConfigState
|
||||
this._onDidChangeState.fire()
|
||||
if (type === 'usual')
|
||||
this._onDidChangeState.fire()
|
||||
else if (type === 'initialState')
|
||||
this._onDidGetInitState.fire()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,25 @@ type DisplayInfo = {
|
|||
placeholder: string,
|
||||
}
|
||||
|
||||
export const titleOfProviderName = (providerName: ProviderName) => {
|
||||
if (providerName === 'anthropic')
|
||||
return 'Anthropic'
|
||||
else if (providerName === 'openAI')
|
||||
return 'OpenAI'
|
||||
else if (providerName === 'ollama')
|
||||
return 'Ollama'
|
||||
else if (providerName === 'openRouter')
|
||||
return 'OpenRouter'
|
||||
else if (providerName === 'openAICompatible')
|
||||
return 'OpenAI-Compatible'
|
||||
else if (providerName === 'gemini')
|
||||
return 'Gemini'
|
||||
else if (providerName === 'groq')
|
||||
return 'Groq'
|
||||
|
||||
throw new Error(`descOfProviderName: Unknown provider name: "${providerName}"`)
|
||||
}
|
||||
|
||||
export const displayInfoOfSettingName = (providerName: ProviderName, settingName: SettingName): DisplayInfo => {
|
||||
if (settingName === 'apiKey') {
|
||||
return {
|
||||
|
|
@ -254,8 +273,8 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
|
|||
}
|
||||
else if (settingName === 'endpoint') {
|
||||
return {
|
||||
title: providerName === 'ollama' ? 'The endpoint of your Ollama instance.' :
|
||||
providerName === 'openAICompatible' ? 'The baseUrl (exluding /chat/completions).'
|
||||
title: providerName === 'ollama' ? 'Your Ollama endpoint' :
|
||||
providerName === 'openAICompatible' ? 'Endpoint compatible with OpenAI API' // (do not include /chat/completions)
|
||||
: '(never)',
|
||||
type: 'string',
|
||||
placeholder: providerName === 'ollama' || providerName === 'openAICompatible' ?
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import { parseMaxTokensStr } from './util.js';
|
||||
import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js';
|
||||
import { displayInfoOfSettingName } from '../../common/voidConfigTypes.js';
|
||||
|
||||
// Anthropic
|
||||
type LLMMessageAnthropic = {
|
||||
|
|
@ -16,6 +17,12 @@ export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onTex
|
|||
|
||||
const thisConfig = settingsOfProvider.anthropic
|
||||
|
||||
const maxTokens = parseMaxTokensStr(thisConfig.maxTokens)
|
||||
if (maxTokens === undefined) {
|
||||
onError({ message: `Please set a value for ${displayInfoOfSettingName('anthropic', 'maxTokens').title}.`, fullError: null })
|
||||
return
|
||||
}
|
||||
|
||||
const anthropic = new Anthropic({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true });
|
||||
|
||||
// find system messages and concatenate them
|
||||
|
|
@ -27,11 +34,13 @@ export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onTex
|
|||
// 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: modelName,
|
||||
max_tokens: parseMaxTokensStr(thisConfig.maxTokens)!, // this might be undefined, but it will just throw an error for the user to see
|
||||
max_tokens: maxTokens,
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -50,10 +59,10 @@ export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onTex
|
|||
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({ error: 'Invalid API key.' })
|
||||
onError({ message: 'Invalid API key.', fullError: error })
|
||||
}
|
||||
else {
|
||||
onError({ error: error + '' })
|
||||
onError({ message: error + '', fullError: error }) // anthropic errors can be stringified nicely like this
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ export const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, on
|
|||
})
|
||||
.catch((error) => {
|
||||
if (error instanceof GoogleGenerativeAIFetchError && error.status === 400) {
|
||||
onError({ error: 'Invalid API key.' });
|
||||
onError({ message: 'Invalid API key.', fullError: null });
|
||||
}
|
||||
else {
|
||||
onError({ error: error + '' });
|
||||
onError({ message: error + '', fullError: error });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onTe
|
|||
onFinalMessage({ fullText });
|
||||
})
|
||||
.catch(error => {
|
||||
onError({ error: error + '' });
|
||||
onError({ message: error + '', fullError: error });
|
||||
})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
|
|||
// return;
|
||||
// }
|
||||
// }
|
||||
onError({ error })
|
||||
onError({ message: error + '', fullError: error })
|
||||
})
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -58,10 +58,10 @@ export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText,
|
|||
// when error/fail - this catches errors of both .create() and .then(for await)
|
||||
.catch(error => {
|
||||
if (error instanceof OpenAI.APIError && error.status === 401) {
|
||||
onError({ error: 'Invalid API key.' });
|
||||
onError({ message: 'Invalid API key.', fullError: error });
|
||||
}
|
||||
else {
|
||||
onError({ error: error + '' });
|
||||
onError({ message: error, fullError: error });
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -59,11 +59,12 @@ export const sendLLMMessage = ({
|
|||
onFinalMessage_({ fullText })
|
||||
}
|
||||
|
||||
const onError: OnError = ({ error }) => {
|
||||
const onError: OnError = ({ message: error, fullError }) => {
|
||||
if (_didAbort) return
|
||||
console.log("ERROR!!!!!", error)
|
||||
console.error('sendLLMMessage onError:', error)
|
||||
captureChatEvent(`${loggingName} - Error`, { error })
|
||||
onError_({ error })
|
||||
onError_({ message: error, fullError })
|
||||
}
|
||||
|
||||
const onAbort = () => {
|
||||
|
|
@ -96,14 +97,14 @@ export const sendLLMMessage = ({
|
|||
sendGroqMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
default:
|
||||
onError({ error: `Error: Void provider was "${providerName}", which is not recognized.` })
|
||||
onError({ message: `Error: Void provider was "${providerName}", which is not recognized.`, fullError: null })
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
if (error instanceof Error) { onError({ error: error + '' }) }
|
||||
else { onError({ error: `Unexpected Error in sendLLMMessage: ${error}` }); }
|
||||
if (error instanceof Error) { onError({ message: error + '', fullError: error }) }
|
||||
else { onError({ message: `Unexpected Error in sendLLMMessage: ${error}`, fullError: error }); }
|
||||
// ; (_aborter as any)?.()
|
||||
// _didAbort = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export class LLMMessageChannel implements IServerChannel {
|
|||
...params,
|
||||
onText: ({ newText, fullText }) => { this._onText.fire({ requestId, newText, fullText }); },
|
||||
onFinalMessage: ({ fullText }) => { this._onFinalMessage.fire({ requestId, fullText }); },
|
||||
onError: ({ error }) => { console.log('sendLLM: firing err'); this._onError.fire({ requestId, error }); },
|
||||
onError: ({ message: error, fullError }) => { console.log('sendLLM: firing err'); this._onError.fire({ requestId, message: error, fullError }); },
|
||||
abortRef: this._abortRefOfRequestId[requestId],
|
||||
}
|
||||
sendLLMMessage(mainThreadParams, this.metricsService);
|
||||
|
|
|
|||
|
|
@ -7,59 +7,60 @@ import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|||
import { ErrorDisplay } from './ErrorDisplay.js';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
fallback?: ReactNode;
|
||||
onDismiss?: () => void;
|
||||
children: ReactNode;
|
||||
fallback?: ReactNode;
|
||||
onDismiss?: () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
error: Error | null;
|
||||
errorInfo: ErrorInfo | null;
|
||||
hasError: boolean;
|
||||
error: Error | null;
|
||||
errorInfo: ErrorInfo | null;
|
||||
}
|
||||
|
||||
class ErrorBoundary extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
hasError: false,
|
||||
error: null,
|
||||
errorInfo: null
|
||||
};
|
||||
}
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
hasError: false,
|
||||
error: null,
|
||||
errorInfo: null
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): Partial<State> {
|
||||
return {
|
||||
hasError: true,
|
||||
error
|
||||
};
|
||||
}
|
||||
static getDerivedStateFromError(error: Error): Partial<State> {
|
||||
return {
|
||||
hasError: true,
|
||||
error
|
||||
};
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
||||
this.setState({
|
||||
error,
|
||||
errorInfo
|
||||
});
|
||||
}
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
||||
this.setState({
|
||||
error,
|
||||
errorInfo
|
||||
});
|
||||
}
|
||||
|
||||
render(): ReactNode {
|
||||
if (this.state.hasError && this.state.error) {
|
||||
// If a custom fallback is provided, use it
|
||||
if (this.props.fallback) {
|
||||
return this.props.fallback;
|
||||
}
|
||||
render(): ReactNode {
|
||||
if (this.state.hasError && this.state.error) {
|
||||
// If a custom fallback is provided, use it
|
||||
if (this.props.fallback) {
|
||||
return this.props.fallback;
|
||||
}
|
||||
|
||||
// Use ErrorDisplay component as the default error UI
|
||||
return (
|
||||
<ErrorDisplay
|
||||
error={this.state.error}
|
||||
onDismiss={this.props.onDismiss || null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
// Use ErrorDisplay component as the default error UI
|
||||
return (
|
||||
<ErrorDisplay
|
||||
message={this.state.error + ''}
|
||||
fullError={this.state.error}
|
||||
onDismiss={this.props.onDismiss || null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
|
|
|
|||
|
|
@ -7,82 +7,31 @@ import React, { useState } from 'react';
|
|||
import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react';
|
||||
|
||||
|
||||
// const opaqueMessage = `\
|
||||
// Unfortunately, Void can't see the full error. However, you should be able to find more details by pressing ${getCmdKey()}+Shift+P, typing "Toggle Developer Tools", and looking at the console.\n
|
||||
// This error often means you have an incorrect API key. If you're self-hosting your own server, it might mean your CORS headers are off, and you should make sure your server's response has the header "Access-Control-Allow-Origins" set to "*", or at least allows "vscode-file://vscode-app".`
|
||||
// if ((error instanceof Error) && (error.cause + '').includes('TypeError: Failed to fetch')) {
|
||||
// e = error as any
|
||||
// e['Void Team'] = opaqueMessage
|
||||
// }
|
||||
|
||||
|
||||
type Details = {
|
||||
message: string,
|
||||
name: string,
|
||||
stack: string | null,
|
||||
cause: string | null,
|
||||
code: string | null,
|
||||
additional: Record<string, any>
|
||||
}
|
||||
|
||||
// Get detailed error information
|
||||
const getErrorDetails = (error: unknown) => {
|
||||
|
||||
let details: Details;
|
||||
|
||||
let e: Error & { [other: string]: undefined | any }
|
||||
|
||||
// If fetch() fails, it gives an opaque message. We add extra details to the error.
|
||||
if (error instanceof Error) {
|
||||
e = error
|
||||
}
|
||||
// sometimes error is an object but not an Error
|
||||
else if (typeof error === 'object') {
|
||||
e = new Error(`More details below.`, { cause: JSON.stringify(error) })
|
||||
|
||||
}
|
||||
else {
|
||||
e = new Error(String(error))
|
||||
}
|
||||
// console.log('error display', JSON.stringify(e))
|
||||
|
||||
const message = e.message && e.error ?
|
||||
(e.message + ':\n' + e.error)
|
||||
: e.message || e.error || JSON.stringify(error)
|
||||
|
||||
details = {
|
||||
name: e.name || 'Error',
|
||||
message: message,
|
||||
stack: null, // e.stack is ignored because it's ugly and not very useful
|
||||
cause: e.cause ? String(e.cause) : null,
|
||||
code: e.code || null,
|
||||
additional: {}
|
||||
}
|
||||
|
||||
|
||||
// Collect any additional properties from e
|
||||
for (let prop of Object.getOwnPropertyNames(e).filter((prop) => !Object.keys(details).includes(prop)))
|
||||
details.additional[prop] = (e as any)[prop]
|
||||
|
||||
return details;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const ErrorDisplay = ({
|
||||
error,
|
||||
onDismiss = null,
|
||||
showDismiss = true,
|
||||
message,
|
||||
fullError,
|
||||
onDismiss,
|
||||
showDismiss,
|
||||
}: {
|
||||
error: Error | object | string,
|
||||
message: string,
|
||||
fullError: Error | null,
|
||||
onDismiss: (() => void) | null,
|
||||
showDismiss?: boolean,
|
||||
className?: string
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const details = getErrorDetails(error);
|
||||
const hasDetails = details.cause || Object.keys(details.additional).length > 0;
|
||||
let details: string | null = null;
|
||||
|
||||
if (fullError === null) {
|
||||
details = null
|
||||
}
|
||||
else if (typeof fullError === 'object') {
|
||||
details = JSON.stringify(fullError, null, 2)
|
||||
}
|
||||
else if (typeof fullError === 'string') {
|
||||
details = null
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={`rounded-lg border border-red-200 bg-red-50 p-4 overflow-auto`}>
|
||||
|
|
@ -92,16 +41,18 @@ export const ErrorDisplay = ({
|
|||
<AlertCircle className="h-5 w-5 text-red-500 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-red-800">
|
||||
{details.name}
|
||||
{/* eg Error */}
|
||||
Error
|
||||
</h3>
|
||||
<p className="text-red-700 mt-1">
|
||||
{details.message}
|
||||
{/* eg Something went wrong */}
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
{hasDetails && (
|
||||
{details && (
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="text-red-600 hover:text-red-800 p-1 rounded"
|
||||
|
|
@ -125,38 +76,12 @@ export const ErrorDisplay = ({
|
|||
</div>
|
||||
|
||||
{/* Expandable Details */}
|
||||
{isExpanded && hasDetails && (
|
||||
{isExpanded && details && (
|
||||
<div className="mt-4 space-y-3 border-t border-red-200 pt-3">
|
||||
{details.code && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Error Code: </span>
|
||||
<span className="text-red-700">{details.code}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{details.cause && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Cause: </span>
|
||||
<span className="text-red-700">{details.cause}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{Object.keys(details.additional).length > 0 && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Additional Information:</span>
|
||||
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
|
||||
{Object.keys(details.additional).map(key => `${key}:\n${details.additional[key]}`).join('\n')}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{/* {details.stack && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Stack Trace:</span>
|
||||
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
|
||||
{details.stack}
|
||||
</pre>
|
||||
</div>
|
||||
)} */}
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Full Error: </span>
|
||||
<pre className="text-red-700">{details}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { FeatureName, featureNames, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js'
|
||||
import { useConfigState, useService } from '../util/services.js'
|
||||
import ErrorBoundary from './ErrorBoundary.js'
|
||||
import { VoidSelectBox } from './inputs.js'
|
||||
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'
|
||||
|
||||
|
||||
|
||||
|
||||
export const ModelSelectionOfFeature = ({ featureName }: { featureName: FeatureName }) => {
|
||||
|
||||
const voidConfigService = useService('configStateService')
|
||||
const voidConfigState = useConfigState()
|
||||
|
||||
const modelOptions: { text: string, value: [string, string] }[] = []
|
||||
|
||||
modelOptions.push({ text: 'Select a Provider', value: ['MyProvider', 'MyModel'] })
|
||||
|
||||
for (const providerName of providerNames) {
|
||||
const providerConfig = voidConfigState[providerName]
|
||||
if (providerConfig.enabled !== 'true') continue
|
||||
providerConfig.models?.forEach(model => {
|
||||
modelOptions.push({ text: `${providerName} - ${model}`, value: [providerName, model] })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
return <>
|
||||
<h2>{featureName}</h2>
|
||||
{
|
||||
<VoidSelectBox
|
||||
options={modelOptions}
|
||||
onChangeSelection={(newVal) => { voidConfigService.setModelSelectionOfFeature(featureName, { providerName: newVal[0] as ProviderName, modelName: newVal[1] }) }}
|
||||
// we are responsible for setting the initial state here
|
||||
onCreateInstance={useCallback((instance: SelectBox) => {
|
||||
const updateState = () => {
|
||||
const settingsAtProvider = voidConfigService.state.modelSelectionOfFeature[featureName]
|
||||
const index = modelOptions.findIndex(v => v.value[0] === settingsAtProvider?.providerName && v.value[1] === settingsAtProvider?.modelName)
|
||||
if (index !== -1)
|
||||
instance.select(index)
|
||||
}
|
||||
updateState()
|
||||
const disposable = voidConfigService.onDidGetInitState(updateState)
|
||||
return [disposable]
|
||||
}, [voidConfigService, modelOptions, featureName])}
|
||||
/>}
|
||||
|
||||
{/* <h1>Settings - {featureName}</h1> */}
|
||||
{/* {models.map(([providerName, model], i) => <p key={i}>{providerName} - {model}</p>)} */}
|
||||
</>
|
||||
}
|
||||
|
||||
export const ModelSelectionSettings = () => {
|
||||
return <>
|
||||
{featureNames.map(featureName => <ModelSelectionOfFeature
|
||||
key={featureName}
|
||||
featureName={featureName}
|
||||
/>)}
|
||||
</>
|
||||
}
|
||||
|
||||
|
|
@ -15,8 +15,8 @@ import { useSidebarState } from '../util/services.js';
|
|||
import '../styles.css'
|
||||
import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
import { SidebarChat } from './SidebarChat.js';
|
||||
import { SidebarModelSettings } from './SidebarModelSettings.js';
|
||||
import { SidebarProviderSettings } from './SidebarProviderSettings.js';
|
||||
import { ModelSelectionSettings } from './ModelSelectionSettings.js';
|
||||
import { VoidProviderSettings } from './VoidProviderSettings.js';
|
||||
import ErrorBoundary from './ErrorBoundary.js';
|
||||
|
||||
const Sidebar = () => {
|
||||
|
|
@ -43,15 +43,15 @@ const Sidebar = () => {
|
|||
<ErrorBoundary>
|
||||
<SidebarChat />
|
||||
</ErrorBoundary>
|
||||
|
||||
<ErrorBoundary>
|
||||
<ModelSelectionSettings />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div className={`${tab === 'settings' ? '' : 'hidden'}`}>
|
||||
<ErrorBoundary>
|
||||
<SidebarModelSettings />
|
||||
</ErrorBoundary>
|
||||
--------
|
||||
<ErrorBoundary>
|
||||
<SidebarProviderSettings />
|
||||
<VoidProviderSettings />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { URI } from '../../../../../../../base/common/uri.js';
|
|||
import { EndOfLinePreference } from '../../../../../../../editor/common/model.js';
|
||||
import { IDisposable } from '../../../../../../../base/common/lifecycle.js';
|
||||
import { ErrorDisplay } from './ErrorDisplay.js';
|
||||
import { LLMMessageServiceParams } from '../../../../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { LLMMessageServiceParams, OnError } from '../../../../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { getCmdKey } from '../../../getCmdKey.js'
|
||||
import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js';
|
||||
import { VoidInputBox } from './inputs.js';
|
||||
|
|
@ -158,7 +158,7 @@ export const SidebarChat = () => {
|
|||
const [isLoading, setIsLoading] = useState(false)
|
||||
const latestRequestIdRef = useRef<string | null>(null)
|
||||
|
||||
const [latestError, setLatestError] = useState<Error | string | null>(null)
|
||||
const [latestError, setLatestError] = useState<Parameters<OnError>[0] | null>(null)
|
||||
|
||||
const sendLLMMessageService = useService('sendLLMMessageService')
|
||||
|
||||
|
|
@ -195,6 +195,11 @@ export const SidebarChat = () => {
|
|||
|
||||
// send message to LLM
|
||||
setIsLoading(true) // must come before message is sent so onError will work
|
||||
setLatestError(null)
|
||||
if (inputBoxRef.current) {
|
||||
inputBoxRef.current.value = ''; // this triggers onDidChangeText
|
||||
inputBoxRef.current.blur();
|
||||
}
|
||||
|
||||
const object: LLMMessageServiceParams = {
|
||||
logging: { loggingName: 'Chat' },
|
||||
|
|
@ -209,8 +214,8 @@ export const SidebarChat = () => {
|
|||
setMessageStream(null)
|
||||
setIsLoading(false)
|
||||
},
|
||||
onError: ({ error }) => {
|
||||
console.log('chat: running error', error)
|
||||
onError: ({ message, fullError }) => {
|
||||
console.log('chat: running error', message, fullError)
|
||||
|
||||
// add assistant's message to chat history, and clear selection
|
||||
let content = messageStream ?? ''; // just use the current content
|
||||
|
|
@ -220,7 +225,7 @@ export const SidebarChat = () => {
|
|||
setMessageStream('')
|
||||
setIsLoading(false)
|
||||
|
||||
setLatestError(error)
|
||||
setLatestError({ message, fullError })
|
||||
},
|
||||
featureName: 'Ctrl+L',
|
||||
|
||||
|
|
@ -229,13 +234,7 @@ export const SidebarChat = () => {
|
|||
const latestRequestId = sendLLMMessageService.sendLLMMessage(object)
|
||||
latestRequestIdRef.current = latestRequestId
|
||||
|
||||
|
||||
if (inputBoxRef.current) {
|
||||
inputBoxRef.current.value = ''; // this triggers onDidChangeText
|
||||
inputBoxRef.current.blur();
|
||||
}
|
||||
threadsStateService.setStaging([]) // clear staging
|
||||
setLatestError(null)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +281,8 @@ export const SidebarChat = () => {
|
|||
{/* error message */}
|
||||
{latestError === null ? null :
|
||||
<ErrorDisplay
|
||||
error={latestError}
|
||||
message={latestError.message}
|
||||
fullError={latestError.fullError}
|
||||
onDismiss={() => { setLatestError(null) }}
|
||||
/>}
|
||||
|
||||
|
|
@ -300,7 +300,7 @@ export const SidebarChat = () => {
|
|||
<VoidInputBox
|
||||
placeholder={`${getCmdKey()}+L to select`}
|
||||
onChangeText={onChangeText}
|
||||
onCreateInstance={inputBoxRef}
|
||||
inputBoxRef={inputBoxRef}
|
||||
multiline={true}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { FeatureName, featureNames, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js'
|
||||
import { useConfigState, useService } from '../util/services.js'
|
||||
import ErrorBoundary from './ErrorBoundary.js'
|
||||
import { VoidSelectBox } from './inputs.js'
|
||||
import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'
|
||||
|
||||
|
||||
|
||||
|
||||
export const SidebarModelSettingsForFeature = ({ featureName }: { featureName: FeatureName }) => {
|
||||
|
||||
const voidConfigService = useService('configStateService')
|
||||
const voidConfigState = useConfigState()
|
||||
|
||||
const models: [string, string][] = []
|
||||
for (const providerName of providerNames) {
|
||||
const providerConfig = voidConfigState[providerName]
|
||||
if (providerConfig.enabled !== 'true') continue
|
||||
providerConfig.models?.forEach(model => {
|
||||
models.push([providerName, model])
|
||||
})
|
||||
}
|
||||
|
||||
const wasEmpty = models.length === 0
|
||||
if (wasEmpty) {
|
||||
models.push(['Provider', 'Model'])
|
||||
}
|
||||
|
||||
const selectBoxRef = useRef<SelectBox | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
// this is really just to sync the state on initial mount, when init value hasn't been set yet
|
||||
let synced = false
|
||||
const syncStateOnMount = () => {
|
||||
if (!selectBoxRef.current) return
|
||||
if (synced) return
|
||||
synced = true
|
||||
const settingsAtProvider = voidConfigService.state.modelSelectionOfFeature[featureName]
|
||||
const index = models.findIndex(v => v[0] === settingsAtProvider?.providerName && v[1] === settingsAtProvider?.modelName)
|
||||
if (index !== -1)
|
||||
selectBoxRef.current.select(index)
|
||||
}
|
||||
syncStateOnMount()
|
||||
synced = false // sync the next time state changes (but not after that - the "current.value = ..." triggers a state change, causing an infinite loop!)
|
||||
const disposable = voidConfigService.onDidChangeState(syncStateOnMount)
|
||||
return () => disposable.dispose()
|
||||
}, [selectBoxRef, voidConfigService, models, featureName])
|
||||
|
||||
|
||||
|
||||
return <>
|
||||
<h2>{featureName}</h2>
|
||||
{
|
||||
<VoidSelectBox
|
||||
initVal={models[0]}
|
||||
options={wasEmpty ? [{ text: 'Please add a Provider!', value: models[0] }] : models.map(s => ({ text: s.join(' - '), value: s }))}
|
||||
onChangeSelection={(newVal) => { voidConfigService.setModelSelectionOfFeature(featureName, { providerName: newVal[0] as ProviderName, modelName: newVal[1] }) }}
|
||||
selectBoxRef={selectBoxRef}
|
||||
/>}
|
||||
|
||||
{/* <h1>Settings - {featureName}</h1> */}
|
||||
{/* {models.map(([providerName, model], i) => <p key={i}>{providerName} - {model}</p>)} */}
|
||||
</>
|
||||
}
|
||||
|
||||
export const SidebarModelSettings = () => {
|
||||
return <>
|
||||
{featureNames.map(featureName => <SidebarModelSettingsForFeature key={featureName} featureName={featureName} />)}
|
||||
</>
|
||||
}
|
||||
|
||||
|
|
@ -4,8 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { displayInfoOfSettingName, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js'
|
||||
import { VoidInputBox, VoidSelectBox } from './inputs.js'
|
||||
import { titleOfProviderName, displayInfoOfSettingName, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js'
|
||||
import { VoidInputBox } from './inputs.js'
|
||||
import { useConfigState, useService } from '../util/services.js'
|
||||
import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'
|
||||
import ErrorBoundary from './ErrorBoundary.js'
|
||||
|
|
@ -16,52 +16,39 @@ const Setting = ({ providerName, settingName }: { providerName: ProviderName, se
|
|||
const { title, type, placeholder } = displayInfoOfSettingName(providerName, settingName)
|
||||
const voidConfigService = useService('configStateService')
|
||||
|
||||
const instanceRef = useRef<InputBox | null>(null)
|
||||
|
||||
// set init val to the current state
|
||||
useEffect(() => {
|
||||
// this is really just to sync the state on initial mount, when init value hasn't been set yet
|
||||
let synced = false
|
||||
const syncStateOnMount = () => {
|
||||
if (!instanceRef.current) return
|
||||
if (synced) return
|
||||
synced = true
|
||||
|
||||
const settingsAtProvider = voidConfigService.state.settingsOfProvider[providerName];
|
||||
|
||||
// @ts-ignore
|
||||
const stateVal = settingsAtProvider[settingName]
|
||||
|
||||
if (instanceRef.current.value !== stateVal) {
|
||||
instanceRef.current.value = stateVal // triggers onChangeText
|
||||
}
|
||||
}
|
||||
syncStateOnMount()
|
||||
synced = false // sync the next time state changes (but not after that - the "current.value = ..." triggers a state change, causing an infinite loop!)
|
||||
const disposable = voidConfigService.onDidChangeState(syncStateOnMount)
|
||||
return () => disposable.dispose()
|
||||
}, [instanceRef, voidConfigService, providerName, settingName])
|
||||
|
||||
return <><ErrorBoundary>
|
||||
<h2>{title}</h2>
|
||||
{<VoidInputBox
|
||||
<label>{title}</label>
|
||||
<VoidInputBox
|
||||
placeholder={placeholder}
|
||||
onChangeText={useCallback((newVal) => {
|
||||
voidConfigService.setSettingOfProvider(providerName, settingName, newVal)
|
||||
}, [voidConfigService, providerName, settingName])
|
||||
}
|
||||
onCreateInstance={instanceRef}
|
||||
}, [voidConfigService, providerName, settingName])}
|
||||
|
||||
// we are responsible for setting the initial value here
|
||||
onCreateInstance={useCallback((instance: InputBox) => {
|
||||
const updateInstanceState = () => {
|
||||
const settingsAtProvider = voidConfigService.state.settingsOfProvider[providerName];
|
||||
// @ts-ignore
|
||||
const stateVal = settingsAtProvider[settingName]
|
||||
instance.value = stateVal
|
||||
}
|
||||
updateInstanceState()
|
||||
const disposable = voidConfigService.onDidGetInitState(updateInstanceState)
|
||||
return [disposable]
|
||||
}, [voidConfigService, providerName, settingName])}
|
||||
multiline={false}
|
||||
/>}
|
||||
/>
|
||||
</ErrorBoundary></>
|
||||
|
||||
}
|
||||
|
||||
|
||||
const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => {
|
||||
const voidConfigState = useConfigState()
|
||||
const { models, ...others } = voidConfigState[providerName]
|
||||
return <>
|
||||
<h1>{providerName}</h1>
|
||||
<h1 className='text-xl'>{titleOfProviderName(providerName)}</h1>
|
||||
{/* settings besides models (e.g. api key) */}
|
||||
{Object.keys(others).map((settingName, i) => {
|
||||
return <Setting key={settingName} providerName={providerName} settingName={settingName} />
|
||||
|
|
@ -70,7 +57,7 @@ const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) =
|
|||
}
|
||||
|
||||
|
||||
export const SidebarProviderSettings = () => {
|
||||
export const VoidProviderSettings = () => {
|
||||
|
||||
return <>
|
||||
{providerNames.map(providerName =>
|
||||
|
|
@ -36,9 +36,10 @@ export const WidgetComponent = <CtorParams extends any[], Instance>({ ctor, prop
|
|||
|
||||
|
||||
|
||||
export const VoidInputBox = ({ onChangeText, onCreateInstance, placeholder, multiline }: {
|
||||
export const VoidInputBox = ({ onChangeText, onCreateInstance, inputBoxRef, placeholder, multiline }: {
|
||||
onChangeText: (value: string) => void;
|
||||
onCreateInstance?: { current: InputBox | null } | ((instance: InputBox) => void | IDisposable[]);
|
||||
onCreateInstance?: (instance: InputBox) => void | IDisposable[];
|
||||
inputBoxRef?: { current: InputBox | null };
|
||||
placeholder: string;
|
||||
multiline: boolean;
|
||||
}) => {
|
||||
|
|
@ -71,15 +72,15 @@ export const VoidInputBox = ({ onChangeText, onCreateInstance, placeholder, mult
|
|||
disposables.push(
|
||||
instance.onDidChange((newText) => onChangeText(newText))
|
||||
)
|
||||
if (typeof onCreateInstance === 'function') {
|
||||
if (onCreateInstance) {
|
||||
const ds = onCreateInstance(instance) ?? []
|
||||
disposables.push(...ds)
|
||||
}
|
||||
if (typeof onCreateInstance === 'object') {
|
||||
onCreateInstance.current = instance
|
||||
}
|
||||
if (inputBoxRef)
|
||||
inputBoxRef.current = instance;
|
||||
|
||||
return disposables
|
||||
}, [onChangeText, onCreateInstance])
|
||||
}, [onChangeText, onCreateInstance, inputBoxRef])
|
||||
}
|
||||
/>
|
||||
};
|
||||
|
|
@ -87,11 +88,11 @@ export const VoidInputBox = ({ onChangeText, onCreateInstance, placeholder, mult
|
|||
|
||||
|
||||
|
||||
export const VoidSelectBox = <T,>({ onChangeSelection, initVal, selectBoxRef, options }: {
|
||||
initVal: T;
|
||||
selectBoxRef: React.MutableRefObject<SelectBox | null>;
|
||||
options: readonly { text: string, value: T }[];
|
||||
export const VoidSelectBox = <T,>({ onChangeSelection, onCreateInstance, selectBoxRef, options }: {
|
||||
onChangeSelection: (value: T) => void;
|
||||
onCreateInstance?: ((instance: SelectBox) => void | IDisposable[]);
|
||||
selectBoxRef?: React.MutableRefObject<SelectBox | null>;
|
||||
options: readonly { text: string, value: T }[];
|
||||
}) => {
|
||||
const contextViewProvider = useService('contextViewService');
|
||||
|
||||
|
|
@ -101,14 +102,14 @@ export const VoidSelectBox = <T,>({ onChangeSelection, initVal, selectBoxRef, op
|
|||
ctor={SelectBox}
|
||||
propsFn={useCallback((container) => {
|
||||
containerRef.current = container
|
||||
const defaultIndex = options.findIndex(opt => opt.value === initVal);
|
||||
const defaultIndex = 0;
|
||||
return [
|
||||
options.map(opt => ({ text: opt.text })),
|
||||
defaultIndex,
|
||||
contextViewProvider,
|
||||
defaultSelectBoxStyles
|
||||
] as const;
|
||||
}, [containerRef, options, initVal, contextViewProvider])}
|
||||
}, [containerRef, options, contextViewProvider])}
|
||||
|
||||
dispose={useCallback((instance: SelectBox) => {
|
||||
instance.dispose();
|
||||
|
|
@ -117,16 +118,24 @@ export const VoidSelectBox = <T,>({ onChangeSelection, initVal, selectBoxRef, op
|
|||
}, [containerRef])}
|
||||
|
||||
onCreateInstance={useCallback((instance: SelectBox) => {
|
||||
selectBoxRef.current = instance;
|
||||
if (containerRef.current) instance.render(containerRef.current)
|
||||
const disposables = [
|
||||
instance.onDidSelect(e => {
|
||||
console.log('e.selected', JSON.stringify(e));
|
||||
onChangeSelection(options[e.index].value);
|
||||
})
|
||||
];
|
||||
const disposables: IDisposable[] = []
|
||||
|
||||
if (containerRef.current)
|
||||
instance.render(containerRef.current)
|
||||
|
||||
disposables.push(
|
||||
instance.onDidSelect(e => { onChangeSelection(options[e.index].value); })
|
||||
)
|
||||
|
||||
if (onCreateInstance) {
|
||||
const ds = onCreateInstance(instance) ?? []
|
||||
disposables.push(...ds)
|
||||
}
|
||||
if (selectBoxRef)
|
||||
selectBoxRef.current = instance;
|
||||
|
||||
return disposables;
|
||||
}, [containerRef, selectBoxRef, options, onChangeSelection])}
|
||||
}, [containerRef, onChangeSelection, options, onCreateInstance, selectBoxRef])}
|
||||
|
||||
/>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -670,10 +670,10 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
resolve(newAutocompletion.insertText)
|
||||
|
||||
},
|
||||
onError: ({ error }) => {
|
||||
onError: ({ message }) => {
|
||||
newAutocompletion.endTime = Date.now()
|
||||
newAutocompletion.status = 'error'
|
||||
reject(error)
|
||||
reject(message)
|
||||
},
|
||||
featureName: 'Autocomplete',
|
||||
range: { startLineNumber: position.lineNumber, startColumn: position.column, endLineNumber: position.lineNumber, endColumn: position.column },
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@
|
|||
// register keybinds
|
||||
import './registerActions.js'
|
||||
|
||||
// register Settings
|
||||
import '../../../../platform/void/common/voidConfigService.js' // TODO move this to platform
|
||||
|
||||
// register inline diffs
|
||||
import './registerInlineDiffs.js'
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue