diff --git a/.changeset/polite-errors-glow.md b/.changeset/polite-errors-glow.md
new file mode 100644
index 00000000000..bc37608e936
--- /dev/null
+++ b/.changeset/polite-errors-glow.md
@@ -0,0 +1,6 @@
+---
+"@kilocode/kilo-ui": patch
+"kilo-code": patch
+---
+
+Improve chat error styling in the VS Code extension.
diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/error-display-data-policy-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/error-display-data-policy-chromium-linux.png
new file mode 100644
index 00000000000..010968eb6a0
--- /dev/null
+++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/error-display-data-policy-chromium-linux.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e3050c782d797be7dad951b76199df2c87195415e699d3e8481bbf40ad0dfccf
+size 10073
diff --git a/packages/kilo-ui/src/components/card.css b/packages/kilo-ui/src/components/card.css
index 6ae4d7d2693..360d1028608 100644
--- a/packages/kilo-ui/src/components/card.css
+++ b/packages/kilo-ui/src/components/card.css
@@ -5,6 +5,13 @@
padding: 8px;
border: 1px solid var(--border-weak-base);
+ &[data-variant="error"] {
+ padding: 12px;
+ background-color: color-mix(in srgb, var(--surface-critical-strong) 10%, var(--surface-inset-base));
+ border-color: color-mix(in srgb, var(--border-critical-selected) 55%, var(--border-weaker-base));
+ color: var(--text-base);
+ }
+
&[data-variant="warning"] {
padding: 12px 14px;
}
diff --git a/packages/kilo-ui/src/components/error-details.css b/packages/kilo-ui/src/components/error-details.css
index 259150984e0..88f97120a2e 100644
--- a/packages/kilo-ui/src/components/error-details.css
+++ b/packages/kilo-ui/src/components/error-details.css
@@ -1,42 +1,92 @@
.error-card {
- padding-bottom: 0;
- background-color: var(--surface-critical-base);
+ gap: 8px;
+ padding-bottom: 12px;
+ --error-card-accent: var(--text-on-critical-base);
+}
+
+.error-card-body {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+}
+
+.error-card-body [data-component="icon"] {
+ color: var(--error-card-accent);
+ margin-top: 2px;
+}
+
+.error-card-message {
+ flex: 1;
+ min-width: 0;
+ color: var(--text-strong);
+ font-size: var(--font-size-base);
+ line-height: var(--line-height-large);
+ overflow-wrap: anywhere;
}
.error-card [data-component="collapsible"] {
- margin-top: 8px;
+ margin-top: 0;
+ padding-left: 24px;
}
-.error-details-trigger {
+.error-card .error-details-trigger[data-slot="collapsible-trigger"] {
display: inline-flex;
align-items: center;
- gap: 4px;
+ align-self: flex-start;
+ gap: 2px;
+ width: auto;
+ height: 22px;
font-size: var(--font-size-small);
+ font-weight: var(--font-weight-medium);
opacity: 0.85;
cursor: pointer;
background: none;
border: none;
- color: inherit;
- padding: 0;
+ border-radius: var(--radius-sm);
+ color: var(--error-card-accent);
+ padding: 0 6px;
}
-.error-details-trigger:hover {
+.error-card .error-details-trigger[data-slot="collapsible-trigger"]:hover {
+ background-color: color-mix(in srgb, var(--error-card-accent) 12%, transparent);
opacity: 1;
}
+.error-card .error-details-trigger[data-slot="collapsible-trigger"]:focus-visible {
+ background-color: color-mix(in srgb, var(--error-card-accent) 12%, transparent);
+ outline: 1px solid var(--border-focus);
+ outline-offset: 2px;
+}
+
+.error-card .error-details-trigger [data-slot="collapsible-arrow"] {
+ width: 16px;
+ height: 16px;
+ opacity: 1;
+}
+
+.error-card .error-details-trigger [data-slot="collapsible-arrow-icon"] {
+ color: currentColor;
+}
+
.error-details {
display: flex;
flex-direction: column;
- gap: 4px;
+ gap: 6px;
font-size: var(--font-size-small);
- margin-top: 4px;
+ margin-top: 8px;
}
.error-detail-pre {
margin: 0;
max-height: 120px;
overflow-y: auto;
+ background-color: color-mix(in srgb, var(--surface-inset-base) 82%, var(--background-base));
+ border: 1px solid color-mix(in srgb, var(--border-critical-base) 30%, var(--border-weaker-base));
+ border-radius: var(--radius-sm);
+ color: var(--text-base);
font-size: var(--font-size-small);
+ line-height: var(--line-height-large);
+ padding: 8px;
white-space: pre-wrap;
word-break: break-all;
flex: 1;
diff --git a/packages/kilo-ui/src/components/error-details.tsx b/packages/kilo-ui/src/components/error-details.tsx
index c720bd80f8c..1a306385865 100644
--- a/packages/kilo-ui/src/components/error-details.tsx
+++ b/packages/kilo-ui/src/components/error-details.tsx
@@ -11,7 +11,7 @@ export function ErrorDetails(props: ErrorDetailsProps) {
return (
)
}
diff --git a/packages/kilo-ui/src/styles/vscode-bridge.css b/packages/kilo-ui/src/styles/vscode-bridge.css
index 2c7bb14df3b..f73537c5c38 100644
--- a/packages/kilo-ui/src/styles/vscode-bridge.css
+++ b/packages/kilo-ui/src/styles/vscode-bridge.css
@@ -68,7 +68,7 @@ html[data-theme="kilo-vscode"] {
--surface-critical-base: var(--vscode-editorMarkerNavigationError-headerBackground);
--surface-critical-weak: var(--vscode-editorMarkerNavigationError-headerBackground);
- --surface-critical-strong: var(--vscode-charts-red);
+ --surface-critical-strong: var(--vscode-errorForeground, var(--vscode-charts-red));
--surface-info-base: var(--vscode-editorMarkerNavigationInfo-headerBackground);
--surface-info-weak: var(--vscode-editorMarkerNavigationInfo-headerBackground);
@@ -125,9 +125,9 @@ html[data-theme="kilo-vscode"] {
--text-on-success-base: var(--vscode-charts-green);
--text-on-success-weak: var(--vscode-charts-green);
--text-on-success-strong: var(--vscode-charts-green);
- --text-on-critical-base: var(--vscode-charts-red);
- --text-on-critical-weak: var(--vscode-charts-red);
- --text-on-critical-strong: var(--vscode-charts-red);
+ --text-on-critical-base: var(--vscode-errorForeground, var(--vscode-charts-red));
+ --text-on-critical-weak: var(--vscode-errorForeground, var(--vscode-charts-red));
+ --text-on-critical-strong: var(--vscode-errorForeground, var(--vscode-charts-red));
--text-on-warning-base: var(--vscode-charts-yellow);
--text-on-warning-weak: var(--vscode-charts-yellow);
--text-on-warning-strong: var(--vscode-charts-yellow);
@@ -192,9 +192,9 @@ html[data-theme="kilo-vscode"] {
--border-warning-base: var(--vscode-charts-yellow);
--border-warning-hover: var(--vscode-charts-yellow);
--border-warning-selected: var(--vscode-charts-yellow);
- --border-critical-base: var(--vscode-charts-red);
- --border-critical-hover: var(--vscode-charts-red);
- --border-critical-selected: var(--vscode-charts-red);
+ --border-critical-base: var(--vscode-inputValidation-errorBorder, var(--vscode-errorForeground, var(--vscode-charts-red)));
+ --border-critical-hover: var(--vscode-inputValidation-errorBorder, var(--vscode-errorForeground, var(--vscode-charts-red)));
+ --border-critical-selected: var(--vscode-inputValidation-errorBorder, var(--vscode-errorForeground, var(--vscode-charts-red)));
--border-info-base: var(--vscode-charts-blue);
--border-info-hover: var(--vscode-charts-blue);
--border-info-selected: var(--vscode-charts-blue);
@@ -230,9 +230,9 @@ html[data-theme="kilo-vscode"] {
--icon-warning-base: var(--vscode-charts-yellow);
--icon-warning-hover: var(--vscode-charts-yellow);
--icon-warning-active: var(--vscode-charts-yellow);
- --icon-critical-base: var(--vscode-charts-red);
- --icon-critical-hover: var(--vscode-charts-red);
- --icon-critical-active: var(--vscode-charts-red);
+ --icon-critical-base: var(--vscode-errorForeground, var(--vscode-charts-red));
+ --icon-critical-hover: var(--vscode-errorForeground, var(--vscode-charts-red));
+ --icon-critical-active: var(--vscode-errorForeground, var(--vscode-charts-red));
--icon-info-base: var(--vscode-charts-blue);
--icon-info-hover: var(--vscode-charts-blue);
--icon-info-active: var(--vscode-charts-blue);
@@ -248,9 +248,9 @@ html[data-theme="kilo-vscode"] {
--icon-on-warning-base: var(--vscode-charts-yellow);
--icon-on-warning-hover: var(--vscode-charts-yellow);
--icon-on-warning-selected: var(--vscode-charts-yellow);
- --icon-on-critical-base: var(--vscode-charts-red);
- --icon-on-critical-hover: var(--vscode-charts-red);
- --icon-on-critical-selected: var(--vscode-charts-red);
+ --icon-on-critical-base: var(--vscode-errorForeground, var(--vscode-charts-red));
+ --icon-on-critical-hover: var(--vscode-errorForeground, var(--vscode-charts-red));
+ --icon-on-critical-selected: var(--vscode-errorForeground, var(--vscode-charts-red));
--icon-on-info-base: var(--vscode-charts-blue);
--icon-on-info-hover: var(--vscode-charts-blue);
--icon-on-info-selected: var(--vscode-charts-blue);
@@ -282,7 +282,7 @@ html[data-theme="kilo-vscode"] {
--syntax-object: var(--vscode-editor-foreground);
--syntax-success: var(--vscode-charts-green);
--syntax-warning: var(--vscode-charts-yellow);
- --syntax-critical: var(--vscode-charts-red);
+ --syntax-critical: var(--vscode-errorForeground, var(--vscode-charts-red));
--syntax-info: var(--vscode-charts-blue);
--syntax-diff-add: var(--vscode-gitDecoration-addedResourceForeground);
--syntax-diff-delete: var(--vscode-gitDecoration-deletedResourceForeground);
diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/ErrorDisplay.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/ErrorDisplay.tsx
index ef600bccfc3..52ae08d88e6 100644
--- a/packages/kilo-vscode/webview-ui/src/components/chat/ErrorDisplay.tsx
+++ b/packages/kilo-vscode/webview-ui/src/components/chat/ErrorDisplay.tsx
@@ -3,6 +3,7 @@ import { Card } from "@kilocode/kilo-ui/card"
import { Collapsible } from "@kilocode/kilo-ui/collapsible"
import { useDialog } from "@kilocode/kilo-ui/context/dialog"
import { ErrorDetails } from "@kilocode/kilo-ui/error-details"
+import { Icon } from "@kilocode/kilo-ui/icon"
import { Button } from "@kilocode/kilo-ui/button"
import type { AssistantMessage } from "@kilocode/sdk/v2"
import { useLanguage } from "../../context/language"
@@ -62,8 +63,11 @@ export const ErrorDisplay: Component = (props) => {
return (
- {errorText()}
+
+
{t("error.details.show")}
diff --git a/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx
index fc539eda514..c259d71b826 100644
--- a/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx
+++ b/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx
@@ -8,8 +8,10 @@
*/
import type { Meta, StoryObj } from "storybook-solidjs-vite"
+import type { AssistantMessage } from "@kilocode/sdk/v2"
import { StoryProviders, defaultMockData, mockSessionValue } from "./StoryProviders"
import { ChatView } from "../components/chat/ChatView"
+import { ErrorDisplay } from "../components/chat/ErrorDisplay"
import { TaskHeader } from "../components/chat/TaskHeader"
import { QuestionDock } from "../components/chat/QuestionDock"
import { SuggestBar } from "../components/chat/SuggestBar"
@@ -77,6 +79,28 @@ const reviewSuggestion: SuggestionRequest = {
tool: { messageID: "asst-msg-002", callID: "call-suggest-001" },
}
+const policyMessage =
+ "No endpoints found matching your data policy (Free model training). Configure: https://openrouter.ai/settings/privacy"
+
+const policyError: NonNullable = {
+ name: "APIError",
+ data: {
+ message: policyMessage,
+ statusCode: 400,
+ isRetryable: false,
+ responseBody: JSON.stringify(
+ {
+ error: {
+ type: "Bad Request",
+ message: "Data collection is required for this model. Please enable data collection to use this model.",
+ },
+ },
+ null,
+ 2,
+ ),
+ },
+}
+
// ---------------------------------------------------------------------------
// Meta
// ---------------------------------------------------------------------------
@@ -232,6 +256,17 @@ export const SuggestBarReview: Story = {
),
}
+export const ErrorDisplayDataPolicy: Story = {
+ name: "ErrorDisplay — data policy",
+ render: () => (
+
+
+
+
+
+ ),
+}
+
const toolUserID = "user-msg-spacing-001"
const toolAssistantID = "asst-msg-spacing-001"
const queuedUserID = "user-msg-spacing-002"