// Main column-stack workspace (Finder-style).
//
// Layout: [column 0 (section)] [column 1] … [column N] [text editor (pinned)].
// The text-editor pane is always rendered last and absorbs the remaining
// horizontal space; the column stack on its left is horizontally scrollable
// so deep chains stay reachable on narrow viewports.
//
// Each column has a drag-to-resize handle on its right edge. Each column also
// gets `index` as a prop so its inner click handlers can call `pushFrom(index, …)`
// to drill in (truncating any deeper columns first).
"use client";
import { useCallback, useEffect, useRef } from "react";
import { TextCanvasPane } from "./sections/TextCanvasPane";
import { ModelPane } from "./sections/ModelPane";
import { ConceptsPane } from "./sections/ConceptsPane";
import { RequirementsPane } from "./sections/RequirementsPane";
import { FindingsPane } from "./sections/FindingsPane";
import {
TermColumn,
BlockColumn,
AssociationColumn,
ConstraintColumn,
RequirementColumn,
FindingColumn,
} from "./columns/EntityColumns";
import {
useOpenPanes,
PANE_MIN_WIDTH,
PANE_MAX_WIDTH,
columnKey,
type Column,
} from "../../lib/workspace/openPanesStore";
import type { FixtureData } from "../../lib/fixtures/aristotle";
interface MainWorkspaceProps {
data: FixtureData;
projectId: string;
}
export function MainWorkspace({ data, projectId }: MainWorkspaceProps) {
const { columns, closeFrom, widthFor, setWidth } = useOpenPanes();
return (
{columns.map((col, index) => (
setWidth(col, w)}
onClose={() => closeFrom(index)}
/>
))}
);
}
interface ResizableColumnProps {
column: Column;
index: number;
projectId: string;
width: number;
onResize: (w: number) => void;
onClose: () => void;
}
function ResizableColumn({ column, index, projectId, width, onResize, onClose }: ResizableColumnProps) {
const startXRef = useRef(0);
const startWidthRef = useRef(0);
const draggingRef = useRef(false);
const onPointerMove = useCallback(
(e: PointerEvent) => {
if (!draggingRef.current) return;
const dx = e.clientX - startXRef.current;
const next = Math.max(PANE_MIN_WIDTH, Math.min(PANE_MAX_WIDTH, startWidthRef.current + dx));
onResize(next);
},
[onResize]
);
const onPointerUp = useCallback(() => {
if (!draggingRef.current) return;
draggingRef.current = false;
document.body.style.cursor = "";
document.body.style.userSelect = "";
}, []);
useEffect(() => {
const move = (e: PointerEvent) => onPointerMove(e);
const up = () => onPointerUp();
window.addEventListener("pointermove", move);
window.addEventListener("pointerup", up);
return () => {
window.removeEventListener("pointermove", move);
window.removeEventListener("pointerup", up);
};
}, [onPointerMove, onPointerUp]);
const onHandleDown = useCallback(
(e: React.PointerEvent) => {
e.preventDefault();
draggingRef.current = true;
startXRef.current = e.clientX;
startWidthRef.current = width;
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
},
[width]
);
return (
);
}
interface ColumnRendererProps {
column: Column;
index: number;
projectId: string;
onClose: () => void;
}
function ColumnRenderer({ column, index, projectId, onClose }: ColumnRendererProps) {
switch (column.kind) {
case "section":
switch (column.id) {
case "concepts":
return ;
case "model":
return ;
case "requirements":
return ;
case "assumptions":
return ;
case "risks":
return ;
case "inconsistencies":
return ;
}
case "term":
return ;
case "block":
return ;
case "association":
return ;
case "constraint":
return ;
case "requirement":
return ;
case "finding":
return (
);
}
}