lint error UI + misc

This commit is contained in:
Andrew Pareles 2025-04-14 12:31:31 -07:00
parent a80883c10f
commit 2ea20243a8
3 changed files with 66 additions and 20 deletions

View file

@ -304,12 +304,6 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, ..
return <p>{contents}</p>
}
if (t.type === "html") {
const contents = t.raw
if (inPTag) return <span className='block'>{contents}</span>
return <p>{contents}</p>
}
if (t.type === "text" || t.type === "escape") {
return <span>{t.raw}</span>
}
@ -324,7 +318,7 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, ..
onClick={() => { window.open(t.href) }}
href={t.href}
title={t.title ?? undefined}
className='underline cursor-pointer hover:brightness-90 transition-all duration-200'
className='underline cursor-pointer hover:brightness-90 transition-all duration-200 text-void-fg-2'
>
{t.text}
</a>
@ -349,7 +343,7 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, ..
}
// inline code
if (t.type === "codespan") {
if (t.type === "codespan" || t.type === "html") {
if (options.isLinkDetectionEnabled && chatMessageLocation) {
return <CodespanWithLink

View file

@ -26,7 +26,6 @@ import { LintErrorItem, ToolCallParams, ToolNameWithApproval } from '../../../..
import { ApplyButtonsHTML, CopyButton, IconShell1, JumpToFileButton, JumpToTerminalButton, StatusIndicator, StatusIndicatorForApplyButton, useApplyButtonState } from '../markdown/ApplyBlockHoverButtons.js';
import { IsRunningType } from '../../../chatThreadService.js';
import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg, rejectBg, rejectBorder } from '../../../../common/helpers/colors.js';
import { PlacesType } from 'react-tooltip';
import { ToolName, toolNames } from '../../../../common/prompt/prompts.js';
import { error } from 'console';
import { RawToolCallObj } from '../../../../common/sendLLMMessageTypes.js';
@ -662,7 +661,8 @@ type ToolHeaderParams = {
children?: React.ReactNode;
bottomChildren?: React.ReactNode;
onClick?: () => void;
isOpen?: boolean,
isOpen?: boolean;
className?: string;
}
const ToolHeaderWrapper = ({
@ -679,7 +679,7 @@ const ToolHeaderWrapper = ({
isOpen,
isRejected,
className, // applies to the main content
}: ToolHeaderParams & { className?: string }) => {
}: ToolHeaderParams) => {
const [isOpen_, setIsOpen] = useState(false);
const isExpanded = isOpen !== undefined ? isOpen : isOpen_
@ -1176,7 +1176,8 @@ const titleOfToolName = {
'create_file_or_folder': { done: `Created`, proposed: `Create`, running: loadingTitleWrapper(`Creating`) },
'delete_file_or_folder': { done: `Deleted`, proposed: `Delete`, running: loadingTitleWrapper(`Deleting`) },
'edit_file': { done: `Edited file`, proposed: 'Edit file', running: loadingTitleWrapper('Editing file') },
'run_terminal_command': { done: `Ran terminal`, proposed: 'Run terminal', running: loadingTitleWrapper('Running terminal') }
'run_terminal_command': { done: `Ran terminal`, proposed: 'Run terminal', running: loadingTitleWrapper('Running terminal') },
'read_lint_errors': { done: `Read lint errors`, proposed: 'Read lint errors', running: loadingTitleWrapper('Reading lint errors') },
} as const satisfies Record<ToolName, { done: any, proposed: any, running: any }>
const getTitle = (toolMessage: Pick<ChatMessage & { role: 'tool' }, 'name' | 'type'>): React.ReactNode => {
@ -1343,6 +1344,15 @@ const EditToolChildren = ({ uri, changeDescription }: { uri: URI | undefined, ch
</div>
}
const LintErrorChildren = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => {
return <div className="text-xs text-void-fg-4 opacity-80 border-l-2 border-void-warning px-2 py-0.5 flex flex-col gap-0.5 overflow-x-auto whitespace-nowrap">
{lintErrors.map((error, i) => (
<div key={i}>Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}</div>
))}
</div>
}
const EditToolLintErrors = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => {
if (lintErrors.length === 0) return null;
@ -1351,12 +1361,8 @@ const EditToolLintErrors = ({ lintErrors }: { lintErrors: LintErrorItem[] }) =>
return (
<div className="w-full px-2">
<ToolHeaderWrapper className='!border-t-0' title={'Lint errors'} desc1={''} isOpen={isOpen} onClick={() => { setIsOpen(o => !o) }} >
<div className="text-xs text-void-fg-4 opacity-80 border-l-2 border-void-warning px-2 py-0.5 flex flex-col gap-0.5 overflow-x-auto whitespace-nowrap">
{lintErrors.map((error, i) => (
<div key={i}>Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}</div>
))}
</div>
<ToolHeaderWrapper className='!border-t-0 !pt-0' title={'Lint errors'} desc1={''} isOpen={isOpen} onClick={() => { setIsOpen(o => !o) }} >
<LintErrorChildren lintErrors={lintErrors} />
</ToolHeaderWrapper>
</div>
@ -1427,6 +1433,13 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
const isError = toolMessage.type === 'tool_error'
const componentParams: ToolHeaderParams = { title, desc1, isError, icon }
if (toolMessage.params.startLine !== null || toolMessage.params.endLine !== null) {
const start = toolMessage.params.startLine === null ? `start` : `${toolMessage.params.startLine}`
const end = toolMessage.params.endLine === null ? `end` : `${toolMessage.params.endLine}`
const addStr = `(${start}-${end})`
componentParams.title += ` ${addStr}`
}
if (toolMessage.type === 'success') {
const { params, result } = toolMessage
componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }
@ -1626,6 +1639,44 @@ const toolNameToComponent: { [T in ToolName]: { resultWrapper: ResultWrapper<T>,
}
},
'read_lint_errors': {
resultWrapper: ({ toolMessage }) => {
const accessor = useAccessor()
const commandService = accessor.get('ICommandService')
const title = getTitle(toolMessage)
const { uri } = toolMessage.params ?? {}
const desc1 = uri ? getBasename(uri.fsPath) : '';
const icon = null
if (toolMessage.type === 'tool_request') return null
if (toolMessage.type === 'rejected') return null // will never happen, not rejectable
if (toolMessage.type === 'running_now') return null // do not show running
const isError = toolMessage.type === 'tool_error'
const componentParams: ToolHeaderParams = { title, desc1, isError, icon }
if (toolMessage.type === 'success') {
const { params, result } = toolMessage
componentParams.onClick = () => { commandService.executeCommand('vscode.open', params.uri, { preview: true }) }
}
else if (toolMessage.type === 'tool_error') {
const { params, result } = toolMessage
if (params) componentParams.desc2 = <JumpToFileButton uri={params.uri} />
componentParams.children = <ToolChildrenWrapper>
<CodeChildren>
{result}
</CodeChildren>
</ToolChildrenWrapper>
}
return <ToolHeaderWrapper {...componentParams} />
},
},
// ---
'create_file_or_folder': {

View file

@ -143,8 +143,9 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName
providerName === 'gemini' ? 'Get your [API Key here](https://aistudio.google.com/apikey).' :
providerName === 'groq' ? 'Get your [API Key here](https://console.groq.com/keys).' :
providerName === 'xAI' ? 'Get your [API Key here](https://console.x.ai).' :
providerName === 'openAICompatible' ? undefined :
'',
providerName === 'mistral' ? 'Get your [API Key here](https://console.mistral.ai/api-keys).' :
providerName === 'openAICompatible' ? `Use any OpenAI-compatible endpoint (LM Studio, LiteLM, etc).` :
'',
isPasswordField: true,
}
}