Files
confidence-engine/components/investigation-map.jsx
T
robbond 46f2d12726 exp(06): focused investigation — visual hierarchy without layout changes
Emphasise the active investigation card through stronger elevation,
clearer borders, and improved spacing. Quiet supporting panels by
reducing border opacity, softening heading weight, and lowering
text contrast — making them available without competing for attention.

Facilitator card receives a warm surface tint to read as a briefing
card rather than a generic panel.

Documentation: close experiment 05 with findings, add experiment 06
to the design evolution log, add Attention Hierarchy to UX guidelines,
defer dark mode to a future Investigation Mode experiment.

Presentation changes only — no reasoning, prompts, graph, API, or
backend modifications.
2026-08-05 12:41:13 +01:00

100 lines
3.4 KiB
React

/**
* 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/60 bg-gray-50/30 p-4" role="region" aria-label="Investigation map preview">
<h2 className="mb-1 text-[11px] font-medium tracking-widest uppercase text-gray-300">
Investigation Map
</h2>
<p className="mb-3 text-xs text-gray-400/70">
Active investigation topics and their status.
</p>
<div className="space-y-px border-t border-gray-200/60 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>
);
}