Overlays

ToolCallPanel

One tool call's side panel: its state, its input and result as fields, a failure's message first with next steps, and a waiting call's note and decision. Docks from 1280px; a Drawer below.

import { ToolCallPanel } from '@lostgradient/cinder';
tool-callpanelagent
01

Overview

Reference usage

The side panel of one tool call, opened from a ToolCallRail row. The header names the tool with its identifier small and monospaced, then says where the call stands: a dot, the state, how long it took and where it falls in the turn ("Failed 10.0s, call 28 of 30"). Under it, an Input section lists what the call was given as labelled fields, and the rest depends on the state: a done call shows its Result, a running call says the result is coming, a failed call shows an Error block with its message first and the next steps, and a waiting call carries a note field with Deny and Approve.

Use ToolCallPanel for one call's details beside a conversation. Use PayloadInspector for an arbitrary payload on its own page, and Drawer for navigation or settings from a page edge. The panel is a Drawer below the width it docks at, so it never reflows the conversation on a small screen.

Placement

  • docked: an inset panel about 340px wide in the flow of the page, with an X. Esc closes it while focus is inside.
  • overlay: a right sheet over the content on a dimming scrim, closed by its X or Esc. The conversation stays visible behind it.
  • sheet: a full-height bottom sheet with a grabber and a Close button, like the shared sheets. It cross-fades under reduced motion. The copy buttons give way to the retry button, which rests at the foot.
  • auto (the default): sheet below 640px, overlay up to dockedFrom (1280px), and docked from there. 1280px is where a 190px sidebar, a 250px list, about 480px of conversation and the 340px panel fit; it is a threshold of its own, apart from the shared 1024px desktop breakpoint.

Usage

svelte
<script lang="ts">
  import { ToolCallPanel } from '@lostgradient/cinder';

  let open = $state(true);
</script>

<ToolCallPanel
  bind:open
  name="Send email"
  identifier="send-email"
  status="failed"
  duration="10.0s"
  position="call 28 of 30"
  input={{ to: 'maya@example.com', template: 'refund-notice', orderId: '4471' }}
  error={{
    message: 'No reply from mail.example.com after 10s',
    code: 'smtp-timeout',
    retryable: true,
  }}
  retryMessage="Please retry Send email to maya@example.com"
  onRetry={() => undefined}
/>

Kinds of call

  • done, approved and denied: Input and Result as fields, with secondary Copy input and Copy result buttons. Pass resultLabel="Output" for a tool that calls its answer an output.
  • running: Input, and a Result that reads "The result appears when the call finishes."
  • waiting: Input, a "Note (optional)" field and Deny and Approve. Approve is the only primary action on the screen. onApprove and onDeny receive the note; resolving writes "Approving…" or "Denying…" on that button and turns both off.
  • failed: Input and an Error block, tinted red, with the message first and then the code in monospace and Retryable. It explains what retrying sends (retryMessage), then a full-width secondary Ask the agent to retry (onRetry) and secondary Copy input and Copy error.

Input and Result read as the Fields view of PayloadInspector does: keys become sentence-case words with acronyms kept (orderId reads "Order ID"), ISO dates read as dates, booleans as "Yes" and "No", null as "None" and an empty string as "Empty". Values the caller's schema marks secret, or that read as secrets when redact is not passed, show "Hidden" in both views and in the copy buttons. view="json" shows the stored value in a code block instead; text that is already JSON is shown as it was stored.

Closing and focus

open is bindable and onClose fires once whenever the panel closes. Call focusCall(id) on the rail from onClose to hand focus back to the row that opened it; the overlay and the sheet also restore focus to triggerRef, or to where it was before they opened.

Props

plaintext
open          boolean (bindable)     optional  Whether the panel is open. Defaults to true.
name          string                 required  The tool's name as a person reads it.
identifier    string                 optional  The tool's identifier, small and monospaced.
status        done | approved | running | waiting | denied | failed   required
duration      string                 optional  How long the call took, formatted by the caller.
position      string                 optional  Where it falls in the turn ("call 28 of 30").
summary       string                 optional  The line under the name, replacing the status line.
input         unknown                optional  What the call was given.
result        unknown                optional  What the call returned.
resultLabel   string                 optional  The Result heading. Defaults to "Result".
error         ToolCallPanelError     optional  Why a failed call failed: message, code, retryable.
view          fields | json (bindable) optional  How Input and Result show. Defaults to fields.
redact        string[]               optional  Secret paths to hide.
retryMessage  string                 optional  The message the retry button sends, quoted in the note.
onRetry       () => void             optional  Shows "Ask the agent to retry" on a failed call.
onApprove     (note) => void         optional  Shows Approve on a waiting call.
onDeny        (note) => void         optional  Shows Deny on a waiting call.
resolving     approve | deny         optional  The answer on its way.
onClose       () => void             optional  Fired once when the panel closes.
placement     auto | docked | overlay | sheet   optional  Defaults to auto.
dockedFrom    string                 optional  The width it docks from. Defaults to 1280px.
triggerRef    HTMLElement | null     optional  Where focus returns for an overlay or sheet.
labels        ToolCallPanelLabels    optional  The panel's words, for another language.
class         string                 optional  Extra class names.
children      Snippet                optional  Extra sections after Input and Result.
Live preview

