Skip to content

Commit cdae15b

Browse files
committed
style: apply prettier formatting
Auto-format all source files with Prettier. Isolated in its own commit so git blame stays clean.
1 parent a6be6ce commit cdae15b

21 files changed

+921
-725
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,27 @@ An interactive educational webapp that visualizes how [Claude Code](https://gith
77
## Features
88

99
### Interactive Visualizer
10+
1011
- **Scenario-based learning**: Walk through real-world scenarios like file reads, multi-tool tasks, permission denials, and subagent delegation
1112
- **Step-by-step playback**: Use playback controls to step forward/backward through each execution step
1213
- **Live flowchart**: Watch the algorithm flow animate as you step through scenarios
1314
- **Detailed inspection**: Click any node to see payload data, tool inputs/outputs, and hook configurations
1415

1516
### Algorithm Overview
17+
1618
- **Complete reference diagram**: See the entire Claude Code algorithm as an interactive flowchart
1719
- **20 algorithm nodes** covering session lifecycle, context management, agentic loop, tool execution, hooks, subagents, and output
1820
- **Click for details**: Each node shows detailed explanations and code examples
1921

2022
### Comprehensive Documentation
23+
2124
- **Learn tab**: ~15 minute read covering all aspects of the algorithm
2225
- **ASCII diagrams**: Visual explanations of data flow
2326
- **Pseudocode**: Understand the logic without implementation details
2427
- **Real code examples**: JSON configurations and TypeScript snippets
2528

2629
### Sandbox Configuration
30+
2731
- **Permission modes**: Toggle between default, acceptEdits, plan, and more
2832
- **Permission rules**: See how deny/ask/allow rules affect execution
2933
- **Hook configuration**: Understand how hooks intercept and modify behavior

eslint.config.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import js from '@eslint/js'
2-
import globals from 'globals'
3-
import reactHooks from 'eslint-plugin-react-hooks'
4-
import reactRefresh from 'eslint-plugin-react-refresh'
5-
import tseslint from 'typescript-eslint'
6-
import eslintConfigPrettier from 'eslint-config-prettier'
7-
import { defineConfig, globalIgnores } from 'eslint/config'
1+
import js from '@eslint/js';
2+
import globals from 'globals';
3+
import reactHooks from 'eslint-plugin-react-hooks';
4+
import reactRefresh from 'eslint-plugin-react-refresh';
5+
import tseslint from 'typescript-eslint';
6+
import eslintConfigPrettier from 'eslint-config-prettier';
7+
import { defineConfig, globalIgnores } from 'eslint/config';
88

99
export default defineConfig([
1010
globalIgnores(['dist']),
@@ -22,4 +22,4 @@ export default defineConfig([
2222
},
2323
},
2424
eslintConfigPrettier,
25-
])
25+
]);

index.html

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,35 @@
55
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<title>Claude Code Algorithm Visualizer</title>
8-
<meta name="description" content="Interactive visualization of how Claude Code's agentic loop works. Explore flowcharts, step through scenarios, and learn the algorithm behind Claude Code." />
8+
<meta
9+
name="description"
10+
content="Interactive visualization of how Claude Code's agentic loop works. Explore flowcharts, step through scenarios, and learn the algorithm behind Claude Code."
11+
/>
912

1013
<!-- Open Graph -->
1114
<meta property="og:type" content="website" />
1215
<meta property="og:title" content="Claude Code Algorithm Visualizer" />
13-
<meta property="og:description" content="Interactive visualization of how Claude Code's agentic loop works. Explore flowcharts, step through scenarios, and learn the algorithm behind Claude Code." />
16+
<meta
17+
property="og:description"
18+
content="Interactive visualization of how Claude Code's agentic loop works. Explore flowcharts, step through scenarios, and learn the algorithm behind Claude Code."
19+
/>
1420
<meta property="og:url" content="https://github.com/jonwiggins/claude-code-visualizer" />
1521
<meta property="og:image" content="/og-image.png" />
1622

1723
<!-- Twitter Card -->
1824
<meta name="twitter:card" content="summary_large_image" />
1925
<meta name="twitter:title" content="Claude Code Algorithm Visualizer" />
20-
<meta name="twitter:description" content="Interactive visualization of how Claude Code's agentic loop works. Explore flowcharts, step through scenarios, and learn the algorithm behind Claude Code." />
26+
<meta
27+
name="twitter:description"
28+
content="Interactive visualization of how Claude Code's agentic loop works. Explore flowcharts, step through scenarios, and learn the algorithm behind Claude Code."
29+
/>
2130
<meta name="twitter:image" content="/og-image.png" />
22-
<link rel="preconnect" href="https://fonts.googleapis.com">
23-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
24-
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
31+
<link rel="preconnect" href="https://fonts.googleapis.com" />
32+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
33+
<link
34+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap"
35+
rel="stylesheet"
36+
/>
2537
</head>
2638
<body>
2739
<div id="root"></div>

