Epicenter
Local-first, open source apps
Chain of Thought is a summary line that expands into the steps an agent took. The summary says what happened, like "Researched in 3 steps". The steps sit inside it, each one a line of its own.
Chain of Thought is built with Trace with a Trace.Collapsible for the summary.
Installation
Chain of Thought is built with the Trace, Code Block and Badge components.
Usage
<script lang="ts">
import * as Trace from "$lib/components/ui/trace/index.js";
</script> <Trace.Root>
<Trace.Collapsible open>
<Trace.CollapsibleTrigger>
<Trace.Line>
<Trace.Icon>
<CircleCheckIcon />
</Trace.Icon>
<Trace.Title>Researched in 3 steps</Trace.Title>
</Trace.Line>
</Trace.CollapsibleTrigger>
<Trace.CollapsibleContent>
<Trace.Root>
<Trace.Line>
<Trace.Icon>
<CircleCheckIcon />
</Trace.Icon>
<Trace.Title>Read the build log for dpl_8Kx2</Trace.Title>
</Trace.Line>
<Trace.Line>
<Trace.Icon>
<CircleCheckIcon />
</Trace.Icon>
<Trace.Title
>Searched the web for next.js image cache key</Trace.Title
>
</Trace.Line>
<Trace.Line>
<Trace.Icon>
<CircleCheckIcon />
</Trace.Icon>
<Trace.Title>Searched the code for cacheKey</Trace.Title>
</Trace.Line>
</Trace.Root>
</Trace.CollapsibleContent>
</Trace.Collapsible>
</Trace.Root> Steps
Each step is a Trace.Line. Set its status and write the title in the matching tense.
Search Results
When a step runs a web search, show the results as a Badge for each hostname in the step's content.
<Trace.CollapsibleContent class="flex flex-wrap gap-1.5">
{#each results as result, index (index)}
<Badge variant="secondary">
{new URL(result.url).hostname}
</Badge>
{/each}
</Trace.CollapsibleContent> AI SDK
Render each tool part as a step. running is true while tool parts stream and before the reply starts. Set the summary's status and title from the steps. The summary opens while the steps run, as in Trace.
<script lang="ts">
let open = $state<boolean>();
const running = $derived(streaming && !answering);
const failed = $derived(
steps.filter((part) => part.state === "output-error").length
);
</script>
<Trace.Collapsible
open={open ?? running}
onOpenChange={(value) => (open = value)}
>
<Trace.CollapsibleTrigger>
<Trace.Line
status={running ? "running" : failed > 0 ? "error" : undefined}
>
<Trace.Title>
{running ? "Researching" : `Researched in ${steps.length} steps`}
</Trace.Title>
</Trace.Line>
</Trace.CollapsibleTrigger>
<Trace.CollapsibleContent>
<Trace.Root>
{#each steps as part (part.toolCallId)}
{@render stepLine(part, streaming)}
{/each}
</Trace.Root>
</Trace.CollapsibleContent>
</Trace.Collapsible> stepLine renders one tool part as a Trace.Line, as in Tool.
API Reference
See Trace.