Beside the rail

  1. Mail server didn't reply within 10s. The refund notice for 4471 wasn't sent.

02

When to use

Use when
  • Opening a tool call from a ToolCallRail row to read what it was given and what came back.
  • Letting a person answer a call that is waiting on them, with a note, without leaving the conversation.
Avoid when
  • Showing an arbitrary payload on its own — use payload-inspector.
  • Showing supplementary navigation or settings from a page edge — use drawer.
03

Examples

Beside the rail

The rail and its panel together: the selected Send email row is tinted and, at 1280px and wider, its panel docks to the right. Below that it opens over the page on a scrim, and on a phone as a bottom sheet, so it never covers the rail. Esc or the X closes the panel and Close hands focus back to the row; choosing the row again closes it too.

At 375px, a bottom sheet

On a phone the panel is a full-height bottom sheet with a grabber and a Close button, like the shared sheets. The title is "Send email" with "send-email", a red "Failed, 10.0s, call 28 of 30", Input fields (To, Template), Error fields (Message, Retryable Yes) and a secondary full-width 44px "Ask the agent to retry" at the foot. It cross-fades under reduced motion.

A phone sheet with JSON blocks

The phone panel for Lookup order opens as a bottom sheet over the run page with a grabber, the title "Lookup order", Close, "Reads, ran in 0.4s", and Input and Output JSON blocks. The line under the title is the caller's own words, and Output is the Result heading renamed.

The panel for each kind of call

A done call shows its Input and its Result as fields with secondary "Copy input" and "Copy result". The waiting call carries a "Note (optional)" field with its description, a secondary Deny and the only primary, Approve. A running call shows its Input and a Result that reads "The result appears when the call finishes." The three panels are the same width.

A failed call, docked

Selecting a rail row docks its panel beside the conversation, about 340px wide, with an X to close it. The header reads "Send email" with "send-email" small and monospaced, then a red dot and "Failed 10.0s, call 28 of 30". The Input section lists its fields (To, Template, Order ID) and the Error section is a tinted danger block with the message first, then the code in monospace and Retryable Yes. Under it the panel says what retrying sends, then a full-width secondary "Ask the agent to retry" with a refresh icon and secondary "Copy input" and "Copy error" buttons. Approve elsewhere on the page stays the only primary action.

Below 1280px, an overlay

Under 1280px the panel opens over the conversation on a dimming scrim, closed by its X or Esc, with the same Send email content as the docked panel. It docks only at 1280px and wider (190 sidebar + 250 list + about 480 conversation + 340 panel = 1,260 plus gutters), a threshold separate from the shared 1024px desktop breakpoint. This example forces the overlay so it shows at any width.

04

Props

