// Requirements pane — list of extracted requirements with traceability + // unsupported flags. Click a requirement's traced block → focus that block // in Model. Re-running Analyze MERGES; new and deprecated requirements // surface for the user to keep or discard. "use client"; import { useMemo } from "react"; import { PaneFrame } from "../PaneFrame"; import { PaneEmpty } from "../PaneEmpty"; import { PaneDrawer } from "../PaneDrawer"; import { StatusChip } from "../StatusChip"; import { useAnalysis, type ClientRequirement } from "../../../lib/workspace/analysisStore"; import { useOpenPanes } from "../../../lib/workspace/openPanesStore"; import { useModelStore } from "../../../lib/sync/ModelStore"; interface RequirementsPaneProps { index: number; onClose: () => void; } export function RequirementsPane({ index, onClose }: RequirementsPaneProps) { const { requirements, analyze, inFlight } = useAnalysis(); const { pushFrom } = useOpenPanes(); const { model } = useModelStore(); const running = inFlight.has("requirements") || inFlight.has("all"); const kept = useMemo( () => requirements.filter(r => r.status === "accepted"), [requirements] ); const pending = useMemo( () => requirements.filter(r => r.status !== "accepted"), [requirements] ); const unsupportedCount = kept.filter(r => r.unsupported).length; const labelOf = (id: string) => model.blocks.find(b => b.id === id)?.label ?? id; const subtitle = requirements.length === 0 ? "no requirements yet" : pending.length > 0 ? `${kept.length} kept · ${pending.length} pending` : unsupportedCount > 0 ? `${kept.length} total · ${unsupportedCount} unsupported` : `${kept.length} total · all traced`; const right = (
); const renderRow = (r: ClientRequirement) => ( pushFrom(index, { kind: "requirement", id: r.id })} onJumpToBlock={id => pushFrom(index, { kind: "block", id })} onJumpToTerm={id => pushFrom(index, { kind: "term", id })} /> ); return (
{kept.length === 0 && pending.length === 0 ? ( Requirements are extracted from sentences in your prose that say the system must / should / needs to. Run Analyze to scan. } action={{ label: running ? "Analyzing…" : "Run Analyze →", onClick: () => void analyze("requirements"), disabled: running, }} /> ) : kept.length === 0 ? ( ) : (
    {kept.map(renderRow)}
)}
{pending.length > 0 ? (
    {pending.map(renderRow)}
) : null}
); } interface RequirementItemProps { req: ClientRequirement; labelOf: (id: string) => string; onOpenSelf: () => void; onJumpToBlock: (id: string) => void; onJumpToTerm: (id: string) => void; } function RequirementItem({ req, labelOf, onOpenSelf, onJumpToBlock, onJumpToTerm }: RequirementItemProps) { const { decideRequirement } = useAnalysis(); return (
  • { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onOpenSelf(); } }} >
    {req.tag} {req.unsupported ? : null}
    {req.text}
    {req.linkedTermId ? ( ) : null} {req.tracedToIds.length > 0 ? (
    e.stopPropagation()}> Traced to:{" "} {req.tracedToIds.map((id, i) => ( ))}
    ) : null} {req.status !== "accepted" ? (
    e.stopPropagation()}>
    ) : null}
  • ); } function ReqStatusBadge({ status }: { status: ClientRequirement["status"] }) { if (status === "suggested") return ; if (status === "deprecated") return ; return null; }