/** * The context rail beneath the graph column (weave-workspace §1.1, §1.2, P2.5). * * LINKS, BACKLINKS, TAGS and MENTIONS for whatever is selected — §1.1's * "bring related information into the current view", with every entry * clickable and nothing requiring a navigation to see. * * Props in, JSX out. `context.model.ts` decides which groups exist, which * rows they hold, what each row is labelled and what it selects — and, since * Tier 6, how many of them there are and whether the group is open. This * renders the result, plus one `useState` for the toggles the model needs to * resolve it. */ import { useState } from "preact/hooks"; import type { ContextModel, ContextRow, RailSectionView, RailToggles, TagGroupRow } from "../context/context.model"; import { contextModel, emptyRailToggles, railPanelId, railSectionView, railTagsView, railToggled } from "../context/context.model"; import type { GraphPayload } from "../../shared/wire"; import { CONTEXT_EMPTY } from "./shell.model"; import { Icon } from "../tree/Tree"; export interface ContextRailProps { graph: GraphPayload | null; selectedId: string | null; onSelect: (id: string) => void; } function Row({ row, onSelect }: { row: ContextRow; onSelect: () => void }) { return (
  • ); } /** * One collapsible section heading. * * The heading is a ` ); } function Group({ view, onSelect, onToggle }: { view: RailSectionView; onSelect: (id: string) => void; onToggle: () => void }) { return (
    ); } function Tag({ tag, onSelect }: { tag: TagGroupRow; onSelect: (id: string) => void }) { return (
  • #{tag.tag}
  • ); } export function ContextRail(props: ContextRailProps) { const model: ContextModel = contextModel(props.graph, props.selectedId); // The user's open/closed word lives in this component — it is view state, // not a workspace fact, so it does not cross the §1.3 bus and does not // survive a reload. Resolving it against the model is the model's job. const [toggles, setToggles] = useState(emptyRailToggles); // The view carries the section's current state, so the click handler needs // no lookup — "which way am I going" was resolved a few lines up. const toggleFor = (view: RailSectionView): (() => void) => () => setToggles(railToggled(toggles, view.heading, view.collapsed)); const tags = railTagsView(model.tags, toggles); return (

    {CONTEXT_EMPTY.title}

    {model.empty === null ? null :

    {model.empty}

    } {model.groups.map((group) => { const view = railSectionView(group, toggles); return ; })} {model.tags.length === 0 ? null : (
    )}
    ); }