diff --git a/docs/for-devs/lidia-flow-interactive-diagrams.mdx b/docs/for-devs/lidia-flow-interactive-diagrams.mdx new file mode 100644 index 0000000000..9cd263e0f5 --- /dev/null +++ b/docs/for-devs/lidia-flow-interactive-diagrams.mdx @@ -0,0 +1,133 @@ +--- +title: LidiaFlow Interactive Diagrams +description: Learn how to use LidiaFlow, a ReactFlow-based component for creating interactive, click-to-explore diagrams in your Arbitrum documentation +--- + +import LidiaFlowDiagram from '@site/src/components/LidiaFlow'; +import exampleConfig from '@site/static/diagrams/lidia-flow-example/config.json'; + +# LidiaFlow Interactive Diagrams + +LidiaFlow is a ReactFlow-based component that enables interactive, click-to-morph diagrams for exploring complex technical concepts. Built on top of ReactFlow, it provides a more powerful and flexible alternative to traditional SVG-based diagrams while maintaining the simplicity of click-to-explore navigation. + +## Example + + + +## How it works + +- **Click** on nodes with blue highlights to explore deeper levels of detail +- **Navigate back** using the back button to return to previous states +- **Tooltips** appear on hover to guide your exploration +- **Mini-map** in the corner helps you navigate complex diagrams +- **Zoom and pan** to explore large diagrams with ease + +## Using LidiaFlow in your documentation + +To add interactive diagrams to your documentation: + +1. Define a configuration file with nodes, edges, and navigation flow +2. Import and use the `LidiaFlowDiagram` component in your MDX files + +### Example usage + +```mdx +import LidiaFlowDiagram from '@site/src/components/LidiaFlow'; +import myDiagramConfig from '@site/static/diagrams/my-diagram/config.json'; + + +``` + +### Configuration structure + +```json +{ + "id": "diagram-id", + "title": "Diagram Title", + "initialStateId": "overview", + "states": [ + { + "id": "overview", + "nodes": [ + { + "id": "1", + "type": "clickable", + "position": { "x": 250, "y": 0 }, + "data": { + "label": "Component Name", + "isClickable": true, + "tooltip": "Click to explore this component" + } + } + ], + "edges": [ + { + "id": "e1-2", + "source": "1", + "target": "2", + "animated": true + } + ], + "clickableAreas": [ + { + "nodeId": "1", + "nextStateId": "detail-view" + } + ] + } + ] +} +``` + +## Configuration Reference + +### Top-level configuration + +- `id`: Unique identifier for the diagram +- `title`: Title displayed in the diagram header +- `initialStateId`: ID of the state to show first +- `states`: Array of diagram states + +### State configuration + +- `id`: Unique identifier for the state +- `nodes`: Array of ReactFlow nodes +- `edges`: Array of ReactFlow edges +- `clickableAreas`: Array defining which nodes navigate to which states + +### Node configuration + +- `id`: Unique node identifier +- `type`: Node type (use `"clickable"` for interactive nodes) +- `position`: `{ x, y }` coordinates +- `data`: Node data including: + - `label`: Text displayed in the node + - `isClickable`: Whether the node can be clicked + - `tooltip`: Hover text for guidance + +### Edge configuration + +- `id`: Unique edge identifier +- `source`: Source node ID +- `target`: Target node ID +- `animated`: Optional boolean for animated edges + +## Best practices + +- Keep diagrams simple and focused on one concept per state +- Use clear, descriptive labels for nodes +- Provide helpful tooltips to guide users +- Limit navigation depth to 3-5 levels for optimal user experience +- Use the mini-map for complex diagrams with many nodes +- Consider using animated edges to show data flow + +## Advanced features + +LidiaFlow leverages ReactFlow's powerful features: + +- **Mini-map navigation** for large diagrams +- **Zoom controls** for detailed exploration +- **Smooth transitions** between states +- **Custom node types** (extend with your own components) +- **Keyboard navigation** support +- **Touch device** compatibility diff --git a/docs/for-devs/lidia-interactive-diagrams.mdx b/docs/for-devs/lidia-interactive-diagrams.mdx new file mode 100644 index 0000000000..45c7d75c74 --- /dev/null +++ b/docs/for-devs/lidia-interactive-diagrams.mdx @@ -0,0 +1,69 @@ +--- +title: Lidia Interactive Diagrams +description: Learn how to use Lidia, a React component for creating interactive, click-to-morph diagrams in your Arbitrum documentation +--- + +import LidiaDiagram from '@site/src/components/Lidia'; +import exampleConfig from '@site/static/diagrams/example/config.json'; + +# Lidia Interactive Diagrams + +Lidia is a React component that enables interactive, click-to-morph diagrams for exploring complex technical concepts. It's particularly useful for visualizing multi-layered architectures, workflows, and system components. + +## Example + + + +## How it works + +- **Click** on highlighted areas to explore deeper levels of detail +- **Navigate back** using the back button to return to previous states +- **Tooltips** appear on hover to guide your exploration + +## Using Lidia in your documentation + +To add interactive diagrams to your documentation: + +1. Create SVG diagrams with clickable areas (elements with unique IDs) +2. Define a configuration file mapping the navigation flow +3. Import and use the `LidiaDiagram` component in your MDX files + +### Example usage + +```mdx +import LidiaDiagram from '@site/src/components/Lidia'; +import myDiagramConfig from '@site/static/diagrams/my-diagram/config.json'; + + +``` + +### Configuration structure + +```json +{ + "id": "diagram-id", + "title": "Diagram Title", + "initialStateId": "overview", + "states": [ + { + "id": "overview", + "svgPath": "/diagrams/my-diagram/overview.svg", + "clickableAreas": [ + { + "id": "area-1", + "selector": "#svg-element-id", + "nextStateId": "detail-view", + "tooltip": "Click to explore" + } + ] + } + ] +} +``` + +## Best practices + +- Keep diagrams simple and focused on one concept per state +- Use clear, descriptive IDs for clickable areas +- Provide helpful tooltips to guide users +- Limit navigation depth to 3-5 levels for optimal user experience diff --git a/sidebars.js b/sidebars.js index 107f5e85d7..db8a394adb 100644 --- a/sidebars.js +++ b/sidebars.js @@ -46,6 +46,16 @@ const sidebars = { id: 'for-devs/dev-tools-and-resources/chain-info', label: 'Chain info', }, + { + type: 'doc', + label: 'Lidia Interactive Diagrams', + id: 'for-devs/lidia-interactive-diagrams', + }, + { + type: 'doc', + label: 'LidiaFlow Interactive Diagrams', + id: 'for-devs/lidia-flow-interactive-diagrams', + }, ], }, { diff --git a/src/css/custom.css b/src/css/custom.css index dbe0b5f53f..e959e8ee4d 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -98,3 +98,6 @@ .wrapcode code { white-space: pre-wrap; } + +/* Import Lidia component styles */ +@import './lidia.css'; diff --git a/src/css/lidia-flow.css b/src/css/lidia-flow.css new file mode 100644 index 0000000000..625aaebadb --- /dev/null +++ b/src/css/lidia-flow.css @@ -0,0 +1,152 @@ +/* Lidia Flow Container */ +.lidia-flow-container { + width: 100%; + height: 600px; + border: 1px solid #e5e7eb; + border-radius: 8px; + overflow: hidden; + background-color: #ffffff; +} + +/* Header */ +.lidia-flow-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 24px; + background-color: #f9fafb; + border-bottom: 1px solid #e5e7eb; +} + +.lidia-flow-title { + font-size: 18px; + font-weight: 600; + color: #1f2937; + margin: 0; +} + +.lidia-flow-back-button { + padding: 8px 16px; + background-color: #3b82f6; + color: white; + border: none; + border-radius: 6px; + font-size: 14px; + cursor: pointer; + transition: all 0.2s ease; +} + +.lidia-flow-back-button:hover { + background-color: #2563eb; +} + +.lidia-flow-back-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ReactFlow wrapper */ +.lidia-flow-wrapper { + height: calc(100% - 65px); + position: relative; + background-color: #fafafa; +} + +/* Transition effects */ +.lidia-flow-fade-enter { + opacity: 0; + transform: scale(0.95); +} + +.lidia-flow-fade-enter-active { + opacity: 1; + transform: scale(1); + transition: opacity 300ms ease-in-out, transform 300ms ease-in-out; +} + +.lidia-flow-fade-exit { + opacity: 1; + transform: scale(1); +} + +.lidia-flow-fade-exit-active { + opacity: 0; + transform: scale(0.95); + transition: opacity 300ms ease-in-out, transform 300ms ease-in-out; +} + +/* Custom Node Styles */ +.lidia-flow-node { + background: white; + border: 2px solid #e5e7eb; + border-radius: 8px; + padding: 16px 24px; + min-width: 150px; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); + transition: all 0.2s ease; +} + +.lidia-flow-node-clickable { + cursor: pointer; +} + +.lidia-flow-node-clickable:hover { + border-color: #3b82f6; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + transform: translateY(-2px); +} + +.lidia-flow-node-selected { + border-color: #3b82f6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +.lidia-flow-node-content { + text-align: center; +} + +.lidia-flow-node-label { + font-size: 16px; + font-weight: 600; + color: #1f2937; + margin-bottom: 4px; +} + +.lidia-flow-node-hint { + font-size: 12px; + color: #3b82f6; + margin-top: 8px; +} + +/* Handle styles */ +.lidia-flow-handle { + width: 8px; + height: 8px; + background-color: #3b82f6; + border: 2px solid white; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); +} + +/* ReactFlow overrides */ +.react-flow__node.selected .lidia-flow-node { + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +.react-flow__edge-path { + stroke: #9ca3af; + stroke-width: 2; +} + +.react-flow__edge.selected .react-flow__edge-path { + stroke: #3b82f6; +} + +/* Error state */ +.lidia-flow-error { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: #ef4444; + font-size: 16px; +} \ No newline at end of file diff --git a/src/css/lidia.css b/src/css/lidia.css new file mode 100644 index 0000000000..2a4cce00e3 --- /dev/null +++ b/src/css/lidia.css @@ -0,0 +1,96 @@ +.lidia-container { + margin: 2rem 0; + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: var(--ifm-border-radius); + padding: 1.5rem; + background: var(--ifm-background-surface-color); +} + +.lidia-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.lidia-title { + margin: 0; + font-size: 1.25rem; +} + +.lidia-back-button { + padding: 0.5rem 1rem; + background: var(--ifm-color-primary); + color: white; + border: none; + border-radius: var(--ifm-border-radius); + cursor: pointer; + font-size: 0.875rem; + transition: opacity 0.2s; +} + +.lidia-back-button:hover { + opacity: 0.8; +} + +.lidia-back-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.lidia-svg-container { + overflow: auto; + display: flex; + justify-content: center; + align-items: center; + min-height: 400px; + position: relative; +} + +.lidia-svg-wrapper { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + transition: opacity 0.6s ease-in-out; +} + +.lidia-svg-wrapper svg { + max-width: 100%; + height: auto; +} + +/* Crossfade animation classes */ +.lidia-svg-wrapper-entering { + opacity: 0; +} + +.lidia-svg-wrapper.lidia-fade-out { + opacity: 0; + position: absolute; + top: 0; + left: 0; + right: 0; + pointer-events: none; +} + +.lidia-svg-wrapper.lidia-fade-in { + opacity: 1; +} + +.lidia-clickable { + cursor: pointer; + transition: opacity 0.2s; +} + +.lidia-clickable:hover { + opacity: 0.7; +} + +.lidia-error { + padding: 1rem; + background: var(--ifm-color-danger-lightest); + color: var(--ifm-color-danger-dark); + border-radius: var(--ifm-border-radius); + margin: 1rem 0; +} \ No newline at end of file diff --git a/website/src/components/Lidia/LidiaDiagram.tsx b/website/src/components/Lidia/LidiaDiagram.tsx new file mode 100644 index 0000000000..4dc1bef57f --- /dev/null +++ b/website/src/components/Lidia/LidiaDiagram.tsx @@ -0,0 +1,136 @@ +import React, { useEffect, useRef, useState, useCallback } from 'react'; +import type { LidiaDiagramConfig } from './types'; +import { useLidiaState } from './hooks/useLidiaState'; + +interface LidiaDiagramProps { + config: LidiaDiagramConfig; +} + +export default function LidiaDiagram({ config }: LidiaDiagramProps) { + const { currentState, navigateToState, goBack, canGoBack } = useLidiaState(config); + const containerRef = useRef(null); + const [isTransitioning, setIsTransitioning] = useState(false); + const prevStateIdRef = useRef(null); + const transitioningRef = useRef(false); + + const handleNavigation = useCallback( + (nextStateId: string) => { + if (!transitioningRef.current) { + transitioningRef.current = true; + setIsTransitioning(true); + navigateToState(nextStateId); + } + }, + [navigateToState], + ); + + const handleBackClick = useCallback(() => { + if (!transitioningRef.current) { + transitioningRef.current = true; + setIsTransitioning(true); + goBack(); + } + }, [goBack]); + + useEffect(() => { + if (!currentState || !containerRef.current) return; + + // Skip if we're still showing the same state + if (prevStateIdRef.current === currentState.id) return; + + const loadAndSetupSvg = async () => { + try { + const response = await fetch(currentState.svgPath); + const svgText = await response.text(); + + // Create a new div for the incoming SVG + const newSvgDiv = document.createElement('div'); + newSvgDiv.className = 'lidia-svg-wrapper'; + newSvgDiv.innerHTML = svgText; + + const svg = newSvgDiv.querySelector('svg'); + if (!svg) return; + + // Set up clickable areas + currentState.clickableAreas.forEach((area) => { + const element = svg.querySelector(area.selector); + if (!element) return; + + element.classList.add('lidia-clickable'); + + if (area.tooltip) { + element.setAttribute('title', area.tooltip); + } + + element.addEventListener('click', () => { + handleNavigation(area.nextStateId); + }); + }); + + // Handle transition + const existingWrapper = containerRef.current.querySelector('.lidia-svg-wrapper'); + + if (existingWrapper && prevStateIdRef.current !== null) { + // Crossfade effect + newSvgDiv.classList.add('lidia-svg-wrapper-entering'); + containerRef.current.appendChild(newSvgDiv); + + // Force reflow + void newSvgDiv.offsetHeight; + + // Start transition + requestAnimationFrame(() => { + existingWrapper.classList.add('lidia-fade-out'); + newSvgDiv.classList.remove('lidia-svg-wrapper-entering'); + newSvgDiv.classList.add('lidia-fade-in'); + + // Clean up after transition + setTimeout(() => { + existingWrapper.remove(); + transitioningRef.current = false; + setIsTransitioning(false); + }, 600); + }); + } else { + // Initial load + if (existingWrapper) existingWrapper.remove(); + containerRef.current.appendChild(newSvgDiv); + transitioningRef.current = false; + setIsTransitioning(false); + } + + // Update the previous state reference + prevStateIdRef.current = currentState.id; + } catch (error) { + console.error('Error loading SVG:', error); + transitioningRef.current = false; + setIsTransitioning(false); + } + }; + + loadAndSetupSvg(); + }, [currentState, handleNavigation]); + + if (!currentState) { + return
Error: Invalid diagram state
; + } + + return ( +
+
+

{config.title}

+ {canGoBack && ( + + )} +
+
+
+ ); +} diff --git a/website/src/components/Lidia/hooks/useLidiaState.ts b/website/src/components/Lidia/hooks/useLidiaState.ts new file mode 100644 index 0000000000..6e237b42f4 --- /dev/null +++ b/website/src/components/Lidia/hooks/useLidiaState.ts @@ -0,0 +1,39 @@ +import { useState, useCallback } from 'react'; +import type { LidiaDiagramConfig, LidiaState } from '../types'; + +export function useLidiaState(config: LidiaDiagramConfig) { + const [state, setState] = useState({ + currentStateId: config.initialStateId, + history: [config.initialStateId], + }); + + const navigateToState = useCallback((stateId: string) => { + setState((prev) => ({ + currentStateId: stateId, + history: [...prev.history, stateId], + })); + }, []); + + const goBack = useCallback(() => { + setState((prev) => { + if (prev.history.length <= 1) return prev; + + const newHistory = prev.history.slice(0, -1); + return { + currentStateId: newHistory[newHistory.length - 1], + history: newHistory, + }; + }); + }, []); + + const canGoBack = state.history.length > 1; + + const currentState = config.states.find((s) => s.id === state.currentStateId); + + return { + currentState, + navigateToState, + goBack, + canGoBack, + }; +} diff --git a/website/src/components/Lidia/index.tsx b/website/src/components/Lidia/index.tsx new file mode 100644 index 0000000000..de9490e319 --- /dev/null +++ b/website/src/components/Lidia/index.tsx @@ -0,0 +1,2 @@ +export { default } from './LidiaDiagram'; +export type { LidiaDiagramConfig, DiagramState, ClickableArea } from './types'; diff --git a/website/src/components/Lidia/types.ts b/website/src/components/Lidia/types.ts new file mode 100644 index 0000000000..46986e9c0b --- /dev/null +++ b/website/src/components/Lidia/types.ts @@ -0,0 +1,24 @@ +export interface ClickableArea { + id: string; + selector: string; + nextStateId: string; + tooltip?: string; +} + +export interface DiagramState { + id: string; + svgPath: string; + clickableAreas: ClickableArea[]; +} + +export interface LidiaDiagramConfig { + id: string; + title: string; + states: DiagramState[]; + initialStateId: string; +} + +export interface LidiaState { + currentStateId: string; + history: string[]; +} diff --git a/website/src/components/LidiaFlow/ClickableNode.tsx b/website/src/components/LidiaFlow/ClickableNode.tsx new file mode 100644 index 0000000000..29bd7fc1c0 --- /dev/null +++ b/website/src/components/LidiaFlow/ClickableNode.tsx @@ -0,0 +1,25 @@ +import React, { memo } from 'react'; +import { Handle, Position, NodeProps } from '@xyflow/react'; +import type { ClickableNodeData } from './types'; + +const ClickableNode = memo(({ data, selected }: NodeProps) => { + return ( +
+ +
+
{data.label}
+ {data.isClickable &&
Click to explore
} +
+ +
+ ); +}); + +ClickableNode.displayName = 'ClickableNode'; + +export default ClickableNode; diff --git a/website/src/components/LidiaFlow/LidiaFlowDiagram.tsx b/website/src/components/LidiaFlow/LidiaFlowDiagram.tsx new file mode 100644 index 0000000000..f60c27a59b --- /dev/null +++ b/website/src/components/LidiaFlow/LidiaFlowDiagram.tsx @@ -0,0 +1,121 @@ +import React, { useCallback, useState, useEffect } from 'react'; +import { + ReactFlow, + Background, + Controls, + MiniMap, + useNodesState, + useEdgesState, + ReactFlowProvider, +} from '@xyflow/react'; +import type { Node, NodeMouseHandler } from '@xyflow/react'; +import '@xyflow/react/dist/style.css'; +import '../../css/lidia-flow.css'; + +import { useLidiaFlowState } from './hooks/useLidiaFlowState'; +import ClickableNode from './ClickableNode'; +import type { LidiaFlowConfig } from './types'; + +const nodeTypes = { + clickable: ClickableNode, +}; + +interface LidiaFlowDiagramProps { + config: LidiaFlowConfig; +} + +function LidiaFlowDiagramInner({ config }: LidiaFlowDiagramProps) { + const { currentState, navigateToState, goBack, canGoBack } = useLidiaFlowState(config); + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [isTransitioning, setIsTransitioning] = useState(false); + + // Update nodes and edges when state changes + useEffect(() => { + if (currentState) { + setNodes(currentState.nodes); + setEdges(currentState.edges); + } + }, [currentState, setNodes, setEdges]); + + const onNodeClick: NodeMouseHandler = useCallback( + (event, node) => { + if (isTransitioning) return; + + const clickableArea = currentState?.clickableAreas.find((area) => area.nodeId === node.id); + + if (clickableArea && clickableArea.nextStateId) { + setIsTransitioning(true); + setTimeout(() => { + navigateToState(clickableArea.nextStateId); + setIsTransitioning(false); + }, 300); + } + }, + [currentState, navigateToState, isTransitioning], + ); + + const handleBackClick = useCallback(() => { + if (!isTransitioning && canGoBack) { + setIsTransitioning(true); + setTimeout(() => { + goBack(); + setIsTransitioning(false); + }, 300); + } + }, [goBack, canGoBack, isTransitioning]); + + if (!currentState) { + return
Error: Invalid diagram state
; + } + + return ( +
+
+

{config.title}

+ {canGoBack && ( + + )} +
+
+ + + + { + const clickableArea = currentState.clickableAreas.find( + (area) => area.nodeId === node.id, + ); + return clickableArea ? '#3b82f6' : '#9ca3af'; + }} + /> + +
+
+ ); +} + +export default function LidiaFlowDiagram(props: LidiaFlowDiagramProps) { + return ( + + + + ); +} diff --git a/website/src/components/LidiaFlow/hooks/useLidiaFlowState.ts b/website/src/components/LidiaFlow/hooks/useLidiaFlowState.ts new file mode 100644 index 0000000000..7948bdd42a --- /dev/null +++ b/website/src/components/LidiaFlow/hooks/useLidiaFlowState.ts @@ -0,0 +1,44 @@ +import { useState, useCallback, useMemo } from 'react'; +import type { LidiaFlowConfig, LidiaFlowState } from '../types'; + +export function useLidiaFlowState(config: LidiaFlowConfig) { + const [state, setState] = useState({ + currentStateId: config.initialStateId, + history: [], + }); + + const currentState = useMemo( + () => config.states.find((s) => s.id === state.currentStateId), + [config.states, state.currentStateId], + ); + + const navigateToState = useCallback((stateId: string) => { + setState((prev) => ({ + currentStateId: stateId, + history: [...prev.history, prev.currentStateId], + })); + }, []); + + const goBack = useCallback(() => { + setState((prev) => { + if (prev.history.length === 0) return prev; + + const newHistory = [...prev.history]; + const previousStateId = newHistory.pop()!; + + return { + currentStateId: previousStateId, + history: newHistory, + }; + }); + }, []); + + const canGoBack = state.history.length > 0; + + return { + currentState, + navigateToState, + goBack, + canGoBack, + }; +} diff --git a/website/src/components/LidiaFlow/index.tsx b/website/src/components/LidiaFlow/index.tsx new file mode 100644 index 0000000000..fff17a1d1a --- /dev/null +++ b/website/src/components/LidiaFlow/index.tsx @@ -0,0 +1,2 @@ +export { default } from './LidiaFlowDiagram'; +export type { LidiaFlowConfig, DiagramState, ClickableArea } from './types'; diff --git a/website/src/components/LidiaFlow/types.ts b/website/src/components/LidiaFlow/types.ts new file mode 100644 index 0000000000..b7a7bd3762 --- /dev/null +++ b/website/src/components/LidiaFlow/types.ts @@ -0,0 +1,33 @@ +import type { Node, Edge } from '@xyflow/react'; + +export interface ClickableArea { + nodeId: string; + nextStateId: string; + tooltip?: string; +} + +export interface DiagramState { + id: string; + nodes: Node[]; + edges: Edge[]; + clickableAreas: ClickableArea[]; +} + +export interface LidiaFlowConfig { + id: string; + title: string; + states: DiagramState[]; + initialStateId: string; +} + +export interface LidiaFlowState { + currentStateId: string; + history: string[]; +} + +export interface ClickableNodeData { + label: string; + tooltip?: string; + isClickable: boolean; + nextStateId?: string; +} diff --git a/website/static/diagrams/example/config.json b/website/static/diagrams/example/config.json new file mode 100644 index 0000000000..c2a0719b40 --- /dev/null +++ b/website/static/diagrams/example/config.json @@ -0,0 +1,47 @@ +{ + "id": "example-diagram", + "title": "Example Interactive Diagram", + "initialStateId": "overview", + "states": [ + { + "id": "overview", + "svgPath": "/diagrams/example/overview.svg", + "clickableAreas": [ + { + "id": "component-a", + "selector": "#component-a", + "nextStateId": "detail-a", + "tooltip": "Click to explore Component A" + }, + { + "id": "component-b", + "selector": "#component-b", + "nextStateId": "detail-b", + "tooltip": "Click to explore Component B" + } + ] + }, + { + "id": "detail-a", + "svgPath": "/diagrams/example/detail-a.svg", + "clickableAreas": [ + { + "id": "sub-component-1", + "selector": "#sub-1", + "nextStateId": "deep-dive-1", + "tooltip": "Dive deeper into Sub-component 1" + } + ] + }, + { + "id": "detail-b", + "svgPath": "/diagrams/example/detail-b.svg", + "clickableAreas": [] + }, + { + "id": "deep-dive-1", + "svgPath": "/diagrams/example/deep-dive-1.svg", + "clickableAreas": [] + } + ] +} \ No newline at end of file diff --git a/website/static/diagrams/example/deep-dive-1.svg b/website/static/diagrams/example/deep-dive-1.svg new file mode 100644 index 0000000000..adeed26f69 --- /dev/null +++ b/website/static/diagrams/example/deep-dive-1.svg @@ -0,0 +1,30 @@ + + + + Sub-component 1 Deep Dive + + + + Input + + + Process + + + Output + + + + + + + + Implementation Details + + • Uses advanced algorithms + • Optimized for performance + • Handles edge cases gracefully + • Fully tested and documented + + This is the deepest level of detail + \ No newline at end of file diff --git a/website/static/diagrams/example/detail-a.svg b/website/static/diagrams/example/detail-a.svg new file mode 100644 index 0000000000..e34cbfca84 --- /dev/null +++ b/website/static/diagrams/example/detail-a.svg @@ -0,0 +1,30 @@ + + + + Component A Details + + + + Component A + + + + + Sub-component 1 + + + + + Sub-component 2 + + + + Sub-component 3 + + + + + + + Click Sub-component 1 to dive deeper + \ No newline at end of file diff --git a/website/static/diagrams/example/detail-b.svg b/website/static/diagrams/example/detail-b.svg new file mode 100644 index 0000000000..6cbd81be38 --- /dev/null +++ b/website/static/diagrams/example/detail-b.svg @@ -0,0 +1,30 @@ + + + + Component B Details + + + + Component B + + + + Module 1 + + + Module 2 + + + Module 3 + + + Module 4 + + + + + + + + Component B has no further drill-down + \ No newline at end of file diff --git a/website/static/diagrams/example/overview.svg b/website/static/diagrams/example/overview.svg new file mode 100644 index 0000000000..c0c959ee9d --- /dev/null +++ b/website/static/diagrams/example/overview.svg @@ -0,0 +1,20 @@ + + + + + + + Component A + + + + + + Component B + + + + + + Click on components to explore details + \ No newline at end of file diff --git a/website/static/diagrams/lidia-flow-example/config.json b/website/static/diagrams/lidia-flow-example/config.json new file mode 100644 index 0000000000..695b98b725 --- /dev/null +++ b/website/static/diagrams/lidia-flow-example/config.json @@ -0,0 +1,189 @@ +{ + "id": "arbitrum-architecture", + "title": "Arbitrum Architecture Overview", + "initialStateId": "overview", + "states": [ + { + "id": "overview", + "nodes": [ + { + "id": "1", + "type": "clickable", + "position": { "x": 250, "y": 0 }, + "data": { + "label": "Sequencer", + "isClickable": true, + "tooltip": "Click to explore the Sequencer components" + } + }, + { + "id": "2", + "type": "clickable", + "position": { "x": 100, "y": 150 }, + "data": { + "label": "L1 Contracts", + "isClickable": true, + "tooltip": "Click to explore L1 Contracts" + } + }, + { + "id": "3", + "type": "clickable", + "position": { "x": 400, "y": 150 }, + "data": { + "label": "Validators", + "isClickable": true, + "tooltip": "Click to explore Validator network" + } + }, + { + "id": "4", + "position": { "x": 250, "y": 300 }, + "data": { + "label": "User Transactions", + "isClickable": false + } + } + ], + "edges": [ + { "id": "e1-2", "source": "1", "target": "2", "animated": true }, + { "id": "e1-3", "source": "1", "target": "3", "animated": true }, + { "id": "e4-1", "source": "4", "target": "1" } + ], + "clickableAreas": [ + { "nodeId": "1", "nextStateId": "sequencer-detail" }, + { "nodeId": "2", "nextStateId": "l1-contracts-detail" }, + { "nodeId": "3", "nextStateId": "validators-detail" } + ] + }, + { + "id": "sequencer-detail", + "nodes": [ + { + "id": "s1", + "position": { "x": 250, "y": 0 }, + "data": { + "label": "Transaction Pool", + "isClickable": false + } + }, + { + "id": "s2", + "position": { "x": 250, "y": 100 }, + "data": { + "label": "Batch Creator", + "isClickable": false + } + }, + { + "id": "s3", + "position": { "x": 250, "y": 200 }, + "data": { + "label": "State Manager", + "isClickable": false + } + }, + { + "id": "s4", + "position": { "x": 250, "y": 300 }, + "data": { + "label": "L1 Publisher", + "isClickable": false + } + } + ], + "edges": [ + { "id": "es1-2", "source": "s1", "target": "s2" }, + { "id": "es2-3", "source": "s2", "target": "s3" }, + { "id": "es3-4", "source": "s3", "target": "s4" } + ], + "clickableAreas": [] + }, + { + "id": "l1-contracts-detail", + "nodes": [ + { + "id": "l1", + "position": { "x": 250, "y": 0 }, + "data": { + "label": "Rollup Contract", + "isClickable": false + } + }, + { + "id": "l2", + "position": { "x": 100, "y": 150 }, + "data": { + "label": "Inbox", + "isClickable": false + } + }, + { + "id": "l3", + "position": { "x": 400, "y": 150 }, + "data": { + "label": "Outbox", + "isClickable": false + } + }, + { + "id": "l4", + "position": { "x": 250, "y": 300 }, + "data": { + "label": "Challenge Manager", + "isClickable": false + } + } + ], + "edges": [ + { "id": "el1-2", "source": "l1", "target": "l2" }, + { "id": "el1-3", "source": "l1", "target": "l3" }, + { "id": "el1-4", "source": "l1", "target": "l4" } + ], + "clickableAreas": [] + }, + { + "id": "validators-detail", + "nodes": [ + { + "id": "v1", + "position": { "x": 250, "y": 0 }, + "data": { + "label": "Validator Node", + "isClickable": false + } + }, + { + "id": "v2", + "position": { "x": 100, "y": 150 }, + "data": { + "label": "State Sync", + "isClickable": false + } + }, + { + "id": "v3", + "position": { "x": 400, "y": 150 }, + "data": { + "label": "Fraud Proofs", + "isClickable": false + } + }, + { + "id": "v4", + "position": { "x": 250, "y": 300 }, + "data": { + "label": "Staking Module", + "isClickable": false + } + } + ], + "edges": [ + { "id": "ev1-2", "source": "v1", "target": "v2" }, + { "id": "ev1-3", "source": "v1", "target": "v3" }, + { "id": "ev1-4", "source": "v1", "target": "v4" } + ], + "clickableAreas": [] + } + ] +} \ No newline at end of file