summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-12-16 17:46:25 -0600
committerAdam <[email protected]>2025-12-17 03:47:48 -0600
commit0c7a297b1d3d9cdf8a060c4ed75160152da2b981 (patch)
tree7671287f7bf81b7af40428152cbeb0074e908770
parent9b1f9007c30e8877fc05a331faa40944440e34cb (diff)
downloadopencode-0c7a297b1d3d9cdf8a060c4ed75160152da2b981.tar.gz
opencode-0c7a297b1d3d9cdf8a060c4ed75160152da2b981.zip
feat(desktop): lsp diagnostics displayed
-rw-r--r--packages/ui/src/components/message-part.css36
-rw-r--r--packages/ui/src/components/message-part.tsx44
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>
)
},