refine(ui): restore visual hierarchy and Situation context in initial workspace
- Enhance Current Understanding prominence with subtle teal/teal border gradient, stronger heading, larger body text, more internal spacing - Restore Situation panel as right-hand column in initial reflection view; uses OriginalSituation when graph exists, scenario text fallback otherwise - Stacks layout on narrow screens via grid-cols-1/gap-6/lg:grid-cols-3 - Apply teal styling to normal-state CurrentUnderstandingCard and PlainLanguageCard (was flat gray border with bg-transparent) - Surface assumption nodes alongside unknowns in Open Questions; add Unclear / Plausible interpretation tags - Wire up follow-up question buttons in deconstructed results
This commit is contained in:
@@ -512,11 +512,11 @@ function CurrentUnderstandingCard({ currentSummary, plainLanguage }) {
|
|||||||
if (!summary) return null;
|
if (!summary) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-gray-100/80 bg-transparent px-6 pt-5 pb-6">
|
<div className="rounded-lg border-[2.5px] border-teal-300/60 bg-gradient-to-b from-teal-50/40 to-white px-7 pt-6 pb-7">
|
||||||
<h2 className="mb-3 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
<h2 className="mb-3 text-[11px] font-bold tracking-widest uppercase text-teal-700/60">
|
||||||
Understanding
|
Understanding
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm leading-relaxed text-gray-600">{summary}</p>
|
<p className="text-base leading-relaxed text-gray-800">{summary}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -526,11 +526,11 @@ function PlainLanguageCard({ summary }) {
|
|||||||
if (!summary) return null;
|
if (!summary) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-gray-100/80 bg-transparent px-6 pt-5 pb-6">
|
<div className="rounded-lg border-[2.5px] border-teal-300/60 bg-gradient-to-b from-teal-50/40 to-white px-7 pt-6 pb-7">
|
||||||
<h2 className="mb-3 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
<h2 className="mb-3 text-[11px] font-bold tracking-widest uppercase text-teal-700/60">
|
||||||
Understanding
|
Understanding
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm leading-relaxed text-gray-600">{summary}</p>
|
<p className="text-base leading-relaxed text-gray-800">{summary}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -710,6 +710,7 @@ function OpenQuestionsPanel({
|
|||||||
focused, formulationStep, formulateMsg, processingStep, deconstructMsg, doneForNowIds,
|
focused, formulationStep, formulateMsg, processingStep, deconstructMsg, doneForNowIds,
|
||||||
startFocused, handleDeconstructSubmit, retryFormulation, setSelectedPresentationItemId,
|
startFocused, handleDeconstructSubmit, retryFormulation, setSelectedPresentationItemId,
|
||||||
setFocusedPresentationItemId, setFocusedAnswer, focusedAnswer, setDoneForNowIds,
|
setFocusedPresentationItemId, setFocusedAnswer, focusedAnswer, setDoneForNowIds,
|
||||||
|
setFollowUpQuestion,
|
||||||
}) {
|
}) {
|
||||||
const openNodes = (graph?.nodes || []).filter(
|
const openNodes = (graph?.nodes || []).filter(
|
||||||
(n) => n.kind === "unknown" && n.status !== "resolved" && !doneForNowIds.includes(n.id),
|
(n) => n.kind === "unknown" && n.status !== "resolved" && !doneForNowIds.includes(n.id),
|
||||||
@@ -785,11 +786,29 @@ function OpenQuestionsPanel({
|
|||||||
|
|
||||||
{focused?.result && (
|
{focused?.result && (
|
||||||
<>
|
<>
|
||||||
<div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">What we learned</h3><ul className="list-disc pl-5 space-y-1">{focused.result.observations.map((o, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{o}</li>))}</ul></div>
|
<div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">What this tells us</h3><ul className="list-disc pl-5 space-y-1">{(focused.result.observations || []).map((o, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{o}</li>))}</ul></div>
|
||||||
<div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">Still unclear</h3><ul className="list-disc pl-5 space-y-1">{focused.result.uncertainties.map((u, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{u}</li>))}</ul></div>
|
<div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">Still unclear</h3><ul className="list-disc pl-5 space-y-1">{(focused.result.uncertainties || []).map((u, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{u}</li>))}</ul></div>
|
||||||
{focused.result.assumptions?.length > 0 && <div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">Assumptions in this response</h3><ul className="list-disc pl-5 space-y-1">{focused.result.assumptions.map((a, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{a}</li>))}</ul></div>}
|
<div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">Assumptions</h3><ul className="list-disc pl-5 space-y-1">{(focused.result.assumptions || []).map((a, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{a}</li>))}</ul></div>
|
||||||
{focused.result.relationships?.length > 0 && <div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">Connections</h3><ul className="list-disc pl-5 space-y-1">{focused.result.relationships.map((r, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{r.from} → {r.to} ({r.type})</li>))}</ul></div>}
|
<div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">Connections</h3><ul className="list-disc pl-5 space-y-1">{(focused.result.relationships || []).map((r, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{r.from} → {r.to} ({r.type})</li>))}</ul></div>
|
||||||
{focused.result.possibleFollowUpQuestions?.length > 0 && <div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">Questions this raises</h3><ul className="list-disc pl-5 space-y-1">{focused.result.possibleFollowUpQuestions.map((q, i) => (<li key={i} className="text-sm leading-relaxed text-gray-700">{q}</li>))}</ul></div>}
|
<div><h3 className="mb-1 text-[11px] font-semibold tracking-widest uppercase text-gray-500">Questions this raises</h3>
|
||||||
|
{(focused.result.possibleFollowUpQuestions || []).length > 0 ? (
|
||||||
|
<div className="space-y-1 mt-1">
|
||||||
|
{focused.result.possibleFollowUpQuestions.map((q, i) => (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
onClick={(e) => { e.stopPropagation(); setFollowUpQuestion(q); }}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
className="w-full text-left rounded-lg border border-blue-200/60 bg-blue-50/40 px-3 py-2.5 text-sm leading-relaxed text-gray-800 hover:border-blue-300 hover:bg-blue-100/60 transition"
|
||||||
|
data-testid="follow-up-question"
|
||||||
|
>
|
||||||
|
{q} → pick this question
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-xs text-gray-400">None yet</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1113,6 +1132,16 @@ export default function ReasoningWorkspace({
|
|||||||
doFormulate(target);
|
doFormulate(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setFollowUpQuestion(followUpText) {
|
||||||
|
const target = focusedPresentationItemId;
|
||||||
|
if (!target || !followUpText?.trim()) return;
|
||||||
|
setFocusedInvestigations((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[target]: { ...prev[target], question: followUpText.trim(), answer: null },
|
||||||
|
}));
|
||||||
|
setFocusedAnswer("");
|
||||||
|
}
|
||||||
|
|
||||||
function hasFocusedContent() {
|
function hasFocusedContent() {
|
||||||
if (!focused) return false;
|
if (!focused) return false;
|
||||||
const q = focused.question;
|
const q = focused.question;
|
||||||
@@ -1201,40 +1230,79 @@ export default function ReasoningWorkspace({
|
|||||||
{/* ── RTO.29D — initial post-Analyse reflection ──────── */}
|
{/* ── RTO.29D — initial post-Analyse reflection ──────── */}
|
||||||
{postAnalyseStatus === "success" && (
|
{postAnalyseStatus === "success" && (
|
||||||
<div className="space-y-6" data-testid="initial-reflection-surface">
|
<div className="space-y-6" data-testid="initial-reflection-surface">
|
||||||
{/* What I understood */}
|
{/* Current Understanding + Situation — prominent two-column orienting surface */}
|
||||||
<div className="rounded-lg border border-blue-200/60 bg-blue-50/40 px-6 pt-5 pb-6">
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||||
<h2 className="mb-3 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
<div className={`space-y-6 ${hasGraph ? 'lg:col-span-2' : 'lg:col-span-full'}`}>
|
||||||
What I understood
|
{/* Current Understanding — prominent orienting surface */}
|
||||||
</h2>
|
<div className="rounded-xl border-[2.5px] border-teal-300/70 bg-gradient-to-b from-teal-50/60 to-white px-8 pt-7 pb-8 shadow-sm">
|
||||||
<p className="text-sm leading-relaxed text-gray-700">{propUnderstanding}</p>
|
<h2 className="mb-4 text-[11px] font-bold tracking-[.18em] uppercase text-teal-700/60">
|
||||||
</div>
|
Current Understanding
|
||||||
|
</h2>
|
||||||
|
<p className="text-lg leading-relaxed text-gray-800">{propUnderstanding}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Initial proposed findings */}
|
{/* Initial proposed findings — unknowns + plausible interpretations from reconstruction */}
|
||||||
<div className="space-y-3" data-testid="initial-proposed-findings">
|
<div className="space-y-3" data-testid="initial-proposed-findings">
|
||||||
<h2 className="text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
<h2 className="text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
||||||
Initial proposed findings
|
Open Questions
|
||||||
</h2>
|
</h2>
|
||||||
{(() => {
|
{(() => {
|
||||||
const resolvedIds = new Set(graph?.resolvedNodeIds || []);
|
const resolvedIds = new Set(graph?.resolvedNodeIds || []);
|
||||||
|
// Surface only candidate items from the semantic reconstruction that are worth investigating:
|
||||||
|
// — unknowns (importantUnknowns from the LLM's reconstruction)
|
||||||
|
// — assumptions (plausibleInterpretations from the LLM's reconstruction)
|
||||||
|
// Skips observations, states, relationships, transitions — these are already established facts/context.
|
||||||
|
// Both kinds check status !== "resolved" and excluded resolvedIds to mirror OpenQuestionsPanel logic.
|
||||||
|
const candidateKinds = ["unknown", "assumption"];
|
||||||
return (
|
return (
|
||||||
graph?.nodes?.map((node) =>
|
(graph?.nodes || [])
|
||||||
node.kind === "unknown" && node.status !== "resolved" && !resolvedIds.has(node.id) ? (
|
.filter(
|
||||||
<button
|
(n) =>
|
||||||
key={node.id}
|
candidateKinds.includes(n.kind) &&
|
||||||
onClick={() => startFocused(node.id)}
|
n.status !== "resolved" &&
|
||||||
style={{ cursor: "pointer" }}
|
!resolvedIds.has(n.id),
|
||||||
className="w-full text-left rounded-lg border border-gray-200 bg-white px-5 py-4 transition hover:border-gray-300 hover:bg-gray-50"
|
)
|
||||||
>
|
.map((node) => {
|
||||||
<span className="block text-sm leading-relaxed text-gray-900">{node.label}</span>
|
const tag = node.kind === "assumption" ? "Plausible interpretation" : "Unclear";
|
||||||
{node.description && node.description !== node.label && (
|
return (
|
||||||
<p className="mt-1.5 text-xs leading-snug text-gray-500">{node.description}</p>
|
<button
|
||||||
)}
|
key={node.id}
|
||||||
</button>
|
onClick={() => startFocused(node.id)}
|
||||||
) : null,
|
style={{ cursor: "pointer" }}
|
||||||
) || []
|
className="w-full text-left rounded-lg border border-gray-200 bg-white px-5 py-4 transition hover:border-gray-300 hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
<span className="block text-sm leading-relaxed text-gray-900">{node.label}</span>
|
||||||
|
{node.description && node.description !== node.label && (
|
||||||
|
<p className="mt-1.5 text-xs leading-snug text-gray-500">{node.description}</p>
|
||||||
|
)}
|
||||||
|
<span className="mt-2 block text-[10px] uppercase tracking-wider text-gray-400">{tag}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Situation panel during initial reflection */}
|
||||||
|
{hasGraph && (
|
||||||
|
<div className="space-y-6 lg:col-span-1">
|
||||||
|
<OriginalSituation scenario={scenario} centralStatement={graph?.centralStatement} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!hasGraph && scenario && (
|
||||||
|
<div className="space-y-6 lg:col-span-1">
|
||||||
|
<div className="rounded-lg border border-gray-200/60 bg-gray-50/40 px-5 py-4">
|
||||||
|
<h2 className="mb-2 text-[11px] font-semibold tracking-widest uppercase text-gray-500">
|
||||||
|
Situation
|
||||||
|
</h2>
|
||||||
|
<p className="whitespace-pre-wrap text-sm leading-relaxed text-gray-600">
|
||||||
|
{scenario}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1269,6 +1337,7 @@ export default function ReasoningWorkspace({
|
|||||||
setFocusedAnswer={setFocusedAnswer}
|
setFocusedAnswer={setFocusedAnswer}
|
||||||
focusedAnswer={focusedAnswer}
|
focusedAnswer={focusedAnswer}
|
||||||
setDoneForNowIds={setDoneForNowIds}
|
setDoneForNowIds={setDoneForNowIds}
|
||||||
|
setFollowUpQuestion={setFollowUpQuestion}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user