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.
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.jsonSource
"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.