Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5dcb8b0
Add interactive click-through diagram navigation system
anegg0 Nov 21, 2025
31c5bb3
Add comprehensive documentation for interactive diagram system
anegg0 Nov 21, 2025
a5566e3
relocate INTERACTIVE-DIAGRAMS-README.md to src/components/MermaidReac…
anegg0 Nov 21, 2025
c92dcbb
Fix useColorMode React Context error during hydration
anegg0 Nov 22, 2025
b268239
Fix dark mode styling for nodes and back button
anegg0 Nov 22, 2025
7966afe
Remove background grid from React Flow diagrams
anegg0 Nov 22, 2025
fb5f528
Add smooth crossfade transitions between diagram renderings
anegg0 Nov 22, 2025
b5ffe2f
better transition
anegg0 Dec 2, 2025
d2d63fb
Remove React Flow Controls panel from MermaidReactFlow component
anegg0 Dec 2, 2025
0ffd601
fix color of clickable nodes and update overview diagram
anegg0 Dec 2, 2025
3d7cb74
update overview diagram
anegg0 Dec 2, 2025
6198da5
Increase border-radius for MermaidReactFlow container
anegg0 Dec 2, 2025
a9faa6c
enlarge test diagram
anegg0 Dec 5, 2025
a7c5fa4
delete misplaced diagram files
anegg0 Dec 16, 2025
50ff2a9
add mermaid diagrams examples
anegg0 Dec 17, 2025
e03a50b
Add three-phase subgraph layout algorithm
anegg0 Dec 17, 2025
f8872fe
Add SubgraphNode component for container rendering
anegg0 Dec 17, 2025
f858592
Register SubgraphNode type and improve centering
anegg0 Dec 17, 2025
e6d169c
Add shape-based color system and subgraph styling
anegg0 Dec 17, 2025
cea93e1
Add test diagrams for subgraph validation
anegg0 Dec 17, 2025
0daef31
Fix SSR build error on test page
anegg0 Dec 17, 2025
f014fd7
Update documentation for subgraph support
anegg0 Dec 17, 2025
c05dc1f
Enhance edge and arrow styling with rotating colors
anegg0 Dec 17, 2025
a4f7245
fix: Remove CSS overrides that made edges invisible
anegg0 Dec 17, 2025
ff649d4
fix: Add defaultEdgeOptions to ensure edge visibility
anegg0 Dec 17, 2025
ae55e58
fix: Add Handle components to ClickableNode for edge connections
anegg0 Dec 17, 2025
2e03234
add decently rendered diagram
anegg0 Dec 17, 2025
afcd0bc
fix: Prevent rotation transform on subgraph containers
anegg0 Dec 17, 2025
f12665f
fix: Force transform none on group nodes with !important
anegg0 Dec 17, 2025
fc4d53e
change demo diagram
anegg0 Dec 17, 2025
62dca1b
Merge branch 'master' into reactflow-interactive-diagrams
anegg0 Dec 17, 2025
5aa5c2a
fix additional rendering issues with subgraph nodes
anegg0 Dec 17, 2025
87362ef
Merge branch 'master' into reactflow-interactive-diagrams
anegg0 Dec 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/test-interactive-diagrams.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
id: test-interactive-diagrams
title: 'Interactive Diagram Test'
description: 'Test page for click-through interactive diagrams'
---

import MermaidReactFlow from '@site/src/components/MermaidReactFlow';

This page demonstrates the click-through diagram navigation system. Click on any **blue node** to navigate to detailed views.

## Arbitrum Architecture Overview

Click on the blue nodes below to explore different components of the Arbitrum system:

<MermaidReactFlow diagramFile="/diagrams/stylus-learning-paths.mmd" height="800px" />

## How to Use

1. **Click on blue nodes** - These are clickable and will load a new diagram
2. **Gray nodes** - These are informational only and not clickable
3. **Back button** - Appears in the top-left when you navigate to a new diagram

## Navigation Flow Example

Starting from the overview diagram above, try this navigation path:

