/* Scene Outliner — collapsible Shots / Cast / Sets / Props with counts + visibility. */ import { useState, type ReactNode } from "react"; import { Icon } from "./Icon"; import type { CastMember, CastSource, EpisodeDocument, PropEntity, Selection, SelectionType, SetEntity } from "../state/types"; import { fidelityLabel, type CharacterFidelity } from "../state/fidelity"; interface RowProps { name: string; sel: boolean; onSelect: () => void; hidden: boolean; onToggle: () => void; meta?: string; accent?: string; glyph?: string; icon?: string; /** Provenance badge (Phase E3): authored-fallback vs catalog-resolved vs user-uploaded. */ source?: CastSource; sourceLabel?: string; /** M7 — fidelity tier badge (A/B/C); C renders as "Previz". */ fidelity?: CharacterFidelity; } /** Short, honest badge text + class per cast provenance class (E3). */ const SOURCE_BADGE: Record = { "authored-fallback": { text: "fallback", cls: "src-fallback" }, "catalog-resolved": { text: "catalog", cls: "src-catalog" }, "user-uploaded": { text: "uploaded", cls: "src-uploaded" } }; function Row({ name, sel, onSelect, hidden, onToggle, meta, accent, glyph, icon, source, sourceLabel, fidelity }: RowProps) { const badge = source ? SOURCE_BADGE[source] : undefined; return (
{glyph ? (
{glyph}
) : (
)} {name} {fidelity && ( {fidelity.previz ? "Previz" : fidelityLabel(fidelity.grade)} )} {badge && ( {badge.text} )} {meta && {meta}}
); } function Group({ title, count, children, onAdd }: { title: string; count: number; children: ReactNode; onAdd?: () => void; }) { const [open, setOpen] = useState(true); return (
setOpen(!open)}> {title} {count} {onAdd && ( )}
{open &&
{children}
}
); } export interface OutlinerProps { data: EpisodeDocument; sel: Selection | null; onSelect: (type: SelectionType, id: string) => void; hidden: Set; onToggle: (id: string) => void; onAddCast: () => void; onOpenPalette: () => void; } export function Outliner({ data, sel, onSelect, hidden, onToggle, onAddCast, onOpenPalette }: OutlinerProps) { return (
⌘K
{/* M7 — scene fidelity tier. A grade-C scene is labeled "previz" and never sold as finished. */}
{data.fidelity.previz ? "Previz" : fidelityLabel(data.fidelity.grade)} {data.fidelity.previz ? "preview-quality — not a finished render" : "scene fidelity"}
{data.shots.map((s) => (
onSelect("shot", s.id)} >
{s.name} {s.dur + "s"}
))}
{data.cast.map((c: CastMember) => ( onSelect("cast", c.id)} hidden={hidden.has(c.id)} onToggle={() => onToggle(c.id)} /> ))} {data.sets.map((s: SetEntity) => ( onSelect("set", s.id)} hidden={hidden.has(s.id)} onToggle={() => onToggle(s.id)} /> ))} {data.props.map((p: PropEntity) => ( onSelect("prop", p.id)} hidden={hidden.has(p.id)} onToggle={() => onToggle(p.id)} /> ))}
); }