Props for tool-call-panel
Name
Type
Default
Description
open bindable boolean true Whether the panel is open. Bindable: the X, Close and Esc set it to false, and the page then hands focus back to the call's row.
name required text The tool's name as a person reads it ("Send email"). It names the panel.
identifier text The tool's identifier ("send-email"), small and monospaced after the name.
status required 'done''approved''running''waiting''denied''failed''interrupted' Where the call stands. It picks the status dot, the status word and which sections show.
duration text How long the call took, or has run so far ("10.0s"), formatted by the caller.
position text Where the call falls in its turn ("call 28 of 30"), written after the duration.
summary text The line under the name, in the caller's words ("Reads, ran in 0.4s"). When set it replaces the status word, duration and position.
input unknown What the call was given. An object shows as labelled fields, a list or a single value as one row, and undefined reads "This tool call takes no input."
result unknown What the call returned, for a call that finished. Hidden for a failed or waiting call, and a running call says the result is coming.
resultLabel text 'Result' The Result heading. A call that returns "Output" in the caller's vocabulary says so here.
error { /** What went wrong, in words ("No reply from mail.example.com after 10s"). Shown first. */ message: string; /** The machine code for the failure ("smtp…
Show full type { /** What went wrong, in words ("No reply from mail.example.com after 10s"). Shown first. */ message: string; /** The machine code for the failure ("smtp-timeout"), in small monospace. */ code?: string | undefined; /** Whether trying the call again can work. Written "Yes" or "No". */ retryable?: boolean | undefined; }
Why a failed call failed. It shows as a tinted danger block under the Input, message first.
view bindable 'fields' | 'json' 'fields' How Input and Result show. fields lists each property with a sentence-case label; json shows the stored value in a code block. Bindable.
redact string[] Secret paths to hide in both views and in the copy buttons, as PayloadInspector's redact: dot-separated property names, * for any one name or position. Without it, a property whose name reads as a secret (token, key, password, authorization) is hidden; pass [] to hide nothing.
retryMessage text The message the retry button sends as the person's own ("Please retry Send email to maya@example.com"). It is quoted in the note under a failed call; the button shows only when onRetry is set.
onRetry (() => void) | undefined Fired when the person asks the agent to retry a failed call. The button is secondary, full width, and shows only when this is set.
onApprove ((note: string) => void)undefined Fired with the note when Approve is pressed on a waiting call. The decision bar shows only when this or onDeny is set.
onDeny ((note: string) => void)undefined Fired with the note when Deny is pressed on a waiting call. If the person denies, the agent reads the note as the reason.
resolving 'approve' | 'deny' The answer that is on its way for a waiting call.
onClose (() => void) | undefined Fired once each time the panel closes, whether by its X, Close, Esc or the caller setting open to false. A page hands focus back to the call's row here, with focusCall(id) on the rail.
placement 'auto' | 'docked' | 'overlay' | 'sheet' 'auto' How the panel sits on the page.
dockedFrom text '1280px' The viewport width the panel docks from, as a CSS length. Narrower than this it opens as an overlay on a scrim: a docked panel beside a 190px sidebar, a 250px list, a conversation and the panel needs about 1,260px. A separate threshold from the shared 1024px desktop breakpoint.
triggerRef HTMLElement | null | undefined null The element that opened the panel, for focus to return to when an overlay or sheet closes.
labels { /** The status word of each status. Defaults to Done, Approved, Running, Waiting on you, Denied, Failed and Interrupted. */ status?: Partial<Record<ToolCallPanelStatus, string>> | undefined; /** The Input heading. Defaults to "Input". */ input?: string | undefined; /** What the Input section says when the call took no input. Defaults to "This tool call takes no input." */ noInput?: string | undefined; /** The Error heading. Defaults to "Error". */ error?: string | undefined; /** The Error section's Message row. Defaults to "Message". */ message?: string | undefined; /** The Error section's Code row. Defaults to "Code". */ code?: string | undefined; /** The Error section's Retryable row. Defaults to "Retryable". */ retryable?: string | undefined; /** What a running call's Result section says. Defaults to "The result appears when the call finishes." */ pending?: string | undefined; /** The note under a failed call, given the retry message. Defaults to the sentence that explains what retrying sends. */ retryNote?: ((retryMessage: string) => string)undefined; /** The retry button. Defaults to "Ask the agent to retry". */ retry?: stringundefined; /** The button that copies the input. Defaults to "Copy input". */ copyInput?: stringundefined; /** The button that copies the result, which writes the result heading after "Copy". Defaults to "Copy result". */ copyResult?: stringundefined; /** The button that copies the error. Defaults to "Copy error". */ copyError?: stringundefined; /** The Note field's label. Defaults to "Note (optional)". */ note?: string | undefined; /** The Note field's description. Defaults to "Saved with your decision. If you deny, the agent reads it as the reason." */ noteDescription?: stringundefined; /** The Deny button. Defaults to "Deny". */ deny?: stringundefined; /** The Approve button. Defaults to "Approve". */ approve?: string
5 more members undefined; /** The Deny button while the answer is on its way. Defaults to "Denying…". */ denying?: stringundefined; /** The Approve button while the answer is on its way. Defaults to "Approving…". */ approving?: stringundefined; /** The text of the close control on a bottom sheet. Defaults to "Close". */ close?: stringundefined; /** The accessible name of the X that closes a docked or overlay panel. Defaults to "Close panel". */ closeIcon?: stringundefined; }
The panel's words.
children Snippet | undefined Extra sections after the Input and Result, such as a link to the run.