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 - frame and badge
- Number input pair -
Label+Input[type=number]for A and B - Operator toggle group -
Buttonper operator witharia-pressed - Result row - muted strip with the computed value
- Reset button - ghost button restoring defaults
Installation
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>
);
}Angular edition planned for Phase 5h. Star the repo to track progress.
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.