Magam
Overview
Magam is a React-based library for creating visual diagrams through code. It enables AI-first diagram creation where users describe their intent in natural language, and the AI generates React/TSX code that renders as visual diagrams. The philosophy is "describing" rather than "drawing" - transforming thoughts into structured visual representations.
When to Use This Skill
- •Creating mind maps to organize ideas or knowledge
- •Building system architecture diagrams
- •Visualizing flowcharts and processes
- •Creating overview/brainstorming diagrams with sticky notes
- •Generating visual documentation from text descriptions
- •Any task requiring code-based diagram generation
Editing Nodes with Copy Node ID
Magam supports a node ID copy feature that enables targeted editing of specific nodes. Users can select a node in the canvas and copy its fully-qualified ID, then paste it into a prompt for the AI to locate and edit.
How It Works
- •Select a node by clicking it on the canvas
- •Copy with
Cmd+C(Mac) /Ctrl+C(Windows/Linux) - •The fully-qualified node ID is copied to clipboard (e.g.,
mindmap.mindmap-0.ai-table-demo) - •Paste the ID into a prompt to tell the AI which node to edit
Node ID Format
Node IDs follow the pattern: {mindmapId}.{nodeId}
- •If the MindMap has an explicit
idprop (e.g.,<MindMap id="main">), the prefix is that ID (e.g.,main.intro) - •If no explicit
id, an auto-generated ID likemindmap-0is used (e.g.,mindmap-0.root) - •Top-level canvas elements (Shape, Sticky, Text) outside a MindMap use their own
iddirectly - •Inside EmbedScope, IDs are prefixed with the scope:
<EmbedScope id="auth">+id="jwt"="auth.jwt" - •EmbedScope + MindMap combines:
<EmbedScope id="auth">+<MindMap id="map">+<Node id="root">="auth.map.root"
AI Workflow for Targeted Edits
When a user provides a copied node ID, the AI should:
- •Find the node in the TSX source by matching the
idprop value (the last segment of the path) - •Identify the MindMap it belongs to by matching the MindMap
idprop (the first segment) - •Edit the node's content as requested
Example prompt from user:
"Add a row for 'Alice' to the table in
mindmap.mindmap-0.ai-table-demo"
AI interpretation:
- •MindMap: auto-generated
mindmap-0(the first/default MindMap in the file) - •Node:
ai-table-demo - •Action: Find
<Node id="ai-table-demo">and add a table row
Multi-Select
Multiple nodes can be selected and copied at once. Each ID is separated by a newline in the clipboard. The AI should handle each node independently when editing.
Core Components
All components are imported from @magam/core:
import { Canvas, Shape, Sticky, MindMap, Node, Edge, Text, Markdown, EmbedScope } from '@magam/core';
Canvas (Required Root)
Every Magam diagram must be wrapped in a Canvas component.
<Canvas>
{/* All diagram elements go here */}
</Canvas>
Shape
Rectangle/box element for architecture diagrams and flowcharts.
Props:
- •
id(required): Unique identifier - •
x,y: Absolute position coordinates - •
width,height: Size in pixels - •
label: Text label (alternative to children) - •
className: Tailwind CSS classes for styling - •
anchor: ID of another Shape for relative positioning - •
position: Position relative to anchor ("right", "bottom", "left", "top") - •
gap: Distance from anchor element
Usage Patterns:
{/* Absolute positioning */}
<Shape id="box1" x={100} y={100}>Content</Shape>
{/* With size */}
<Shape id="box2" x={200} y={100} width={180} height={80}>Sized Box</Shape>
{/* Relative positioning with anchor */}
<Shape id="api" anchor="users" position="right" gap={120}>
<Text>API Server</Text>
<Edge to="users" />
</Shape>
{/* With Tailwind styling */}
<Shape id="success" className="bg-green-50 border-green-300 text-green-700">
Success State
</Shape>
Sticky
Sticky note element, similar to Shape but styled as a sticky note.
<Sticky id="idea" x={100} y={100}>
My Idea
<Edge to="target" />
</Sticky>
{/* With glass effect */}
<Sticky id="glass" className="bg-white/50 backdrop-blur-sm border-white/30">
Glass Effect
</Sticky>
MindMap
Container for hierarchical mind map structures.
Props:
- •
id: MindMap identifier (required for multiple MindMaps) - •
x,y: Position on canvas (default: 0, 0) - •
layout: "tree" (horizontal), "bidirectional" (left+right), or "radial" (circular) - •
spacing: Gap between nodes
<MindMap id="main" layout="tree" spacing={80}>
<Node id="root">Root Topic</Node>
<Node id="child1" from="root">Child 1</Node>
<Node id="child2" from="root">Child 2</Node>
<Node id="grandchild" from="child1">Grandchild</Node>
</MindMap>
Node
MindMap node element. Must be used inside a MindMap.
Props:
- •
id(required): Unique identifier - •
from: Parent node ID (creates connection automatically) - •
bubble: Enable floating label when zoomed out (semantic zoom)
Content Options:
- •Plain text:
<Node id="x">Plain text</Node> - •Text component:
<Node id="x"><Text>Styled</Text></Node> - •Multiple Text:
<Node id="x"><Text>Line 1</Text><Text>Line 2</Text></Node> - •Markdown:
<Node id="x"><Markdown>content</Markdown></Node> - •Markdown with bubble:
<Node id="x"><Markdown bubble>{content}</Markdown></Node>
Semantic Zoom (Bubble)
When zoomed out (zoom < 0.4), nodes with the bubble prop display a floating label overlay. The label text is auto-extracted from the first line of content.
Usage:
{/* On Node directly */}
<Node id="x" bubble>
<Text>This text becomes bubble label</Text>
</Node>
{/* On Markdown inside Node */}
<Node id="x">
<Markdown bubble>{`
# Heading becomes bubble
Content here...
`}</Markdown>
</Node>
{/* On Shape */}
<Shape id="x" label="Bubble text" bubble>
<Text>Shape content</Text>
</Shape>
Behavior:
- •First line of content is extracted as bubble text
- •Markdown syntax (headings, bold, etc.) is cleaned
- •Text over 40 characters is truncated with
... - •Bubble appears as floating overlay on top of original content
Best Practices:
| Level | Recommendation | Reason |
|---|---|---|
| Root node | Always use | Main topic visibility at any zoom |
| Level 1-2 (children of root) | Recommended | Section titles remain readable when zoomed out |
| Level 3 | Selective | Only for key concepts that need visibility |
| Level 4+ | Usually skip | Too many bubbles cause visual clutter |
- •Use for navigation nodes: Nodes with Table of Contents or section headers benefit from bubbles
- •Use for key landmarks: Important concepts users need to locate quickly
- •Skip for leaf nodes: Detail nodes don't need bubble labels—users will zoom in to read them
- •Skip for repetitive content: If many siblings have similar structure, bubble only the parent
{/* Good: bubbles on structural nodes */}
<MindMap id="docs">
<Node id="root" bubble>Main Topic</Node>
<Node id="section1" from="root" bubble>Section 1</Node>
<Node id="section2" from="root" bubble>Section 2</Node>
<Node id="detail1" from="section1">Detail (no bubble)</Node>
</MindMap>
Text
Standalone or inline text element.
Props:
- •
id: Identifier (for standalone) - •
x,y: Position (for standalone) - •
className: Tailwind CSS classes
{/* Standalone text on canvas */}
<Text id="title" x={200} y={30}>Page Title</Text>
{/* Styled text */}
<Text className="text-xl font-bold text-blue-600">Styled Text</Text>
{/* Inside Shape */}
<Shape id="box">
<Text className="font-bold">Title</Text>
<Text className="text-sm text-gray-500">Subtitle</Text>
</Shape>
Markdown
Rich text content with Markdown support. Typically used inside Node.
Supported Features:
- •Headers:
# H1,## H2,### H3 - •Emphasis:
**bold**,*italic* - •Lists:
- itemor1. item - •Code:
`inline`and code blocks with ``` - •Tables:
| col1 | col2 | - •Blockquotes:
> quote
<Node id="docs">
<Markdown>{`
### API Reference
| Method | Endpoint |
|--------|----------|
| GET | /users |
| POST | /users |
\`\`\`typescript
function hello() {
return "world";
}
\`\`\`
`}</Markdown>
</Node>
Node Links (Internal Navigation)
Navigate between nodes using the node: scheme in Markdown links. Clicking a node link smoothly animates the viewport to the target node.
Syntax: [link text](node:/mindmapId/nodeId)
<Node id="intro">
<Markdown>{`
## Introduction
Learn the basics first, then move on.
[Next: Core Concepts](node:/main/concepts)
`}</Markdown>
</Node>
<Node id="concepts" from="intro">
<Markdown>{`
## Core Concepts
- Canvas: Infinite drawing area
- MindMap: Auto-layout container
- Node: Content container
[← Previous](node:/main/intro) | [Next →](node:/main/examples)
`}</Markdown>
</Node>
Path Formats:
- •
/mindmapId/nodeId→ navigates tomindmapId.nodeId - •
/nodeId→ navigates tonodeId(for single MindMap)
Styling: Node links are styled with indigo color and arrow prefix (→) to distinguish from external links.
EmbedScope
ID namespace isolation for reusable components. Wrapping elements in EmbedScope automatically prefixes all child IDs with the scope name, preventing collisions when the same component is used multiple times.
Props:
- •
id(required): Scope identifier. Becomes the prefix for all child IDs. - •
children: React nodes to scope.
How it works:
- •Child
idprops are automatically prefixed:id="app"inside<EmbedScope id="auth">becomes"auth.app" - •Child
anchorprops are automatically resolved to scoped IDs when a matching node exists in the same scope - •Edge
from/toprops are also scoped automatically - •IDs containing a dot (e.g.,
"billing.app") are treated as already qualified and not prefixed - •Nesting is supported: scopes chain (e.g.,
"infra">"aws">"ec2"="infra.aws.ec2") - •EmbedScope adds no visual element to the canvas - it is purely logical
Basic usage:
{/* Reusable component - uses local IDs only */}
function ServiceCluster({ label }: { label: string }) {
return (
<>
<Shape id="lb" anchor="gateway" position="bottom" gap={80} width={120} height={50}>
Load Balancer
</Shape>
<Shape id="app" anchor="lb" position="bottom" gap={60} width={120} height={50}>
{label} Server
<Edge to="db" />
</Shape>
<Shape id="db" anchor="app" position="bottom" gap={60} width={120} height={50}>
Database
</Shape>
<Edge from="lb" to="app" />
</>
);
}
export default function Example() {
return (
<Canvas>
<Shape id="gateway" x={0} y={0} width={140} height={50}>API Gateway</Shape>
{/* IDs become auth.lb, auth.app, auth.db */}
<EmbedScope id="auth">
<ServiceCluster label="Auth" />
</EmbedScope>
{/* IDs become billing.lb, billing.app, billing.db */}
<EmbedScope id="billing">
<ServiceCluster label="Billing" />
</EmbedScope>
{/* Cross-scope edge using fully qualified IDs */}
<Edge from="auth.app" to="billing.app" label="verify" />
</Canvas>
);
}
Anchor resolution inside EmbedScope:
Inside a scope, anchor props referencing sibling nodes are automatically resolved:
- •
anchor="lb"inside<EmbedScope id="auth">resolves to"auth.lb"(because"auth.lb"exists) - •
anchor="gateway"inside<EmbedScope id="auth">stays as"gateway"(because"auth.gateway"does not exist - it is an external reference)
This means reusable components work without any modification. Internal anchor chains resolve within the scope, while references to external nodes pass through unchanged.
Cross-boundary edges:
{/* From Canvas to scope: use fully qualified ID */}
<Edge from="gateway" to="auth.lb" />
{/* Between scopes: use fully qualified IDs */}
<Edge from="auth.app" to="billing.app" />
{/* Inside a component, reference external node with dot notation */}
<Shape id="app" ...>
<Edge to="billing.app" /> {/* dot = already qualified, not prefixed */}
</Shape>
Edge
Connection line between elements.
Props:
- •
from: Source element ID - •
to: Target element ID
{/* Standalone edge */}
<Edge from="box1" to="box2" />
{/* Inside Shape (only 'to' needed) */}
<Shape id="api">
<Text>API</Text>
<Edge to="database" />
</Shape>
Styling with Tailwind CSS
All components support className prop for Tailwind CSS styling.
Colors and States
<Shape className="bg-green-50 border-green-300 text-green-700">Success</Shape> <Shape className="bg-yellow-50 border-yellow-300 text-yellow-700">Warning</Shape> <Shape className="bg-red-50 border-red-300 text-red-700">Error</Shape>
Effects
<Shape className="bg-gradient-to-r from-blue-400 to-purple-500 text-white border-none"> Gradient </Shape> <Shape className="shadow-xl">Shadow</Shape> <Sticky className="bg-white/50 backdrop-blur-sm">Glass Effect</Sticky>
Borders
<Shape className="rounded-2xl">Rounded</Shape> <Shape className="border-dashed border-2">Dashed</Shape> <Shape className="border-4 border-indigo-500">Thick Border</Shape>
Sizing
<Shape width={180} height={80}>Props sizing</Shape>
<Shape className="w-[200px] h-[60px]">Tailwind sizing</Shape>
Common Patterns
Architecture Diagram
<Canvas>
<Text id="title" x={200} y={30}>System Architecture</Text>
<Shape id="users" x={50} y={100}>
<Text>Users</Text>
</Shape>
<Shape id="api" anchor="users" position="right" gap={120}>
<Text>API Server</Text>
<Edge to="users" />
</Shape>
<Shape id="db" anchor="api" position="right" gap={120}>
<Text>Database</Text>
<Edge to="api" />
</Shape>
<Shape id="auth" anchor="api" position="bottom" gap={80}>
<Text>Auth Service</Text>
<Edge to="api" />
</Shape>
</Canvas>
Knowledge Mind Map
<Canvas>
<MindMap id="knowledge" layout="tree" spacing={80}>
<Node id="root">
<Markdown>{`# Main Topic`}</Markdown>
</Node>
<Node id="category1" from="root">Category 1</Node>
<Node id="item1a" from="category1">Item 1A</Node>
<Node id="item1b" from="category1">Item 1B</Node>
<Node id="category2" from="root">Category 2</Node>
<Node id="item2a" from="category2">
<Markdown>{`
**Detailed Item**
- Point 1
- Point 2
`}</Markdown>
</Node>
</MindMap>
</Canvas>
Multiple MindMaps
Place multiple independent MindMaps on a single Canvas. Each MindMap has its own ID namespace, so node IDs won't conflict.
Key Features:
- •Scoped IDs: Same node ID can exist in different MindMaps
- •Positioning: Use
x,yprops to position each MindMap - •Cross-MindMap Edges: Use dot notation
mapId.nodeIdfor references
<Canvas>
{/* First MindMap */}
<MindMap id="concepts" layout="bidirectional">
<Node id="root">
<Markdown>{`# Core Concepts`}</Markdown>
</Node>
<Node id="feature1" from="root">Feature A</Node>
<Node id="feature2" from="root">Feature B</Node>
</MindMap>
{/* Second MindMap - positioned to the right */}
<MindMap id="details" layout="tree" x={600} y={0}>
<Node id="root">
<Markdown>{`## Details`}</Markdown>
</Node>
<Node id="item1" from="root">Detail 1</Node>
<Node id="item2" from="root">Detail 2</Node>
</MindMap>
{/* Cross-MindMap connection using dot notation */}
<Edge from="concepts.feature1" to="details.item1" />
</Canvas>
Reusable Components with EmbedScope
Extract repeated patterns into components and use EmbedScope to isolate IDs. Anchor references inside a scope are automatically resolved to sibling nodes.
function DatabaseCluster() {
return (
<>
<Shape id="primary" x={0} y={0} width={120} height={50}>Primary</Shape>
<Shape id="replica" anchor="primary" position="right" gap={80} width={120} height={50}>
Replica
</Shape>
<Edge from="primary" to="replica" label="sync" />
</>
);
}
export default function MultiDB() {
return (
<Canvas>
{/* "users" scope: users.primary, users.replica */}
<EmbedScope id="users">
<DatabaseCluster />
</EmbedScope>
{/* "orders" scope: orders.primary, orders.replica */}
<EmbedScope id="orders">
<DatabaseCluster />
</EmbedScope>
{/* Cross-scope connection */}
<Edge from="users.primary" to="orders.primary" label="join" />
</Canvas>
);
}
Brainstorming with Stickies
<Canvas>
<Text id="title" x={200} y={30}>Brainstorm</Text>
<Sticky id="idea1" x={100} y={100}>
Core Idea
<Edge to="central" />
</Sticky>
<Sticky id="idea2" x={100} y={200}>
Supporting Idea
<Edge to="central" />
</Sticky>
<Shape id="central" x={300} y={150}>Central Concept</Shape>
</Canvas>
Best Practices
- •Always use unique IDs for all elements with id prop
- •Use semantic IDs that describe the element's purpose
- •Prefer relative positioning with
anchor/position/gapfor maintainable layouts - •Use Markdown for rich content in mind map nodes
- •Do not add emojis unless the user explicitly requests them
- •Group related content using comments in JSX
- •Use consistent styling with Tailwind utility classes
- •Keep node content concise - use child nodes for detailed breakdowns
- •Split large MindMaps into multiple smaller ones - Rather than cramming everything into one huge MindMap, separate by topic or section. This makes hierarchies clearer and improves readability. Use
anchorpositioning to arrange them spatially. - •Use
bubblefor semantic zoom - Addbubbleprop to root nodes and level 1-2 children so section titles remain visible when zoomed out. Skip bubbles on level 4+ detail nodes to avoid visual clutter. - •Use
EmbedScopefor reusable components - When the same component pattern is used multiple times, wrap each instance in<EmbedScope id="...">to isolate IDs. Internalanchorreferences resolve automatically within the scope. Use dot notation (e.g.,"auth.app") for cross-scope references.
File Structure
Magam files are TypeScript/TSX files that export a React component:
import { Canvas, MindMap, Node, Text, Markdown } from '@magam/core';
export default function MyDiagram() {
return (
<Canvas>
{/* Diagram content */}
</Canvas>
);
}
Files are typically placed in an examples/ directory with descriptive names like architecture.tsx, mindmap.tsx, overview.tsx.