Merge pull request #6613 from Kilo-Org/feat/reasoning-block-styling

feat(kilo-ui): improve reasoning block styling (#6254)
This commit is contained in:
Igor Šćekić
2026-03-04 17:06:50 +01:00
committed by GitHub
21 changed files with 194 additions and 1 deletions
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "موصى به",
"dialog.provider.kilo.note": "الوصول إلى أكثر من 500 نموذج ذكاء اصطناعي",
// Reasoning block label
"ui.reasoning.label": "الاستدلال",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "Recomendados",
"dialog.provider.kilo.note": "Acesso a mais de 500 modelos de IA",
// Reasoning block label
"ui.reasoning.label": "Raciocínio",
}
+3
View File
@@ -16,4 +16,7 @@ export const dict = {
"desktop.menu.reloadWebview": "Ponovno učitavanje webview-a",
"desktop.updater.installFailed.message": "Neuspjelo instaliranje ažuriranja",
"desktop.cli.installed.message": "CLI je instaliran u {{path}}\n\nRestartuj terminal da bi koristio komandu 'kilo'.",
// Reasoning block label
"ui.reasoning.label": "Rezonovanje",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "Anbefalede",
"dialog.provider.kilo.note": "Adgang til 500+ AI-modeller",
// Reasoning block label
"ui.reasoning.label": "Ræsonnement",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "Empfohlen",
"dialog.provider.kilo.note": "Zugriff auf 500+ KI-Modelle",
// Reasoning block label
"ui.reasoning.label": "Denken",
}
+3
View File
@@ -14,6 +14,9 @@ export const dict = {
"dialog.provider.group.recommended": "Recommended",
"dialog.provider.kilo.note": "Access 500+ AI models",
// Reasoning block label
"ui.reasoning.label": "Reasoning",
// Add any upstream overrides below this line
// Example: "some.upstream.key": "Kilo-specific override value",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "Recomendados",
"dialog.provider.kilo.note": "Acceso a más de 500 modelos de IA",
// Reasoning block label
"ui.reasoning.label": "Razonamiento",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "Recommandés",
"dialog.provider.kilo.note": "Accès à plus de 500 modèles d'IA",
// Reasoning block label
"ui.reasoning.label": "Raisonnement",
}
+3
View File
@@ -10,4 +10,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "おすすめ",
"dialog.provider.kilo.note": "500以上のAIモデルにアクセス",
// Reasoning block label
"ui.reasoning.label": "推論",
}
+3
View File
@@ -10,4 +10,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "추천",
"dialog.provider.kilo.note": "500개 이상의 AI 모델 이용 가능",
// Reasoning block label
"ui.reasoning.label": "추론",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "Anbefalt",
"dialog.provider.kilo.note": "Tilgang til 500+ AI-modeller",
// Reasoning block label
"ui.reasoning.label": "Resonnement",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "Polecane",
"dialog.provider.kilo.note": "Dostęp do ponad 500 modeli AI",
// Reasoning block label
"ui.reasoning.label": "Rozumowanie",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "Рекомендуемые",
"dialog.provider.kilo.note": "Доступ к 500+ моделям ИИ",
// Reasoning block label
"ui.reasoning.label": "Рассуждение",
}
+3
View File
@@ -11,4 +11,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "แนะนำ",
"dialog.provider.kilo.note": "เข้าถึงโมเดล AI มากกว่า 500 รายการ",
// Reasoning block label
"ui.reasoning.label": "การให้เหตุผล",
}
+3
View File
@@ -9,4 +9,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "推荐",
"dialog.provider.kilo.note": "访问 500+ AI 模型",
// Reasoning block label
"ui.reasoning.label": "推理",
}
+3
View File
@@ -9,4 +9,7 @@ export const dict = {
// Provider dialog translations
"dialog.provider.group.recommended": "推薦",
"dialog.provider.kilo.note": "存取 500+ AI 模型",
// Reasoning block label
"ui.reasoning.label": "推理",
}
@@ -117,3 +117,64 @@ html[data-theme="kilo-vscode"] [data-component="bash-output"] {
[data-component="question-answers"] {
padding: 8px;
}
/* Reasoning block: visually distinct from normal agent output */
[data-component="reasoning-part"] {
width: 100%;
border-left: 2px solid var(--border-weak-base);
border-radius: var(--radius-sm);
overflow: hidden;
[data-component="collapsible"].tool-collapsible {
gap: 0;
}
[data-slot="collapsible-trigger"] {
height: 28px;
padding: 0 8px;
gap: 6px;
background: var(--surface-inset-base);
border-radius: 0;
cursor: pointer;
}
[data-slot="reasoning-header"] {
display: flex;
flex: 1 1 auto;
align-items: center;
gap: 6px;
color: var(--text-weaker);
font-size: var(--font-size-small);
font-weight: var(--font-weight-medium);
min-width: 0;
[data-component="icon"] {
flex-shrink: 0;
color: var(--icon-weak-base);
}
}
[data-slot="reasoning-label"] {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
[data-slot="reasoning-content"] {
padding: 8px 10px;
background: var(--surface-inset-base);
[data-component="markdown"] {
margin-top: 0;
font-style: italic;
font-size: var(--font-size-small);
color: var(--text-weak);
opacity: 0.9;
strong,
b {
color: var(--text-weak);
}
}
}
}
@@ -1 +1,41 @@
export * from "@opencode-ai/ui/message-part"
import { Show, createSignal } from "solid-js"
import { PART_MAPPING } from "@opencode-ai/ui/message-part"
import { Collapsible } from "@opencode-ai/ui/collapsible"
import { Icon } from "@opencode-ai/ui/icon"
import { Markdown } from "@opencode-ai/ui/markdown"
import { useI18n } from "@opencode-ai/ui/context/i18n"
import type { ReasoningPart } from "@kilocode/sdk/v2"
import type { MessagePartProps } from "@opencode-ai/ui/message-part"
// Override: collapsible reasoning block with brain icon + label
// Replaces the upstream flat markdown render with a clearly distinguished, collapsed-by-default block
PART_MAPPING["reasoning"] = function ReasoningPartDisplay(props: MessagePartProps) {
const part = props.part as unknown as ReasoningPart
const i18n = useI18n()
const [open, setOpen] = createSignal(false)
// Filter out redacted reasoning chunks from OpenRouter (encrypted reasoning data appears as [REDACTED])
const text = () => (part.text ?? "").replace("[REDACTED]", "").trim()
return (
<Show when={text()}>
<div data-component="reasoning-part">
<Collapsible open={open()} onOpenChange={setOpen}>
<Collapsible.Trigger>
<div data-slot="reasoning-header">
<Icon name="brain" size="small" />
<span data-slot="reasoning-label">{i18n.t("ui.reasoning.label" as never)}</span>
</div>
<Collapsible.Arrow />
</Collapsible.Trigger>
<Collapsible.Content>
<div data-slot="reasoning-content">
<Markdown text={text()} cacheKey={part.id} />
</div>
</Collapsible.Content>
</Collapsible>
</div>
</Show>
)
}
@@ -1,6 +1,8 @@
/** @jsxImportSource solid-js */
import type { Meta, StoryObj } from "storybook-solidjs-vite"
import { Message, AssistantMessageDisplay, UserMessageDisplay } from "@opencode-ai/ui/message-part"
// Side-effect import: registers kilo-ui's PART_MAPPING override for reasoning blocks
import "../components/message-part"
import { DataProvider } from "@opencode-ai/ui/context/data"
import { DiffComponentProvider } from "@opencode-ai/ui/context/diff"
import { CodeComponentProvider } from "@opencode-ai/ui/context/code"
@@ -8,7 +10,7 @@ import { DialogProvider } from "@opencode-ai/ui/context/dialog"
import { MarkedProvider } from "@opencode-ai/ui/context/marked"
import { Diff } from "@opencode-ai/ui/diff"
import { Code } from "@opencode-ai/ui/code"
import type { UserMessage, AssistantMessage, TextPart, ToolPart } from "@kilocode/sdk/v2"
import type { UserMessage, AssistantMessage, TextPart, ToolPart, ReasoningPart } from "@kilocode/sdk/v2"
const SESSION_ID = "session-story-001"
const USER_MSG_ID = "user-msg-001"
@@ -104,6 +106,15 @@ const errorToolPart: ToolPart = {
},
}
const reasoningPart: ReasoningPart = {
id: "part-reasoning-001",
sessionID: SESSION_ID,
messageID: ASST_MSG_ID,
type: "reasoning",
text: "Let me think about this carefully. The user wants code improvements.\n\n1. First, I should check for error boundaries — they prevent cascading failures\n2. The dependencies could be updated to newer minor versions\n3. Unit tests would improve confidence in refactoring later\n\nI'll structure my response to address each point clearly.",
time: { start: now - 9000, end: now - 8500 },
}
const mockData = {
session: [],
session_status: {},
@@ -186,6 +197,33 @@ export const FullConversationTurn: Story = {
),
}
export const WithReasoningCollapsed: Story = {
name: "WithReasoning (collapsed)",
render: () => (
<AllProviders>
<AssistantMessageDisplay message={mockAssistantMessage} parts={[reasoningPart, textPart]} />
</AllProviders>
),
}
export const WithReasoningExpanded: Story = {
name: "WithReasoning (expanded)",
render: () => {
// Use a wrapper to render with the collapsible open by default
const expandedReasoningPart = { ...reasoningPart, id: "part-reasoning-expanded" }
return (
<AllProviders>
<AssistantMessageDisplay message={mockAssistantMessage} parts={[expandedReasoningPart, textPart]} />
</AllProviders>
)
},
play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
// Click the collapsible trigger to expand it
const trigger = canvasElement.querySelector("[data-slot='reasoning-header']")?.closest("button")
if (trigger) trigger.click()
},
}
export const MessageSwitch: Story = {
render: () => (
<AllProviders>
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cf93ca79d15deab7dc52473f68305b521c62d10e968a9258e09609ad9c04fde6
size 15837
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14bf991adcbc6fcbf53f4cff2275dcec255943b5545c624231aeb73529ca2d6e
size 32478