Skip to content

Instantly share code, notes, and snippets.

@lostintangent
Created July 31, 2026 19:16
Show Gist options
  • Select an option

  • Save lostintangent/753daad03e0e3785c61434ce82188b60 to your computer and use it in GitHub Desktop.

Select an option

Save lostintangent/753daad03e0e3785c61434ce82188b60 to your computer and use it in GitHub Desktop.
Agent Copy Desk - Toy Box app
{
"title": "Agent Copy Desk",
"description": "Turn a short brief and a tone into generated copy written by an app-owned worker.",
"icon": "Feather",
"defaultState": { "brief": "", "tone": "friendly", "copy": "", "generatedAt": null, "requestId": null }
}
import { useState } from "react";
import {
AppBadge,
AppButton,
AppEmptyState,
AppHeader,
AppModelPicker,
AppShell,
AppTextarea,
useAppActions,
useAppState,
useWorkspace,
} from "@toy-box/app";
import { Check, Feather, Loader2, Sparkles, X } from "lucide-react";
const TONES = ["friendly", "confident", "playful", "technical"] as const;
type Tone = (typeof TONES)[number];
type DeskState = {
brief: string;
tone: Tone;
copy: string;
generatedAt: string | null;
requestId: string | null;
};
function normalize(raw: unknown): DeskState {
const value = (raw ?? {}) as Partial<DeskState>;
return {
brief: typeof value.brief === "string" ? value.brief : "",
tone: TONES.includes(value.tone as Tone) ? (value.tone as Tone) : "friendly",
copy: typeof value.copy === "string" ? value.copy : "",
generatedAt: typeof value.generatedAt === "string" ? value.generatedAt : null,
requestId: typeof value.requestId === "string" ? value.requestId : null,
};
}
const WORKER_KIND = "agent-copy-desk-draft";
type WorkerMetadata = { kind?: string; requestId?: string };
export default function AgentCopyDesk() {
const [rawState, updateState] = useAppState<unknown>();
const state = normalize(rawState);
const workers = useWorkspace((workspace) => workspace.workers);
const defaultModel = useWorkspace((workspace) => workspace.defaultModel);
const actions = useAppActions();
const [model, setModel] = useState(defaultModel);
const [spawning, setSpawning] = useState(false);
const activeModel = model ?? defaultModel;
const pendingWorker = workers.find((worker) => {
const metadata = (worker.metadata ?? {}) as WorkerMetadata;
return metadata.kind === WORKER_KIND && metadata.requestId !== state.requestId;
});
const isPending = spawning || pendingWorker !== undefined;
const canAsk = state.brief.trim() !== "" && activeModel !== null && !isPending;
function setBrief(brief: string) {
updateState((current) => ({ ...normalize(current), brief }));
}
function setTone(tone: Tone) {
updateState((current) => ({ ...normalize(current), tone }));
}
async function ask() {
if (!canAsk || !activeModel) return;
const { brief, tone } = state;
const requestId = `copy-${Date.now()}`;
setSpawning(true);
try {
await actions.spawnWorker({
name: "Copy draft",
metadata: { kind: WORKER_KIND, requestId },
message: {
model: activeModel,
content: `Write marketing copy for this brief, in a ${tone} tone.
Brief:
${brief.trim()}
Produce two to four short sentences of finished copy with no preamble, headings,
or quotation marks. Set "copy" in the current app state to that text, set
"generatedAt" to the current ISO 8601 timestamp, set "requestId" to "${requestId}",
and preserve every other field exactly as it is.`,
},
});
} finally {
setSpawning(false);
}
}
async function cancel() {
if (!pendingWorker) return;
await actions.stopWorker(pendingWorker.sessionId);
}
return (
<AppShell>
<AppHeader>
<div className="flex min-w-0 flex-1 items-center gap-2">
<Feather className="size-4 shrink-0 text-primary" />
<span className="truncate text-sm font-semibold">Agent Copy Desk</span>
{isPending ? (
<AppBadge variant="secondary" className="shrink-0 gap-1 text-[11px]">
<Loader2 className="size-3 animate-spin" />
Drafting
</AppBadge>
) : state.copy ? (
<AppBadge variant="outline" className="shrink-0 gap-1 text-[11px]">
<Check className="size-3" />
Ready
</AppBadge>
) : null}
</div>
</AppHeader>
<div className="flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto p-3 @2xl:flex-row">
<section className="flex shrink-0 flex-col gap-2.5 @2xl:w-80">
<div className="flex flex-col gap-1.5">
<label htmlFor="copy-desk-brief" className="text-xs font-medium text-muted-foreground">
Brief
</label>
<AppTextarea
id="copy-desk-brief"
value={state.brief}
onChange={(event) => setBrief(event.target.value)}
placeholder="What are we writing about?"
rows={4}
className="resize-none text-sm"
/>
</div>
<div className="flex flex-col gap-1.5">
<span className="text-xs font-medium text-muted-foreground">Tone</span>
<div className="flex flex-wrap gap-1.5">
{TONES.map((tone) => (
<AppButton
key={tone}
variant={state.tone === tone ? "default" : "outline"}
size="sm"
onClick={() => setTone(tone)}
aria-pressed={state.tone === tone}
className="h-7 px-2.5 text-xs capitalize"
>
{tone}
</AppButton>
))}
</div>
</div>
{activeModel ? (
<div className="flex flex-col gap-1.5">
<span className="text-xs font-medium text-muted-foreground">Model</span>
<AppModelPicker value={activeModel} onValueChange={setModel} />
</div>
) : null}
<div className="flex items-center gap-1.5">
<AppButton size="sm" onClick={ask} disabled={!canAsk} className="h-8 flex-1 gap-1.5">
{isPending ? (
<Loader2 className="size-3.5 animate-spin" />
) : (
<Sparkles className="size-3.5" />
)}
{isPending ? "Working…" : "Ask agent"}
</AppButton>
{pendingWorker ? (
<AppButton
variant="outline"
size="sm"
onClick={cancel}
className="h-8 shrink-0 gap-1.5 px-2.5 text-xs"
aria-label="Cancel the running draft"
>
<X className="size-3.5" />
Cancel
</AppButton>
) : null}
</div>
</section>
<section className="flex min-h-40 min-w-0 flex-1 flex-col gap-1.5">
<div className="flex items-center justify-between gap-2">
<span className="text-xs font-medium text-muted-foreground">Generated copy</span>
{state.generatedAt ? (
<span className="truncate text-[11px] text-muted-foreground">
{new Date(state.generatedAt).toLocaleString()}
</span>
) : null}
</div>
<div className="min-h-0 flex-1 overflow-y-auto rounded-lg border border-border bg-card/60 p-3">
{isPending && !state.copy ? (
<div className="flex flex-col gap-2" aria-live="polite">
<div className="h-3 w-4/5 animate-pulse rounded bg-muted" />
<div className="h-3 w-full animate-pulse rounded bg-muted" />
<div className="h-3 w-3/5 animate-pulse rounded bg-muted" />
</div>
) : state.copy ? (
<p className="text-sm leading-relaxed whitespace-pre-wrap">{state.copy}</p>
) : (
<AppEmptyState
title="No copy yet"
description="Write a brief, pick a tone, then ask the agent."
/>
)}
</div>
</section>
</div>
</AppShell>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment