diff --git a/packages/dashboard/src/App.tsx b/packages/dashboard/src/App.tsx
index 13c4142..c633bb1 100644
--- a/packages/dashboard/src/App.tsx
+++ b/packages/dashboard/src/App.tsx
@@ -7,10 +7,12 @@ import SearchBar from "./components/SearchBar";
import NodeInfo from "./components/NodeInfo";
import ChatPanel from "./components/ChatPanel";
import LayerLegend from "./components/LayerLegend";
+import LearnPanel from "./components/LearnPanel";
function App() {
const graph = useDashboardStore((s) => s.graph);
const setGraph = useDashboardStore((s) => s.setGraph);
+ const tourActive = useDashboardStore((s) => s.tourActive);
useEffect(() => {
fetch("/knowledge-graph.json")
@@ -21,6 +23,7 @@ function App() {
const nodeCount = graph?.nodes.length ?? 0;
const edgeCount = graph?.edges.length ?? 0;
+ const hasTour = (graph?.tour.length ?? 0) > 0;
return (
-
+ {/* Bottom-right: Node Info / Learn Panel */}
+
+ {hasTour ? (
+ <>
+ {/* Tab bar */}
+
+
+
+
+ {/* Active panel */}
+
+ {tourActive ? : }
+
+ >
+ ) : (
+
+ )}
diff --git a/packages/dashboard/src/components/LearnPanel.tsx b/packages/dashboard/src/components/LearnPanel.tsx
new file mode 100644
index 0000000..c1641bb
--- /dev/null
+++ b/packages/dashboard/src/components/LearnPanel.tsx
@@ -0,0 +1,226 @@
+import ReactMarkdown from "react-markdown";
+import { useDashboardStore } from "../store";
+
+export default function LearnPanel() {
+ const graph = useDashboardStore((s) => s.graph);
+ const tourActive = useDashboardStore((s) => s.tourActive);
+ const currentTourStep = useDashboardStore((s) => s.currentTourStep);
+ const startTour = useDashboardStore((s) => s.startTour);
+ const stopTour = useDashboardStore((s) => s.stopTour);
+ const setTourStep = useDashboardStore((s) => s.setTourStep);
+ const nextTourStep = useDashboardStore((s) => s.nextTourStep);
+ const prevTourStep = useDashboardStore((s) => s.prevTourStep);
+ const selectNode = useDashboardStore((s) => s.selectNode);
+
+ const tourSteps = graph?.tour
+ ? [...graph.tour].sort((a, b) => a.order - b.order)
+ : [];
+ const hasTour = tourSteps.length > 0;
+
+ // State 1: No tour available
+ if (!hasTour) {
+ return (
+
+ {/* Header with progress counter and exit */}
+
+
+
+ Tour
+
+
+ {currentTourStep + 1} / {totalSteps}
+
+
+
+
+
+ {/* Progress bar */}
+
+
+ {/* Scrollable content */}
+
+ {/* Step title */}
+
{step.title}
+
+ {/* Description via ReactMarkdown */}
+
+
(
+ {children}
+ ),
+ strong: ({ children }) => (
+ {children}
+ ),
+ code: ({ className, children }) => {
+ const isBlock = className?.includes("language-");
+ return isBlock ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ );
+ },
+ ul: ({ children }) => (
+
+ ),
+ ol: ({ children }) => (
+
+ {children}
+
+ ),
+ }}
+ >
+ {step.description}
+
+
+
+ {/* Language lesson */}
+ {step.languageLesson && (
+
+
+ Language Lesson
+
+
+ {step.languageLesson}
+
+
+ )}
+
+ {/* Referenced component pills */}
+ {step.nodeIds.length > 0 && (
+
+
+ Referenced Components
+
+
+ {step.nodeIds.map((nodeId) => {
+ const node = graph?.nodes.find((n) => n.id === nodeId);
+ return (
+
+ );
+ })}
+
+
+ )}
+
+
+ {/* Navigation: dots + prev/next */}
+
+ {/* Step dots */}
+
+ {tourSteps.map((_, i) => (
+
+
+ {/* Prev / Next buttons */}
+
+
+
+
+
+
+ );
+}
diff --git a/packages/dashboard/src/store.ts b/packages/dashboard/src/store.ts
index e65f67d..c208f8f 100644
--- a/packages/dashboard/src/store.ts
+++ b/packages/dashboard/src/store.ts
@@ -22,6 +22,10 @@ interface DashboardStore {
showLayers: boolean;
+ tourActive: boolean;
+ currentTourStep: number;
+ tourHighlightedNodeIds: string[];
+
setGraph: (graph: KnowledgeGraph) => void;
selectNode: (nodeId: string | null) => void;
setSearchQuery: (query: string) => void;
@@ -29,6 +33,12 @@ interface DashboardStore {
sendChatMessage: (message: string) => Promise