Kosmesis
Components

Tool Call

A collapsible tool/function call an agent made, with its input/output as children. Purely presentational — no Morphos equivalent.

Installation

npx kosmesis add tool-call
pnpm dlx kosmesis add tool-call
yarn dlx kosmesis add tool-call
bunx kosmesis add tool-call

Install the following dependencies:

npm install @morphos/icons
pnpm add @morphos/icons
yarn add @morphos/icons
bun add @morphos/icons

Copy and paste the following code into your project.

tool-call.tsx
import { StatefulComponent } from "@praxisjs/core";import { Component, Prop, State } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { Icon } from "@morphos/icons";import { cn } from "@/lib/utils";export type ToolCallStatus = "pending" | "running" | "done" | "error";export interface ToolCallProps {  name: string;  status?: ToolCallStatus;  defaultOpen?: boolean;  class?: string;  children?: Children;}@Component()export class ToolCall extends StatefulComponent {  @Prop() name = "";  @Prop() status: ToolCallStatus = "done";  @Prop() defaultOpen = false;  @Prop() class?: string;  @Prop() children?: Children;  @State() _open = false;  onBeforeMount() {    this._open = this.defaultOpen;  }  toggle(): void {    this._open = !this._open;  }  render() {    return (      <div        data-slot="tool-call"        data-status={() => this.status}        class={cn("rounded-lg border bg-muted/30 font-mono text-xs", this.class)}      >        <button type="button" class="flex w-full items-center gap-2 px-3 py-2" onClick={() => { this.toggle(); }}>          <span class={() => cn("inline-block shrink-0 transition-transform", this._open && "rotate-90")}>            <Icon name="ChevronRight" size={14} />          </span>          <span            class={cn(              "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5",              "in-data-[status=error]:bg-destructive/20 in-data-[status=error]:text-destructive",            )}          >            <Icon name="Wrench" size={12} />            {this.name}          </span>          <span class="ml-auto text-muted-foreground in-data-[status=running]:animate-pulse">            {() => (this.status === "running" ? "running…" : this.status === "error" ? "error" : this.status === "pending" ? "pending" : "done")}          </span>        </button>        <div          data-state={() => (this._open ? "open" : "closed")}          class="overflow-x-auto border-t px-3 py-2 text-muted-foreground data-[state=closed]:hidden"        >          {this.children}        </div>      </div>    );  }}

Install the following dependencies:

npm install @praxisjs/css @morphos/icons
pnpm add @praxisjs/css @morphos/icons
yarn add @praxisjs/css @morphos/icons
bun add @praxisjs/css @morphos/icons

Copy and paste the following code into your project.

tool-call.tsx
import { StatefulComponent } from "@praxisjs/core";import { cx, keyframes, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component, Prop, State } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { Icon } from "@morphos/icons";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);const pulse = keyframes("kosmesis-tool-call-pulse", { "0%, 100%": { opacity: "1" }, "50%": { opacity: "0.5" } });class ToolCallStyles extends Stylesheet {  $root = this.css({    borderRadius: "0.5rem",    border: `1px solid ${t.border}`,    backgroundColor: `color-mix(in oklab, ${t.muted} 30%, transparent)`,    fontFamily: "ui-monospace, monospace",    fontSize: "0.75rem",  });  $trigger = this.css({ display: "flex", width: "100%", alignItems: "center", gap: "0.5rem", padding: "0.5rem 0.75rem", cursor: "pointer" });  $chevron = this.css({ display: "inline-block", flexShrink: 0, transition: "transform 150ms ease" }).on("&[data-open]", {    transform: "rotate(90deg)",  });  $name = this.css({    display: "inline-flex",    alignItems: "center",    gap: "0.25rem",    borderRadius: "4px",    backgroundColor: t.muted,    padding: "0.125rem 0.375rem",  }).on(    '[data-status="error"] &',    { backgroundColor: `color-mix(in oklab, ${t.destructive} 20%, transparent)`, color: t.destructive },  );  $status = this.css({ marginLeft: "auto", color: t.mutedForeground }).on('[data-status="running"] &', {    animation: `${pulse} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite`,  });  $content = this.css({    overflowX: "auto",    borderTop: `1px solid ${t.border}`,    padding: "0.5rem 0.75rem",    color: t.mutedForeground,  }).on('&[data-state="closed"]', { display: "none" });}export type ToolCallStatus = "pending" | "running" | "done" | "error";export interface ToolCallProps {  name: string;  status?: ToolCallStatus;  defaultOpen?: boolean;  class?: string;  children?: Children;}@Component()export class ToolCall extends StatefulComponent {  @Styled(ToolCallStyles) $s!: ToolCallStyles;  @Prop() name = "";  @Prop() status: ToolCallStatus = "done";  @Prop() defaultOpen = false;  @Prop() class?: string;  @Prop() children?: Children;  @State() _open = false;  onBeforeMount() {    this._open = this.defaultOpen;  }  toggle(): void {    this._open = !this._open;  }  render() {    return (      <div data-slot="tool-call" data-status={() => this.status} class={cx(this.$s.$root, this.class)}>        <button type="button" class={this.$s.$trigger} onClick={() => { this.toggle(); }}>          <span data-open={() => (this._open ? "" : undefined)} class={this.$s.$chevron}>            <Icon name="ChevronRight" size={14} />          </span>          <span class={this.$s.$name}>            <Icon name="Wrench" size={12} />            {this.name}          </span>          <span class={this.$s.$status}>            {() => (this.status === "running" ? "running…" : this.status === "error" ? "error" : this.status === "pending" ? "pending" : "done")}          </span>        </button>        <div data-state={() => (this._open ? "open" : "closed")} class={this.$s.$content}>          {this.children}        </div>      </div>    );  }}

Examples

About

Purely presentational — no Morphos equivalent. Structurally close to Task/Reasoning (collapsible header + detail body), styled monospace and meant to hold raw input/output JSON as children.

Usage

import { ToolCall } from "@/components/ui/tool-call";

<ToolCall name="search_docs" status="done">
  {JSON.stringify({ query: "refund policy" })}
</ToolCall>

Props

PropTypeDefault
namestring
status"pending" | "running" | "done" | "error""done"
defaultOpenbooleanfalse
classstring

On this page