Components
Reasoning
A collapsible block for an AI response's raw reasoning/thinking text. Purely presentational — no Morphos equivalent.
Installation
npx kosmesis add reasoningpnpm dlx kosmesis add reasoningyarn dlx kosmesis add reasoningbunx kosmesis add reasoningInstall 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 interface ReasoningProps { defaultOpen?: boolean; duration?: number; streaming?: boolean; class?: string; children?: Children;}// Distinct from `ChainOfThought` (a stepped list of named steps): this is a single collapsible block.@Component()export class Reasoning extends StatefulComponent { @Prop() defaultOpen = false; @Prop() duration?: number; @Prop() streaming = 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="reasoning" class={cn("rounded-lg border bg-muted/30", this.class)}> <button type="button" class="flex w-full items-center gap-2 px-3 py-2 text-sm text-muted-foreground" onClick={() => { this.toggle(); }} > <span class={() => cn("inline-block transition-transform", this._open && "rotate-90")}> <Icon name="ChevronRight" size={14} /> </span> {this.streaming ? "Thinking…" : this.duration !== undefined ? `Thought for ${String(this.duration)}s` : "Reasoning"} </button> <div data-state={() => (this._open ? "open" : "closed")} class="flex flex-col gap-2 border-t px-3 py-2 text-sm 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, 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);class ReasoningStyles extends Stylesheet { $root = this.css({ borderRadius: "0.5rem", border: `1px solid ${t.border}`, backgroundColor: `color-mix(in oklab, ${t.muted} 30%, transparent)`, }); $trigger = this.css({ display: "flex", width: "100%", alignItems: "center", gap: "0.5rem", padding: "0.5rem 0.75rem", fontSize: "0.875rem", color: t.mutedForeground, cursor: "pointer", }); $chevron = this.css({ display: "inline-block", transition: "transform 150ms ease" }).on("&[data-open]", { transform: "rotate(90deg)", }); $content = this.css({ display: "flex", flexDirection: "column", gap: "0.5rem", borderTop: `1px solid ${t.border}`, padding: "0.5rem 0.75rem", fontSize: "0.875rem", color: t.mutedForeground, }).on('&[data-state="closed"]', { display: "none" });}export interface ReasoningProps { defaultOpen?: boolean; duration?: number; streaming?: boolean; class?: string; children?: Children;}// Distinct from `ChainOfThought` (a stepped list of named steps): this is a single collapsible block.@Component()export class Reasoning extends StatefulComponent { @Styled(ReasoningStyles) $s!: ReasoningStyles; @Prop() defaultOpen = false; @Prop() duration?: number; @Prop() streaming = 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="reasoning" 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> {this.streaming ? "Thinking…" : this.duration !== undefined ? `Thought for ${String(this.duration)}s` : "Reasoning"} </button> <div data-state={() => (this._open ? "open" : "closed")} class={this.$s.$content}> {this.children} </div> </div> ); }}Examples
About
Purely presentational — no Morphos equivalent. Distinct from ChainOfThought (a stepped list of
named steps): this is a single block, typically collapsed by default once streaming becomes
false. The trigger label shows "Thinking…" while streaming, then "Thought for Ns" once
duration is known.
Usage
import { Reasoning } from "@/components/ui/reasoning";
<Reasoning streaming={isStreaming} duration={elapsedSeconds}>
The user is asking about refund windows...
</Reasoning>Props
| Prop | Type | Default |
|---|---|---|
defaultOpen | boolean | false |
duration | number (seconds) | — |
streaming | boolean | false |
class | string | — |