1. Click on **"Ethereum L1"** → Shows Layer 1 components
2. Click on **"Inbox Contract"** → Shows inbox message flow details
3. Click **"Back"** button → Returns to Ethereum L1 view
4. Click **"Back"** again → Returns to overview

## Features

- **Theme-aware**: Diagrams adapt to light/dark mode
- **Keyboard accessible**: Use Tab to focus nodes, Enter/Space to navigate
- **Responsive**: Works on mobile, tablet, and desktop
- **History-based navigation**: Back button follows your navigation path

## Try Different Paths

You can also try:

- Overview → **Arbitrum L2** → explore Layer 2 components
- Ethereum Details → **Rollup Contract** (if implemented)

The navigation is non-linear - you can explore in any order and always return using the back button.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,29 @@
"@notionhq/client": "^2.2.3",
"@radix-ui/react-dialog": "^1.0.5",
"@react-spring/web": "^9.7.3",
"@types/dagre": "^0.7.53",
"@types/mdx": "^2.0.11",
"@types/prismjs": "^1.26.5",
"@types/react-syntax-highlighter": "^15.5.13",
"classnames": "^2.5.1",
"clsx": "^1.2.1",
"dagre": "^0.8.5",
"docusaurus-plugin-fathom": "^1.2.0",
"docusaurus-plugin-sass": "^0.2.5",
"dotenv": "^17.0.1",
"ethers": "5.7.2",
"got": "11.8.5",
"marked": "^15.0.7",
"mermaid": "^10.0.0",
"motion": "^12.23.24",
"posthog-docusaurus": "^2.0.0",
"posthog-js": "^1.219.6",
"prism-react-renderer": "^1.3.5",
"prismjs": "^1.29.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-syntax-highlighter": "^15.6.1",
"reactflow": "^11.11.4",
"rehype-katex": "7",
"remark-math": "6",
"sass": "^1.66.1",
Expand Down
5 changes: 5 additions & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ const sidebars = {
id: 'get-started/arbitrum-introduction',
label: 'Arbitrum: introduction',
},
{
type: 'doc',
id: 'test-interactive-diagrams',
label: 'Test interactive diagrams',
},
{
type: 'category',
label: 'Build apps',
Expand Down
73 changes: 73 additions & 0 deletions src/components/MermaidReactFlow/ClickableNode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { NodeProps, Handle, Position } from 'reactflow';
import { NodeData } from './types';

export function ClickableNode({ data }: NodeProps<NodeData>) {
const { label, shape, colors, link, onNavigate } = data;
const hasLink = !!link;

const handleClick = () => {
if (hasLink && link && onNavigate) {
onNavigate(link);
}
};

const handleKeyDown = (e: React.KeyboardEvent) => {
if (hasLink && (e.key === 'Enter' || e.key === ' ')) {
e.preventDefault();
if (link && onNavigate) {
onNavigate(link);
}
}
};

const baseStyle: React.CSSProperties = {
padding: '12px 20px',
fontFamily: 'var(--ifm-font-family-base)',
fontSize: '14px',
// Color is handled by CSS to support theme switching
transition: 'all 0.2s ease',
cursor: hasLink ? 'pointer' : 'default',
backgroundColor: colors.backgroundColor,
borderColor: colors.borderColor,
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
wordWrap: 'break-word',
whiteSpace: 'pre-wrap',
};

// Apply shape-specific transforms
let shapeStyle: React.CSSProperties = {};
if (shape === 'diamond') {
shapeStyle = {
transform: 'rotate(45deg)',
};
}

const contentStyle: React.CSSProperties =
shape === 'diamond' ? { transform: 'rotate(-45deg)' } : {};

return (
<div
className={`custom-node ${hasLink ? 'node-clickable' : 'node-static'} shape-${shape}`}
style={{ ...baseStyle, ...shapeStyle }}
onClick={handleClick}
onKeyDown={handleKeyDown}
role={hasLink ? 'button' : undefined}
tabIndex={hasLink ? 0 : -1}
aria-label={hasLink ? `Navigate to ${link}` : label}
>
{/* Connection handles for edges */}
<Handle type="target" position={Position.Top} />
<Handle type="source" position={Position.Bottom} />

<div className="node-content" style={contentStyle}>
{label}
</div>
</div>
);
}
170 changes: 170 additions & 0 deletions src/components/MermaidReactFlow/NavigableDiagram.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import ReactFlow from 'reactflow';
import { useColorMode } from '@docusaurus/theme-common';
import { motion, AnimatePresence } from 'motion/react';
import 'reactflow/dist/style.css';
import { ClickableNode } from './ClickableNode';
import { SubgraphNode } from './SubgraphNode';
import { convertMermaidToReactFlow } from './utils/mermaidToReactFlow';
import { ReactFlowData, MermaidReactFlowProps } from './types';

// Safe hook to handle context not being available during hydration
function useSafeColorMode() {
try {
const { colorMode } = useColorMode();
return colorMode;
} catch (error) {
// Fallback to light mode if context is not available
return 'light';
}
}

export function NavigableDiagram({
diagramFile,
height = '500px',
className = '',
}: MermaidReactFlowProps) {
const colorMode = useSafeColorMode();
const [flowData, setFlowData] = useState<ReactFlowData>({ nodes: [], edges: [] });
const [currentDiagram, setCurrentDiagram] = useState<string>(diagramFile);
const [history, setHistory] = useState<string[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

// Custom node types
const nodeTypes = useMemo(() => ({ custom: ClickableNode, group: SubgraphNode }), []);

// Default edge options to ensure visibility
const defaultEdgeOptions = useMemo(
() => ({
style: { strokeWidth: 2, stroke: '#1976D2' },
animated: false,
}),
[],
);

// Handle navigation to a new diagram
const handleNavigate = useCallback(
(link: string) => {
// Add current diagram to history
setHistory((prev) => [...prev, currentDiagram]);
setCurrentDiagram(link);
},
[currentDiagram],
);

// Handle back button
const handleBack = useCallback(() => {
if (history.length > 0) {
const previousDiagram = history[history.length - 1];
setHistory((prev) => prev.slice(0, -1));
setCurrentDiagram(previousDiagram);
}
}, [history]);

// Load diagram from file
useEffect(() => {
const loadDiagram = async () => {
setLoading(true);
setError(null);

try {
// Fetch the .mmd file
const response = await fetch(currentDiagram);
if (!response.ok) {
throw new Error(`Failed to load diagram: ${currentDiagram}`);
}

const mermaidCode = await response.text();

// Convert to React Flow
const data = await convertMermaidToReactFlow(mermaidCode, handleNavigate);
setFlowData(data);
} catch (err) {
console.error('Error loading diagram:', err);
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};

loadDiagram();
}, [currentDiagram, handleNavigate]);

if (loading) {
return (
<div
className={`mermaid-reactflow-container ${className}`}
style={{ height, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
>
<div>Loading diagram...</div>
</div>
);
}

if (error) {
return (
<div
className={`mermaid-reactflow-container ${className}`}
style={{ height, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
>
<div style={{ color: 'var(--ifm-color-danger)' }}>Error: {error}</div>
</div>
);
}

return (
<div
className={`mermaid-reactflow-container ${className}`}
data-theme={colorMode}
style={{ height, position: 'relative' }}
>
{history.length > 0 && (
<div className="diagram-nav-controls">
<button
className="back-button"
onClick={handleBack}
aria-label="Go back to previous diagram"
>
<span className="back-icon">←</span>
Back
</button>
</div>
)}
<AnimatePresence mode="wait">
<motion.div
key={currentDiagram}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{
opacity: {
duration: 1.5,
ease: 'easeInOut',
},
exit: {
duration: 2.2,
ease: 'easeOut',
},
}}
style={{ width: '100%', height: '100%' }}
>
<ReactFlow
nodes={flowData.nodes}
edges={flowData.edges}
nodeTypes={nodeTypes}
defaultEdgeOptions={defaultEdgeOptions}
fitView
fitViewOptions={{
padding: 0.2,
includeHiddenNodes: false,
minZoom: 0.5,
maxZoom: 1.5,
}}
attributionPosition="bottom-right"
/>
</motion.div>
</AnimatePresence>
</div>
);
}
Loading