World Breadcrumbs

Spatial trail showing the canvas's current location in a hierarchy of worlds, groups, and runs. Install World Breadcrumbs from the VLLNT UI registry with the shadcn CLI.

Report a bug

Spatial trail showing the canvas's current location in a hierarchy of worlds, groups, and runs. Part of the navigation family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

Add World Breadcrumbs to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/world-breadcrumbs.json

Source

"use client"; import type { ComponentPropsWithoutRef, ReactNode } from "react"; import { cn } from "../../lib/utils"; /** * One spatial trail crumb. * * @public */ export type WorldCrumb = { /** Stable identifier — used as the React key. */ id: string; /** Optional kind (drives the leading glyph). */ kind?: WorldCrumbKind; /** Display label. */ label: ReactNode; }; /** * Glyph hint for a crumb kind. * * @public */ export type WorldCrumbKind = | "agent" | "artifact" | "group" | "run" | "task" | "world"; const KIND_GLYPH: Record<WorldCrumbKind, string> = { agent: "◇", artifact: "◌", group: "▤", run: "▶", task: "▢", world: "✦", }; /** * Localizable strings. * * @public */ export type WorldBreadcrumbsLabels = { /** Empty-state copy. Defaults to `"No location"`. */ empty?: string; /** Aria-label override. Defaults to `"World breadcrumbs"`. */ region?: string; }; const DEFAULT_LABELS = { empty: "No location", region: "World breadcrumbs", } as const satisfies Required<WorldBreadcrumbsLabels>; /** * Props for {@link WorldBreadcrumbs}. * * @public */ export type WorldBreadcrumbsProps = { /** Trail in render order — the last crumb represents the active location. */ crumbs: WorldCrumb[]; /** Localizable strings. */ labels?: WorldBreadcrumbsLabels; /** Click handler — receives the activated crumb id. */ onSelect?: (id: string) => void; } & ComponentPropsWithoutRef<"nav">; const Crumb = (props: { crumb: WorldCrumb; isLast: boolean; onSelect?: (id: string) => void; }): React.ReactElement => { const { crumb, isLast, onSelect } = props; const glyph = crumb.kind ? KIND_GLYPH[crumb.kind] : null; const handleSelectCrumb = (): void => { onSelect?.(crumb.id); }; const text = ( <span className="inline-flex items-center gap-1"> {glyph ? ( <span aria-hidden="true" className="text-muted-foreground"> {glyph} </span> ) : null} <span className="truncate">{crumb.label}</span> </span> ); if (isLast || !onSelect) { return ( <span aria-current={isLast ? "location" : undefined} className={cn( "inline-flex items-center text-xs", isLast ? "font-semibold text-foreground" : "text-muted-foreground", )} data-world-breadcrumb={crumb.id} data-world-breadcrumb-active={isLast} > {text} </span> ); } return ( <button className="inline-flex items-center rounded-sm text-xs text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" data-world-breadcrumb={crumb.id} data-world-breadcrumb-active="false" onClick={handleSelectCrumb} type="button" > {text} </button> ); }; /** * Spatial trail showing where the viewport sits in a hierarchy of * worlds / groups / runs / agents. Distinct from \`Breadcrumb\` * (route-based, document-tree style) — this primitive describes the * canvas's spatial location and supports per-kind glyphs. * * Pure presentation; the host computes the trail from the active * viewport target and the world graph, and resolves \`onSelect\` to a * camera transition. * * @example * ```tsx * <WorldBreadcrumbs * crumbs={[ * { id: "world", kind: "world", label: "Production" }, * { id: "group", kind: "group", label: "Ingest cluster" }, * { id: "run", kind: "run", label: "research-2025" }, * ]} * onSelect={jumpTo} * /> * ``` * * @public */ export const WorldBreadcrumbs = ({ ref, ...props }: WorldBreadcrumbsProps & { ref?: React.Ref<HTMLElement> }) => { const { className, crumbs, labels, onSelect, ...rest } = props; const resolvedLabels = { ...DEFAULT_LABELS, ...labels }; if (crumbs.length === 0) { return ( <nav aria-label={resolvedLabels.region} className={cn( "inline-flex items-center text-xs text-muted-foreground", className, )} data-world-breadcrumbs data-world-breadcrumbs-state="empty" ref={ref} {...rest} > {resolvedLabels.empty} </nav> ); } return ( <nav aria-label={resolvedLabels.region} className={cn( "inline-flex items-center gap-1.5 text-xs text-muted-foreground", className, )} data-world-breadcrumbs ref={ref} {...rest} > {crumbs.map((crumb, index) => ( <span className="inline-flex items-center gap-1.5" key={crumb.id}> <Crumb crumb={crumb} isLast={index === crumbs.length - 1} onSelect={onSelect} /> {index < crumbs.length - 1 ? ( <span aria-hidden="true" className="text-muted-foreground/60" data-world-breadcrumb-sep > </span> ) : null} </span> ))} </nav> ); }; WorldBreadcrumbs.displayName = "WorldBreadcrumbs";

Stories

Explore every variant and state in the interactive Storybook:

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Dependencies

  • @vllnt/ui@^0.3.0