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

  1. Card / CardHeader / CardTitle - frame and badge
  2. Number input pair - Label + Input[type=number] for A and B
  3. Operator toggle group - Button per operator with aria-pressed
  4. Result row - muted strip with the computed value
  5. Reset button - 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>
  );
}

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.

On this page