Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion web/components/chat/CitedChunks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { useState } from "react"
import { Badge } from "@/components/ui/badge"
import { ChevronDown, ChevronRight, FileText } from "lucide-react"
import { cn } from "@/lib/utils"
import { levelTone } from "@/lib/log-levels"

export type CitedChunk = {
chunk_id: string
Expand Down Expand Up @@ -62,7 +64,7 @@ export function CitedChunks({
</Badge>
)}
{c.level && (
<Badge variant="outline" className="px-1.5 py-0">
<Badge variant="outline" className={cn("px-1.5 py-0", levelTone(c.level))}>
{c.level}
</Badge>
)}
Expand Down
17 changes: 1 addition & 16 deletions web/components/chat/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useState } from "react"
import { Badge } from "@/components/ui/badge"
import { ChevronDown, ChevronRight, Clock } from "lucide-react"
import { cn } from "@/lib/utils"
import { levelTone } from "@/lib/log-levels"

export type TimelineEntry = {
service: string | null
Expand All @@ -24,22 +25,6 @@ function formatRange(first: string, last: string): string {
return `${formatTs(first)}–${formatTs(last)}`
}

function levelTone(level: string | null): string {
switch ((level || "").toUpperCase()) {
case "ERROR":
case "CRITICAL":
case "FATAL":
return "text-red-500 border-red-500/30"
case "WARNING":
case "WARN":
return "text-amber-500 border-amber-500/30"
case "INFO":
return "text-blue-500 border-blue-500/30"
default:
return "text-muted-foreground border-border"
}
}

// Optional controlled `open` so a parent (e.g. a quick-action button on the
// assistant turn) can force the panel open. Falls back to uncontrolled
// internal state when omitted.
Expand Down
34 changes: 16 additions & 18 deletions web/components/investigation-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@ import { Spinner } from "@/components/ui/spinner"
import { Terminal, Brain, Search, ChevronDown, ChevronRight, Sparkles, Lightbulb, Flag } from "lucide-react"
import { useState } from "react"
import ReactMarkdown from "react-markdown"
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"

// Neutral JSON rendering — matches CitedChunks' raw-text styling so every
// monospace panel in the app reads the same. Syntax-highlighting themes
// (vscDarkPlus etc.) brought a full colour scheme into an otherwise
// two-tone UI and ignored light/dark mode.
function JsonBlock({ value }: { value: unknown }) {
return (
<pre className="font-mono text-[11px] whitespace-pre-wrap break-all leading-snug text-foreground/85 m-0">
{JSON.stringify(value, null, 2)}
</pre>
)
}

export function InvestigationStepCard({ step }: { step: Step }) {
const [showTool, setShowTool] = useState(false)
Expand Down Expand Up @@ -79,7 +89,7 @@ export function InvestigationStepCard({ step }: { step: Step }) {

{/* Action/Tool Call */}
{step.action && (
<div className="ml-8 border rounded-md overflow-hidden bg-card/50">
<div className="ml-8 border rounded-lg overflow-hidden bg-card/50">
<button
onClick={() => setShowTool(!showTool)}
className="w-full flex items-center justify-between p-2 px-3 text-xs font-mono bg-muted/50 hover:bg-muted transition-colors"
Expand All @@ -93,21 +103,15 @@ export function InvestigationStepCard({ step }: { step: Step }) {
</button>
{showTool && (
<div className="p-3 bg-muted/20">
<SyntaxHighlighter
language="json"
style={vscDarkPlus}
customStyle={{ margin: 0, padding: 0, background: 'transparent', fontSize: '11px' }}
>
{JSON.stringify(step.action.args, null, 2)}
</SyntaxHighlighter>
<JsonBlock value={step.action.args} />
</div>
)}
</div>
)}

{/* Observation */}
{step.observation && (
<div className="ml-8 border rounded-md overflow-hidden bg-card/50">
<div className="ml-8 border rounded-lg overflow-hidden bg-card/50">
<button
onClick={() => setShowObservation(!showObservation)}
className="w-full flex items-center justify-between p-2 px-3 text-xs font-mono bg-muted/50 hover:bg-muted transition-colors"
Expand All @@ -125,13 +129,7 @@ export function InvestigationStepCard({ step }: { step: Step }) {
</button>
{showObservation && (
<div className="max-h-[300px] overflow-auto p-3 bg-muted/20">
<SyntaxHighlighter
language="json"
style={vscDarkPlus}
customStyle={{ margin: 0, padding: 0, background: 'transparent', fontSize: '11px' }}
>
{JSON.stringify(step.observation, null, 2)}
</SyntaxHighlighter>
<JsonBlock value={step.observation} />
</div>
)}
</div>
Expand Down
19 changes: 19 additions & 0 deletions web/lib/log-levels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Single source of truth for log-level badge styling, shared by every panel
// that renders a level (Timeline, CitedChunks, …) so they can't drift apart.
//
// Deliberately minimal palette: theme `destructive` for error-class levels,
// one amber accent for warnings, neutral for INFO and everything else —
// INFO is the overwhelming majority of rows, so colouring it is pure noise.
export function levelTone(level: string | null | undefined): string {
switch ((level || "").toUpperCase()) {
case "ERROR":
case "CRITICAL":
case "FATAL":
return "text-destructive border-destructive/30"
case "WARNING":
case "WARN":
return "text-amber-600 dark:text-amber-500 border-amber-500/30"
default:
return "text-muted-foreground border-border"
}
}
Loading