src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function App() {
2525
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors',
2626
isActive
2727
? 'bg-[#1f6feb]/20 text-[#58a6ff]'
28-
: 'text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#1f2428]'
28+
: 'text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#1f2428]',
2929
)
3030
}
3131
>
@@ -39,7 +39,7 @@ function App() {
3939
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors',
4040
isActive
4141
? 'bg-[#1f6feb]/20 text-[#58a6ff]'
42-
: 'text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#1f2428]'
42+
: 'text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#1f2428]',
4343
)
4444
}
4545
>

src/components/AlgorithmOverview.tsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ import {
99
} from '@xyflow/react';
1010
import Dagre from '@dagrejs/dagre';
1111
import '@xyflow/react/dist/style.css';
12-
import { overviewNodes, overviewEdges, categoryColors, type OverviewNode } from '../algorithmOverview';
12+
import {
13+
overviewNodes,
14+
overviewEdges,
15+
categoryColors,
16+
type OverviewNode,
17+
} from '../algorithmOverview';
1318
import { CodeBlock } from './CodeBlock';
1419
import { BookOpen, X } from 'lucide-react';
1520
import clsx from 'clsx';
@@ -19,16 +24,23 @@ const NODE_WIDTH = 160;
1924
const NODE_HEIGHT = 50;
2025

2126
// Custom node component for overview
22-
function OverviewFlowNode({ data, selected }: { data: Record<string, unknown>; selected: boolean }) {
23-
const colorClass = categoryColors[data.category as OverviewNode['category']] || 'border-gray-500 bg-gray-500/10';
27+
function OverviewFlowNode({
28+
data,
29+
selected,
30+
}: {
31+
data: Record<string, unknown>;
32+
selected: boolean;
33+
}) {
34+
const colorClass =
35+
categoryColors[data.category as OverviewNode['category']] || 'border-gray-500 bg-gray-500/10';
2436

2537
return (
2638
<div
2739
className={clsx(
2840
'px-3 py-2 rounded-lg border-2 text-center transition-all duration-200 cursor-pointer',
2941
colorClass,
3042
selected && 'ring-2 ring-[#58a6ff] ring-offset-2 ring-offset-[#0d1117]',
31-
!selected && 'hover:ring-1 hover:ring-[#58a6ff]/50'
43+
!selected && 'hover:ring-1 hover:ring-[#58a6ff]/50',
3244
)}
3345
style={{ minWidth: NODE_WIDTH, maxWidth: NODE_WIDTH }}
3446
>
@@ -139,12 +151,7 @@ export function AlgorithmOverview() {
139151
}}
140152
proOptions={{ hideAttribution: true }}
141153
>
142-
<Background
143-
variant={BackgroundVariant.Dots}
144-
gap={16}
145-
size={1}
146-
color="#30363d"
147-
/>
154+
<Background variant={BackgroundVariant.Dots} gap={16} size={1} color="#30363d" />
148155
<Controls
149156
showInteractive={false}
150157
className="!bg-[#161b22] !border-[#30363d] !rounded [&>button]:!bg-[#161b22] [&>button]:!border-[#30363d] [&>button]:!text-[#c9d1d9] [&>button]:!w-6 [&>button]:!h-6 [&>button:hover]:!bg-[#30363d]"
@@ -167,7 +174,7 @@ export function AlgorithmOverview() {
167174
selectedNode.category === 'tool' && 'bg-orange-500/20 text-orange-400',
168175
selectedNode.category === 'hook' && 'bg-purple-500/20 text-purple-400',
169176
selectedNode.category === 'subagent' && 'bg-cyan-500/20 text-cyan-400',
170-
selectedNode.category === 'output' && 'bg-emerald-500/20 text-emerald-400'
177+
selectedNode.category === 'output' && 'bg-emerald-500/20 text-emerald-400',
171178
)}
172179
>
173180
{selectedNode.category}
@@ -199,9 +206,7 @@ export function AlgorithmOverview() {
199206
)}
200207
</div>
201208
) : (
202-
<div className="p-4 text-center text-sm text-[#8b949e]">
203-
Click a node to see details
204-
</div>
209+
<div className="p-4 text-center text-sm text-[#8b949e]">Click a node to see details</div>
205210
)}
206211
</div>
207212

