// ~/src/siteplane/components/trace/trace.tsx "use client"; import { ChevronRightIcon } from "lucide-react"; import * as React from "react"; import { cn } from "../../system/utils/cn"; import { slotClass } from "../../system/utils/slot-class"; import { Button } from "../button"; import { Collapsible, CollapsiblePanel, CollapsibleTrigger, } from "../collapsible"; import { MotionShimmer } from "../motion"; import "./trace.css"; /** * The state of the work a line reports. A line with no status reports work * that is done. */ export type TraceStatus = "running" | "error" | "pending"; /* * The row of a step that does something: the trigger of a collapsible step * and a link. Both are the kit's muted inline Button (rulebook T2), which owns * the colours at rest, on hover, press and keyboard focus, the focus outline * and the 44 px touch target. The Button's inline profile draws no box, so the * row has the height of its line; it is laid out as a flex row that is only as * wide as its words, and a long title ends with an ellipsis (K5). A text * Button underlines on hover; a step is not text in running prose, so it does * not. */ const traceRowClassName = "flex w-fit max-w-full min-w-0 gap-1 text-start no-underline"; const TraceStatusContext = React.createContext( undefined, ); /** * The text of children that are nothing but text: strings and numbers, such as * `Ran {command}`. With an element among the children it is undefined, so the * server and the browser always agree on it. */ function plainText(node: React.ReactNode): string | undefined { if (node === null || node === undefined || typeof node === "boolean") return ""; if ( typeof node === "string" || typeof node === "number" || typeof node === "bigint" ) return `${node}`; if (!Array.isArray(node)) return undefined; let text = ""; for (const child of node as React.ReactNode[]) { const part = plainText(child); if (part === undefined) return undefined; text += part; } return text; } /** The steps of an agent, as one-line rows with an icon, a title and a status. */ export function Trace({ className, ...props }: React.ComponentProps<"div">): React.ReactElement { return (
); } export interface TraceLineProps extends React.ComponentProps<"span"> { /** * `running` is work in progress (write the title in the present tense), * `error` is work that failed (say so in the title) and `pending` is work * that has not started. Without a status the line reports work that is done. */ status?: TraceStatus | undefined; } /** * One step. It is a span, so it is valid inside the button or the link that * makes it do something, and it is neither a button nor a tab stop on its own. * Its status reaches its title through context. */ export function TraceLine({ className, status, ...props }: TraceLineProps): React.ReactElement { return ( ); } /** * A decorative icon slot, hidden from assistive technology. Before the title * it names the action; after it, it is an indicator. The glyph is 16 px * (rulebook I3) and keeps its full opacity inside a Button. */ export function TraceIcon({ className, ...props }: React.ComponentProps<"span">): React.ReactElement { return (