Components
Branch
Navigable alternate responses (regenerations) for one AI turn. Purely presentational — no Morphos equivalent.
Installation
npx kosmesis add branchpnpm dlx kosmesis add branchyarn dlx kosmesis add branchbunx kosmesis add branchInstall 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 BranchProps { branches: Children[]; class?: string;}// All branches render up front and toggle via `data-active`; a reactive children-thunk can't// return a bare `Children` value.@Component()export class Branch extends StatefulComponent { @Prop() branches: Children[] = []; @Prop() class?: string; @State() _index = 0; prev(): void { this._index = Math.max(0, this._index - 1); } next(): void { this._index = Math.min(this.branches.length - 1, this._index + 1); } render() { return ( <div data-slot="branch" class={cn("flex flex-col gap-2", this.class)}> <div> {this.branches.map((branch, i) => ( <div key={i} data-active={() => (this._index === i ? "" : undefined)} class="hidden data-[active]:block"> {branch} </div> ))} </div> {this.branches.length > 1 && ( <div class="flex items-center gap-2 text-xs text-muted-foreground"> <button type="button" aria-label="Previous branch" disabled={() => this._index === 0} class="rounded p-1 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-30" onClick={() => { this.prev(); }} > <Icon name="ChevronLeft" size={14} /> </button> <span>{() => `${String(this._index + 1)} / ${String(this.branches.length)}`}</span> <button type="button" aria-label="Next branch" disabled={() => this._index === this.branches.length - 1} class="rounded p-1 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-30" onClick={() => { this.next(); }} > <Icon name="ChevronRight" size={14} /> </button> </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 BranchStyles extends Stylesheet { $root = this.css({ display: "flex", flexDirection: "column", gap: "0.5rem" }); $branch = this.css({ display: "none" }).on("&[data-active]", { display: "block" }); $nav = this.css({ display: "flex", alignItems: "center", gap: "0.5rem", fontSize: "0.75rem", color: t.mutedForeground }); $navButton = this.css({ borderRadius: "0.25rem", padding: "0.25rem", cursor: "pointer" }) .on("&:hover", { backgroundColor: t.accent, color: t.accentForeground }) .on("&:disabled", { pointerEvents: "none", opacity: 0.3 });}export interface BranchProps { branches: Children[]; class?: string;}// All branches render up front and toggle via `data-active`; a reactive children-thunk can't// return a bare `Children` value.@Component()export class Branch extends StatefulComponent { @Styled(BranchStyles) $s!: BranchStyles; @Prop() branches: Children[] = []; @Prop() class?: string; @State() _index = 0; prev(): void { this._index = Math.max(0, this._index - 1); } next(): void { this._index = Math.min(this.branches.length - 1, this._index + 1); } render() { return ( <div data-slot="branch" class={cx(this.$s.$root, this.class)}> <div> {this.branches.map((branch, i) => ( <div key={i} data-active={() => (this._index === i ? "" : undefined)} class={this.$s.$branch}> {branch} </div> ))} </div> {this.branches.length > 1 && ( <div class={this.$s.$nav}> <button type="button" aria-label="Previous branch" disabled={() => this._index === 0} class={this.$s.$navButton} onClick={() => { this.prev(); }}> <Icon name="ChevronLeft" size={14} /> </button> <span>{() => `${String(this._index + 1)} / ${String(this.branches.length)}`}</span> <button type="button" aria-label="Next branch" disabled={() => this._index === this.branches.length - 1} class={this.$s.$navButton} onClick={() => { this.next(); }} > <Icon name="ChevronRight" size={14} /> </button> </div> )} </div> ); }}Examples
About
Purely presentational — no Morphos equivalent. Every branch is rendered up front and toggled via a
data-active attribute (not swapped in/out), the same pattern StickyScroll's panel uses, since a
reactive children-thunk can't return a bare Children value. The prev/next nav only renders when
there's more than one branch.
Usage
import { Branch } from "@/components/ui/branch";
<Branch branches={[responseA, responseB, responseC]} />Props
| Prop | Type | Default |
|---|---|---|
branches | Children[] | — |
class | string | — |