Skip to main content
Gremorie

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

Quick calculator
Interactive widget generated from a tool call.
Tool widget
Result60

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 defaults

Installation

npx gremorie@latest add artifact-calculator
pnpm dlx gremorie@latest add artifact-calculator
yarn dlx gremorie@latest add artifact-calculator
bunx --bun gremorie@latest add artifact-calculator

Prompt 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.

On this page