+ {focused?.question?.trim() ? (
+
+
Question
+
{focused.question}
+
+ ) : formulationStep === "active" ? (
+
{formulateMsg}
+ ) : null}
- if (!hasContent) return null;
+ {focused?.question?.trim() && processingStep !== "active" && focused.status === "formulated" && (
+
+
+
+ )}
- return (
-
-
- {focused?.question?.trim() ? (
-
-
Question
-
{focused.question}
+ {processingStep === "active" &&
{deconstructMsg}
}
+
+ {focused?.result && (
+ <>
+ {/* Prior accumulated learning (prior turns, current turn excluded — shown above) */}
+
+
+
What this tells us
{(focused.result.observations || []).map((o, i) => (- {o}
))}
+
Still unclear
{(focused.result.uncertainties || []).map((u, i) => (- {u}
))}
+
Questions this raises
+ {(focused.result.possibleFollowUpQuestions || []).length > 0 ? (
+
+ {focused.result.possibleFollowUpQuestions.map((q, i) => {
+ const isCurrentQuestion = q === focused?.question;
+ return (
+
+ );
+ })}
+
+ ) : (
+
None yet
+ )}
- ) : formulationStep === "active" ? (
-
{formulateMsg}
- ) : null}
+
Assumptions
{(focused.result.assumptions || []).map((a, i) => (- {a}
))}
+
Connections
{(focused.result.relationships || []).map((r, i) => (- {r.from} → {r.to} ({r.type})
))}
+ >
+ )}
- {focused?.question?.trim() && processingStep !== "active" && focused.status === "formulated" && (
-
-
-
- )}
-
- {processingStep === "active" &&
{deconstructMsg}
}
-
- {focused?.result && (
- <>
- {/* Prior accumulated learning (prior turns, current turn excluded — shown above) */}
-
-
-
What this tells us
{(focused.result.observations || []).map((o, i) => (- {o}
))}
-
Still unclear
{(focused.result.uncertainties || []).map((u, i) => (- {u}
))}
-
Questions this raises
- {(focused.result.possibleFollowUpQuestions || []).length > 0 ? (
-
- {focused.result.possibleFollowUpQuestions.map((q, i) => {
- const isCurrentQuestion = q === focused?.question;
- return (
-
- );
- })}
-
- ) : (
-
None yet
- )}
-
-
Assumptions
{(focused.result.assumptions || []).map((a, i) => (- {a}
))}
-
Connections
{(focused.result.relationships || []).map((r, i) => (- {r.from} → {r.to} ({r.type})
))}
- >
- )}
-
- {focused?.error && processingStep !== "active" && (
-
We were unable to process your request right now. Please try again later.
- )}
-
-
-
-
-
-
+ {focused?.error && processingStep !== "active" && (
+
We were unable to process your request right now. Please try again later.
+ )}
- );
- })()}
+
+ )}
>
);
}
+// ── Persistent navigation controls (overlay-level, outside content grid) ──
+
+function FocusedWorkspaceNavigation({ nodeId, doneForNow, onBackToOpenQuestions, isDoneForNowActive }) {
+ const canDoneForNow = Boolean(isDoneForNowActive);
+ return (
+
+
+
+
+ );
+}
+
// ── Current understanding card ────────────────────────────────
// Evidence-limit text that must not appear inside Current understanding
@@ -463,6 +479,54 @@ function PriorContributionsSummary({ nodeId, contributions }) {
);
}
+// ── Standalone previous learning block (for two-column secondary placement) ───
+
+function SecondaryPreviousLearning({ nodeId, contributions }) {
+ const threadContribs = contributions.filter((c) => c.targetNodeId === nodeId);
+ if (!threadContribs.length) return null;
+
+ // Exclude the most recent contribution — it is already shown as the current result above.
+ const priorContribs = threadContribs.slice(0, -1);
+ if (!priorContribs.length) return null;
+
+ return (
+
+
+ Previous learning
+
+ {priorContribs.map((c, idx) => (
+
+
+ Turn {c.sequence || idx + 1} — contribution ({c.observations?.length ?? 0} observations, {c.uncertainties?.length ?? 0} unclear)
+
+
+ {c.observations?.length ? (
+
+
What this tells us
+
+ {c.observations.map((o, i) => (
+ - {o}
+ ))}
+
+
+ ) : null}
+ {c.uncertainties?.length ? (
+
+
Still unclear
+
+ {c.uncertainties.map((u, i) => (
+ - {u}
+ ))}
+
+
+ ) : null}
+
+
+ ))}
+
+ );
+}
+
// ── Thread contributions badge (standalone — used outside focused body) ───
function ThreadContributionsBadge({ nodeId, contributions }) {
@@ -938,6 +1002,65 @@ function getErrorType(errorStr, stage, hasGraph) {
return null;
}
+// ── Focused investigation overlay workspace container ──────────
+
+function FocusedInvestigationWorkspace({
+ nodeId,
+ focused,
+ formulationStep,
+ formulateMsg,
+ processingStep,
+ deconstructMsg,
+ focusedAnswer,
+ handleDeconstructSubmit,
+ retryFormulation,
+ setFocusedAnswer,
+ setSelectedPresentationItemId,
+ setFocusedPresentationItemId,
+ setDoneForNowIds,
+ setFollowUpQuestion,
+ setIsFocusedWorkspaceOpen,
+ hasCompletedInvestigation,
+ focusedContributions,
+}) {
+ const hasResult = Boolean(focused?.result);
+
+ return (
+
+ {/* Two-column responsive grid: primary active work + secondary context */}
+
+ {/* ── Primary column: active investigation — min-w-0 prevents flex growth in single-column mode ── */}
+
+
+
+
+ {/* ── Secondary context: Previous Learning — visible on all breakpoints, placed in grid column on wide / flows below primary on narrow ── */}
+ {hasResult && (
+
+ )}
+
+
+ );
+}
+
// ── Open questions panel (production, non-experiment mode) ────────
function OpenQuestionsPanel({
@@ -945,7 +1068,7 @@ function OpenQuestionsPanel({
focused, formulationStep, formulateMsg, processingStep, deconstructMsg, doneForNowIds,
startFocused, handleDeconstructSubmit, retryFormulation, setSelectedPresentationItemId,
setFocusedPresentationItemId, setFocusedAnswer, focusedAnswer, setDoneForNowIds,
- setFollowUpQuestion, focusedContributions, focusedInvestigations,
+ setFollowUpQuestion, focusedContributions, focusedInvestigations, setIsFocusedWorkspaceOpen,
}) {
const openNodes = (graph?.nodes || []).filter(
(n) => n.kind === "unknown" && n.status !== "resolved" && !doneForNowIds.includes(n.id),
@@ -959,17 +1082,14 @@ function OpenQuestionsPanel({
return Boolean(inv && inv.status === "formulated" && inv.result && typeof inv.question === "string" && inv.question.trim());
};
- // When a completed result exists and this node is already selected, reopen it.
function handleNodeClick(node) {
- if (!focused?.question?.trim() || (focused && !hasFocusedContent())) {
- if (hasCompletedInvestigation(node.id) && selectedPresentationItemId === node.id) {
- setFocusedPresentationItemId(node.id);
- return;
- }
- setSelectedPresentationItemId(
- selectedPresentationItemId === node.id ? null : node.id,
- );
+ if (hasCompletedInvestigation(node.id) && selectedPresentationItemId === node.id) {
+ setFocusedPresentationItemId(node.id);
+ return;
}
+ setSelectedPresentationItemId(
+ selectedPresentationItemId === node.id ? null : node.id,
+ );
}
return (
@@ -1081,6 +1201,9 @@ export default function ReasoningWorkspace({
const [focusedPresentationItemId, setFocusedPresentationItemId] = useState(null);
const [doneForNowIds, setDoneForNowIds] = useState([]);
+ // ── Focused investigation overlay workspace ───────────────
+ const [isFocusedWorkspaceOpen, setIsFocusedWorkspaceOpen] = useState(false);
+
// ── RTO.29D — post-Analyse initial reflection surface ─────────
const [initialReflectionActive, setInitialReflectionActive] = useState(false);
const [postAnalyseStatus, setPostAnalyseStatus] = useState(null);
@@ -1247,6 +1370,9 @@ export default function ReasoningWorkspace({
setFocusedPresentationItemId(target);
setFocusedAnswer("");
+ // Always open the focused workspace overlay
+ setIsFocusedWorkspaceOpen(true);
+
if (priorContribs.length > 0) {
// Reopen path: resume from accumulated contribution history.
// Do NOT call doFormulate — the user's prior investigation direction
@@ -1418,6 +1544,14 @@ export default function ReasoningWorkspace({
// ── End RTO.13B ──────────────────────────────────────────────
+ // ── Body scroll lock while overlay is open ────────────────
+ useEffect(() => {
+ if (isFocusedWorkspaceOpen) {
+ document.body.style.overflow = "hidden";
+ return () => { document.body.style.overflow = ""; };
+ }
+ }, [isFocusedWorkspaceOpen]);
+
const canAnswer =
status === "success" &&
!isUpdating &&
@@ -1585,6 +1719,7 @@ export default function ReasoningWorkspace({
function handleNodeClick(n) {
if (hasCompleted && selectedPresentationItemId === n.id) {
setFocusedPresentationItemId(n.id);
+ setIsFocusedWorkspaceOpen(true);
return;
}
startFocused(n.id);
@@ -1606,8 +1741,8 @@ export default function ReasoningWorkspace({
);
})}
- {/* Focused content — rendered inline in the initial reflection surface */}
- {(() => {
+ {/* Focused content — rendered inline in the initial reflection surface (suppressed when overlay open) */}
+ {!isFocusedWorkspaceOpen && (() => {
const activeForFocus = openUnknowns.find((n) => focusedPresentationItemId === n.id);
if (!activeForFocus) return null;
@@ -1711,6 +1846,7 @@ export default function ReasoningWorkspace({
setFollowUpQuestion={setFollowUpQuestion}
focusedContributions={focusedContributions}
focusedInvestigations={focusedInvestigations}
+ setIsFocusedWorkspaceOpen={setIsFocusedWorkspaceOpen}
/>
)}
@@ -1830,6 +1966,91 @@ export default function ReasoningWorkspace({
)}
+ {/* ── Focused investigation overlay workspace ─────────── */}
+ {isFocusedWorkspaceOpen && (
+
+ )}
+
{/* Developer details — full-width beneath workspace */}
{(status === "success" || status === "error") && graph && (