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-callpnpm dlx kosmesis add tool-callyarn dlx kosmesis add tool-callbunx kosmesis add tool-callInstall the following dependencies:
npm install @morphos/iconspnpm add @morphos/iconsyarn add @morphos/iconsbun add @morphos/iconsCopy and paste the following code into your project.
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/iconspnpm add @praxisjs/css @morphos/iconsyarn add @praxisjs/css @morphos/iconsbun add @praxisjs/css @morphos/iconsCopy and paste the following code into your project.
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
| Prop | Type | Default |
|---|---|---|
name | string | — |
status | "pending" | "running" | "done" | "error" | "done" |
defaultOpen | boolean | false |
class | string | — |