import type { ReactNode } from 'react' import { cn } from '@/lib/cn' /** * The editor's side rail, and the answer to "four things want one rail". * * The inspector, history, journal, and (later) the chat panel all want the same * strip of screen. Rather than each bolting on its own chrome, they are tabs in * one rail — which keeps the canvas one width instead of a different * width per panel, and means adding the journal or chat is adding a tab. * * Two constraints shaped it: * * - Selecting an object must land you in the inspector with no extra click. * The editor watches the selection and switches to that tab itself, so the * rail never becomes a thing you have to operate before you can edit. * - The canvas has to stay worth watching while the agent edits it, so the * rail is a fixed 20rem column and closes completely when nothing needs it. * * On a phone the same tabs render in a bottom sheet, which is where the * inspector already lived. */ export interface RailTab { id: string label: string /** Rendered lazily: only the active tab's content is built. */ render: () => ReactNode /** Shown as a dot on the tab — a count, or true for a plain marker. */ badge?: number | boolean } export function EditorRail({ tabs, activeId, onActivate, onClose, }: { tabs: RailTab[] activeId: string onActivate: (id: string) => void onClose: () => void }) { const active = tabs.find((t) => t.id === activeId) ?? tabs[0] if (!active) return null return (
{tabs.map((tab) => ( ))}
{active.render()}
) }