@@ -219,7 +224,7 @@ export function AlgorithmOverview() {
219224
<div
220225
className={clsx(
221226
'w-2 h-2 rounded-sm border',
222-
categoryColors[category as OverviewNode['category']]
227+
categoryColors[category as OverviewNode['category']],
223228
)}
224229
/>
225230
<span className="text-[10px] text-[#8b949e]">{label}</span>

src/components/DetailPanel.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ export function DetailPanel() {
2020
if (!step) {
2121
return (
2222
<div className="bg-[#161b22] border border-[#30363d] rounded-lg p-4">
23-
<div className="text-center text-sm text-[#8b949e]">
24-
Node not yet executed
25-
</div>
23+
<div className="text-center text-sm text-[#8b949e]">Node not yet executed</div>
2624
</div>
2725
);
2826
}

src/components/ExecutionLog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function ExecutionLog() {
2727
onClick={() => selectNode(step.nodeId)}
2828
className={clsx(
2929
'w-full p-3 text-left hover:bg-[#1f2428] transition-colors',
30-
index === currentStep && 'bg-[#1f2428]'
30+
index === currentStep && 'bg-[#1f2428]',
3131
)}
3232
>
3333
<div className="flex items-center gap-2 mb-1">
@@ -37,7 +37,7 @@ export function ExecutionLog() {
3737
<span
3838
className={clsx(
3939
'w-2 h-2 rounded-full',
40-
index === currentStep ? 'bg-green-500' : 'bg-[#30363d]'
40+
index === currentStep ? 'bg-green-500' : 'bg-[#30363d]',
4141
)}
4242
/>
4343
</div>

src/components/FlowCanvas.tsx

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ interface LayoutOptions {
3434
function getLayoutedElements(
3535
nodes: Node[],
3636
edges: Edge[],
37-
options: LayoutOptions = { direction: 'TB', nodesep: 60, ranksep: 80 }
37+
options: LayoutOptions = { direction: 'TB', nodesep: 60, ranksep: 80 },
3838
) {
3939
const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
4040

@@ -53,7 +53,12 @@ function getLayoutedElements(
5353
});
5454

5555
// Add edges to the graph (skip loop-back edges for layout calculation to reduce tangles)
56-
const loopBackEdges = new Set(['response-user-input', 'hook-stop-model-inference', 'compaction-model-inference', 'context-check-model-inference']);
56+
const loopBackEdges = new Set([
57+
'response-user-input',
58+
'hook-stop-model-inference',
59+
'compaction-model-inference',
60+
'context-check-model-inference',
61+
]);
5762

5863
edges.forEach((edge) => {
5964
const edgeId = `${edge.source}-${edge.target}`;
@@ -82,7 +87,8 @@ function getLayoutedElements(
8287
}
8388

8489
export function FlowCanvas() {
85-
const { executionLog, currentStep, selectedNodeId, selectNode, currentScenario } = useVisualizerStore();
90+
const { executionLog, currentStep, selectedNodeId, selectNode, currentScenario } =
91+
useVisualizerStore();
8692

8793
const isOverview = currentScenario?.id === 'algorithm-overview';
8894

@@ -95,12 +101,7 @@ export function FlowCanvas() {
95101
const nodeMap = new Map<string, Node>();
96102

97103
executionLog.forEach((step, index) => {
98-
const status =
99-
index < currentStep
100-
? 'completed'
101-
: index === currentStep
102-
? 'active'
103-
: 'idle';
104+
const status = index < currentStep ? 'completed' : index === currentStep ? 'active' : 'idle';
104105

105106
// For overview, get category from payload
106107
const category = (step.payload as Record<string, unknown>)?.category as string | undefined;
@@ -117,7 +118,9 @@ export function FlowCanvas() {
117118
status,
118119
description: isOverview
119120
? (step.payload as Record<string, unknown>)?.summary
120-
: (step.payload ? Object.keys(step.payload).join(', ') : undefined),
121+
: step.payload
122+
? Object.keys(step.payload).join(', ')
123+
: undefined,
121124
},
122125
selected: step.nodeId === selectedNodeId,
123126
});
@@ -141,7 +144,7 @@ export function FlowCanvas() {
141144

142145
if (isOverview) {
143146
// Use the predefined overview edges for proper algorithm flow
144-
const nodeIds = new Set(executionLog.map(s => s.nodeId));
147+
const nodeIds = new Set(executionLog.map((s) => s.nodeId));
145148

146149
overviewEdges.forEach((edge) => {
147150
// Only include edges where both nodes exist
@@ -161,7 +164,7 @@ export function FlowCanvas() {
161164
label: edge.label,
162165
labelStyle: {
163166
fontSize: 9,
164-
fill: isLoopBack ? '#484f58' : '#8b949e'
167+
fill: isLoopBack ? '#484f58' : '#8b949e',
165168
},
166169
labelBgStyle: { fill: '#0d1117', fillOpacity: 0.9 },
167170
labelBgPadding: [4, 2] as [number, number],
@@ -214,7 +217,7 @@ export function FlowCanvas() {
214217
(_: React.MouseEvent, node: Node) => {
215218
selectNode(node.id);
216219
},
217-
[selectNode]
220+
[selectNode],
218221
);
219222

220223
if (executionLog.length === 0) {
@@ -243,15 +246,8 @@ export function FlowCanvas() {
243246
type: 'smoothstep',
244247
}}
245248
>
246-
<Background
247-
variant={BackgroundVariant.Dots}
248-
gap={20}
249-
size={1}
250-
color="#30363d"
251-
/>
252-
<Controls
253-
className="!bg-[#161b22] !border-[#30363d] !rounded-lg [&>button]:!bg-[#161b22] [&>button]:!border-[#30363d] [&>button]:!text-[#c9d1d9] [&>button:hover]:!bg-[#30363d]"
254-
/>
249+
<Background variant={BackgroundVariant.Dots} gap={20} size={1} color="#30363d" />
250+
<Controls className="!bg-[#161b22] !border-[#30363d] !rounded-lg [&>button]:!bg-[#161b22] [&>button]:!border-[#30363d] [&>button]:!text-[#c9d1d9] [&>button:hover]:!bg-[#30363d]" />
255251
<MiniMap
256252
nodeColor={(node) => {
257253
const status = (node.data as Record<string, unknown>).status;

src/components/FlowNode.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const iconMap: Record<string, React.ElementType> = {
4747
response: MessageSquare,
4848
'tool-blocked': XCircle,
4949
'user-approval': Shield,
50-
'compaction': FileText,
50+
compaction: FileText,
5151
'context-check': FileText,
5252
};
5353

@@ -71,14 +71,15 @@ const colorMap: Record<string, string> = {
7171
response: 'border-green-500 bg-green-500/10',
7272
'tool-blocked': 'border-red-500 bg-red-500/10',
7373
'user-approval': 'border-yellow-500 bg-yellow-500/10',
74-
'compaction': 'border-purple-400 bg-purple-400/10',
74+
compaction: 'border-purple-400 bg-purple-400/10',
7575
'context-check': 'border-yellow-400 bg-yellow-400/10',
7676
};
7777

7878
function FlowNode({ data, selected }: FlowNodeProps) {
7979
const nodeType = data.nodeType.split('-').slice(0, 2).join('-');
8080
const Icon = iconMap[nodeType] || iconMap[data.nodeType.split('-')[0]] || Cog;
81-
const colorClass = colorMap[nodeType] || colorMap[data.nodeType.split('-')[0]] || 'border-gray-500 bg-gray-500/10';
81+
const colorClass =
82+
colorMap[nodeType] || colorMap[data.nodeType.split('-')[0]] || 'border-gray-500 bg-gray-500/10';
8283

8384
const statusClasses = {
8485
idle: 'opacity-50',
@@ -94,7 +95,7 @@ function FlowNode({ data, selected }: FlowNodeProps) {
9495
'px-4 py-3 rounded-lg border-2 min-w-[180px] max-w-[240px] transition-all duration-300',
9596
colorClass,
9697
statusClasses[data.status],
97-
selected && 'ring-2 ring-[#58a6ff] ring-offset-2 ring-offset-[#0d1117]'
98+
selected && 'ring-2 ring-[#58a6ff] ring-offset-2 ring-offset-[#0d1117]',
9899
)}
99100
>
100101
<Handle
@@ -108,7 +109,7 @@ function FlowNode({ data, selected }: FlowNodeProps) {
108109
className={clsx(
109110
data.status === 'active' && 'text-green-400',
110111
data.status === 'error' && 'text-red-400',
111-
data.status === 'completed' && 'text-[#c9d1d9]'
112+
data.status === 'completed' && 'text-[#c9d1d9]',
112113
)}
113114
/>
114115
<span className="text-sm font-medium truncate">{data.label}</span>

0 commit comments

Comments
 (0)