Calculator
Interactive tool-call artifact with state management and a result display.
Overview
An interactive artifact for tool calls that need a quick UI. The model
emits { a, b, op }; the artifact renders editable inputs, an operator
toggle, and a live result. The user can adjust the values without
round-tripping back to the model.
This artifact demonstrates the stateful end of the spectrum — most artifacts render once, but interactive widgets keep their own state after the initial payload lands.
Preview
Schema
The LLM returns structured output matching this shape:
{
type: "calculator",
a: number,
b: number,
op: "+" | "-" | "*" | "/",
}Anatomy
Card
├─ CardHeader CardTitle + type Badge
├─ number input pair Label + Input[type=number] (A, B)
├─ operator toggle Button per operator (aria-pressed)
├─ result row muted strip with the computed value
└─ reset ghost Button restoring defaultsInstallation
npx gremorie@latest add artifact-calculatorpnpm dlx gremorie@latest add artifact-calculatoryarn dlx gremorie@latest add artifact-calculatorbunx --bun gremorie@latest add artifact-calculatorPrompt examples
Sample prompts that produce valid output for this artifact:
- "Help me compute a tip — start with 48 and 12 and let me adjust."
- "Drop a calculator with 100 divided by 4."
- "I need to quickly multiply some numbers — open a calculator."
Code
'use client';
import { useState } from 'react';
import {
Badge,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@gremorie/rx-display';
import { Button, Input, Label } from '@gremorie/rx-forms';
import { RotateCcw } from 'lucide-react';
type Op = '+' | '-' | '*' | '/';
function compute(a: number, b: number, op: Op): number {
switch (op) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return b === 0 ? NaN : a / b;
}
}
export function Calculator() {
const [a, setA] = useState(48);
const [b, setB] = useState(12);
const [op, setOp] = useState<Op>('+');
const result = compute(a, b, op);
return (
<Card>
<CardHeader>
<div className="flex items-center justify-between gap-3">
<div>
<CardTitle>Quick calculator</CardTitle>
<CardDescription>
Interactive widget generated from a tool call.
</CardDescription>
</div>
<Badge variant="outline">Tool widget</Badge>
</div>
</CardHeader>
<CardContent className="flex flex-col gap-4">
{/* Inputs, operator toggle, result, reset... */}
</CardContent>
</Card>
);
}The Angular edition of this component is planned; the React edition is production-ready today.
Streaming behavior
This artifact renders after the payload parses, then takes over its own state. The model seeds the initial values; the user drives everything that follows. Subsequent tool calls can re-seed.