feat: add investigation map workspace view
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Investigation Map — user-facing workspace card.
|
||||
*
|
||||
* Shows the progress of reasoning as a set of investigation topics with
|
||||
* simple status indicators. Does NOT expose graph internals.
|
||||
*
|
||||
* Design principles:
|
||||
* - Calm, spacious, accessible
|
||||
* - No percentages, no progress bars, no confidence scores
|
||||
* - Topics evolve naturally across turns
|
||||
*/
|
||||
|
||||
import getInvestigationMapTopics from "@/lib/map/investigation-map-adapter";
|
||||
|
||||
/* ── Status icons (unicode — no icon library dependency) ─── */
|
||||
|
||||
const STATUS_ICONS = {
|
||||
established: "✓",
|
||||
current: "●",
|
||||
unknown: "○",
|
||||
};
|
||||
|
||||
function topicRowColor(status) {
|
||||
switch (status) {
|
||||
case "established":
|
||||
return "text-gray-900";
|
||||
case "current":
|
||||
return "text-blue-800";
|
||||
default:
|
||||
return "text-gray-400";
|
||||
}
|
||||
}
|
||||
|
||||
function topicIconColor(status) {
|
||||
switch (status) {
|
||||
case "established":
|
||||
return "text-green-600";
|
||||
case "current":
|
||||
return "text-blue-500";
|
||||
default:
|
||||
return "text-gray-300";
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Single topic row ───────────────────────────────────── */
|
||||
|
||||
function TopicRow({ title, status }) {
|
||||
const icon = STATUS_ICONS[status];
|
||||
const colorClass = topicRowColor(status);
|
||||
const iconColor = topicIconColor(status);
|
||||
const ariaLabel = `${status === "established" ? "Established" : status === "current" ? "Currently investigating" : "Still to explore"}: ${title}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex items-center gap-3 py-2.5 text-sm transition-opacity duration-300 ease-in-out ${colorClass}`}
|
||||
aria-label={ariaLabel}
|
||||
role="listitem"
|
||||
data-testid={`map-topic-${status === "established" ? "established" : status === "current" ? "current" : "unknown"}`}
|
||||
>
|
||||
<span className={`flex-none text-base ${iconColor} leading-none`} aria-hidden="true">
|
||||
{icon}
|
||||
</span>
|
||||
<span className="flex-1">{title}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── Card wrapper ────────────────────────────────────────── */
|
||||
|
||||
export default function InvestigationMap({ turnCount = 0 }) {
|
||||
const topics = getInvestigationMapTopics(turnCount);
|
||||
|
||||
// Group topics by status for cleaner rendering
|
||||
const groups = {
|
||||
established: topics.filter((t) => t.status === "established"),
|
||||
current: topics.filter((t) => t.status === "current"),
|
||||
unknown: topics.filter((t) => t.status === "unknown"),
|
||||
};
|
||||
|
||||
// Only render the card if there are non-established topics (during active investigation)
|
||||
const hasActiveTopics = groups.current.length > 0 || groups.unknown.length > 0;
|
||||
if (!hasActiveTopics && groups.established.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-gray-200 bg-white p-5" role="region" aria-label="Investigation map">
|
||||
<h2 className="mb-1 text-sm font-semibold uppercase tracking-wide text-gray-500">
|
||||
Investigation Map
|
||||
</h2>
|
||||
<p className="mb-3 text-xs text-gray-400">
|
||||
We are building an understanding of your situation one question at a time.
|
||||
</p>
|
||||
|
||||
<div className="space-y-px border-t border-gray-100 pt-3" role="list" aria-label="Investigation topics">
|
||||
{topics.map((topic, i) => (
|
||||
<TopicRow key={`${topic.title}-${i}`} title={topic.title} status={topic.status} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import DiagnosticsView from "@/components/diagnostics-view";
|
||||
import GraphUpdateView from "@/components/graph-update-view";
|
||||
import SituationGraphView from "@/components/situation-graph-view";
|
||||
import InvestigationSummaryPanel from "@/components/investigation-summary-panel";
|
||||
import InvestigationMap from "@/components/investigation-map";
|
||||
|
||||
// ── Technical summary detector (main view filters these) ───
|
||||
const TECHNICAL_PATTERNS = [
|
||||
@@ -733,6 +734,11 @@ export default function ReasoningWorkspace({
|
||||
<CurrentUnderstandingCard currentSummary={graph?.currentSummary || result?.updatedSituationGraph?.currentSummary} plainLanguage={propUnderstanding || null} />
|
||||
)}
|
||||
|
||||
{/* ── Investigation Map (visible during active investigation) ─ */}
|
||||
{hasSelectedQuestion && (
|
||||
<InvestigationMap turnCount={investigationHistory.length} />
|
||||
)}
|
||||
|
||||
<OriginalSituation scenario={scenario} centralStatement={graph?.centralStatement} />
|
||||
|
||||
{investigationHistory.length > 0 && <InvestigationHistory turns={investigationHistory} />}
|
||||
|
||||
Reference in New Issue
Block a user