<script lang="ts">
import { Brain, ChevronRight, ChevronDown, Copy, Check, EyeOff } from "lucide-svelte";
import type { ScratchpadEntry } from "../lib/streaming-v2";
export let entry: ScratchpadEntry;
export let expanded: boolean = false;
let copied = false;
let copyTimeout: ReturnType<typeof setTimeout> | undefined;
function toggle() {
expanded = !expanded;
}
$: rawContent = (() => {
const parts: string[] = [];
if (entry.thoughts) {
parts.push(entry.thoughts);
}
if (entry.prose) {
parts.push(entry.prose);
}
if (entry.toolProposed) {
const args = entry.toolProposed.arguments ? `\n${entry.toolProposed.arguments}` : "";
parts.push(`tool: ${entry.toolProposed.name}${args}`);
}
return parts.join("\n\n");
})();
$: lineCount = rawContent ? rawContent.split("\n").length : 0;
$: lineLabel = `${lineCount} ${lineCount === 1 ? "line" : "lines"}`;
async function copyPayload() {
if (!rawContent) return;
try {
await navigator.clipboard.writeText(rawContent);
copied = true;
clearTimeout(copyTimeout);
copyTimeout = setTimeout(() => {
copied = false;
}, 1600);
} catch {
// clipboard error fallback
}
}
</script>
<div class="scratchpad-record">
<div class="header-line">
<Brain size={13} stroke-width={1.5} class="lane-scratchpad" />
<span class="chip-quiet icon-word"><EyeOff size={12} stroke-width={1.5} /> not delivered</span>
{#if entry.step !== undefined}
<span class="mute num">step {entry.step}</span>
{/if}
<span class="mute num">{lineLabel}</span>
<button
type="button"
class="btn btn-icon disclosure"
onclick={toggle}
aria-expanded={expanded}
title={expanded ? "collapse scratchpad" : "expand scratchpad"}
aria-label={expanded ? "collapse scratchpad" : "expand scratchpad"}
>
{#if expanded}
<ChevronDown size={13} stroke-width={1.5} />
{:else}
<ChevronRight size={13} stroke-width={1.5} />
{/if}
</button>
<button
type="button"
class="btn btn-icon copy-btn"
class:is-copied={copied}
onclick={copyPayload}
title={copied ? "copied" : "copy raw scratchpad"}
aria-label={copied ? "copied" : "copy raw scratchpad"}
>
{#if copied}
<Check size={13} stroke-width={1.5} />
{:else}
<Copy size={13} stroke-width={1.5} />
{/if}
</button>
</div>
{#if expanded}
<div class="scratchpad-body">
<pre class="raw-content">{rawContent || "(empty)"}</pre>
</div>
{/if}
</div>
<style>
.scratchpad-record {
display: flex;
flex-direction: column;
gap: var(--s-2);
width: 100%;
}
.header-line {
display: flex;
align-items: center;
gap: var(--s-2);
min-height: 24px;
flex-wrap: wrap;
}
.copy-btn {
margin-left: auto;
}
.copy-btn.is-copied {
color: var(--ok);
border-color: var(--ok);
}
/* the same shape as a cell: one hairline in from the gutter, never a card inside a card. */
.scratchpad-body {
margin-left: var(--s-5);
padding: var(--s-2) 0 var(--s-1) var(--s-5);
border-left: 1px solid var(--line);
max-width: var(--measure);
}
.raw-content {
margin: 0;
font-family: var(--mono);
color: var(--fg-mute);
font-size: var(--fs-2);
line-height: var(--lh);
white-space: pre-wrap;
word-break: break-word;
}
</style>