diff options
| author | Adam <[email protected]> | 2025-12-16 17:46:25 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-12-17 03:47:48 -0600 |
| commit | 0c7a297b1d3d9cdf8a060c4ed75160152da2b981 (patch) | |
| tree | 7671287f7bf81b7af40428152cbeb0074e908770 | |
| parent | 9b1f9007c30e8877fc05a331faa40944440e34cb (diff) | |
| download | opencode-0c7a297b1d3d9cdf8a060c4ed75160152da2b981.tar.gz opencode-0c7a297b1d3d9cdf8a060c4ed75160152da2b981.zip | |
feat(desktop): lsp diagnostics displayed
| -rw-r--r-- | packages/ui/src/components/message-part.css | 36 | ||||
| -rw-r--r-- | packages/ui/src/components/message-part.tsx | 44 |
2 files changed, 79 insertions, 1 deletions
diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index 914202c45..b79ac2894 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -272,3 +272,39 @@ } } } + +[data-component="diagnostics"] { + display: flex; + flex-direction: column; + gap: 4px; + padding: 8px 12px; + background-color: var(--surface-critical-weak); + border-top: 1px solid var(--border-critical-base); + + [data-slot="diagnostic"] { + display: flex; + align-items: baseline; + gap: 6px; + font-family: var(--font-family-mono); + font-size: var(--font-size-small); + line-height: var(--line-height-large); + } + + [data-slot="diagnostic-label"] { + color: var(--text-on-critical-base); + font-weight: var(--font-weight-medium); + text-transform: uppercase; + letter-spacing: -0.5px; + flex-shrink: 0; + } + + [data-slot="diagnostic-location"] { + color: var(--text-on-critical-weak); + flex-shrink: 0; + } + + [data-slot="diagnostic-message"] { + color: var(--text-on-critical-base); + word-break: break-word; + } +} diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index b838bebc2..186b52cf3 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -1,4 +1,4 @@ -import { Component, createMemo, For, Match, Show, Switch } from "solid-js" +import { Component, createMemo, For, Match, Show, Switch, type JSX } from "solid-js" import { Dynamic } from "solid-js/web" import { AssistantMessage, @@ -22,6 +22,44 @@ import { Markdown } from "./markdown" import { getDirectory as _getDirectory, getFilename } from "@opencode-ai/util/path" import { checksum } from "@opencode-ai/util/encode" +interface Diagnostic { + range: { + start: { line: number; character: number } + end: { line: number; character: number } + } + message: string + severity?: number +} + +function getDiagnostics( + diagnosticsByFile: Record<string, Diagnostic[]> | undefined, + filePath: string | undefined, +): Diagnostic[] { + if (!diagnosticsByFile || !filePath) return [] + const diagnostics = diagnosticsByFile[filePath] ?? [] + return diagnostics.filter((d) => d.severity === 1).slice(0, 3) +} + +function DiagnosticsDisplay(props: { diagnostics: Diagnostic[] }): JSX.Element { + return ( + <Show when={props.diagnostics.length > 0}> + <div data-component="diagnostics"> + <For each={props.diagnostics}> + {(diagnostic) => ( + <div data-slot="diagnostic"> + <span data-slot="diagnostic-label">Error</span> + <span data-slot="diagnostic-location"> + [{diagnostic.range.start.line + 1}:{diagnostic.range.start.character + 1}] + </span> + <span data-slot="diagnostic-message">{diagnostic.message}</span> + </div> + )} + </For> + </div> + </Show> + ) +} + export interface MessageProps { message: MessageType parts: PartType[] @@ -444,6 +482,7 @@ ToolRegistry.register({ name: "edit", render(props) { const diffComponent = useDiffComponent() + const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath)) return ( <BasicTool defaultOpen @@ -482,6 +521,7 @@ ToolRegistry.register({ /> </div> </Show> + <DiagnosticsDisplay diagnostics={diagnostics()} /> </BasicTool> ) }, @@ -491,6 +531,7 @@ ToolRegistry.register({ name: "write", render(props) { const codeComponent = useCodeComponent() + const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath)) return ( <BasicTool defaultOpen @@ -523,6 +564,7 @@ ToolRegistry.register({ /> </div> </Show> + <DiagnosticsDisplay diagnostics={diagnostics()} /> </BasicTool> ) }, |
