AgentSkillsCN

magam

当您使用 Magam 库创建可视化图表、思维导图、流程图、架构图,或任何基于代码的可视化呈现时,应使用此技能。当涉及图表创建、思维导图生成、可视化文档编写、系统架构可视化,或当用户提及“Magam”、“思维导图”、“图表”、“流程图”或“画布”时,均可触发此技能。

SKILL.md
--- frontmatter
name: magam
description: This skill should be used when creating visual diagrams, mind maps, flowcharts, architecture diagrams, or any code-based visual representation using the Magam library. Triggers on tasks involving diagram creation, mind map generation, visual documentation, system architecture visualization, or when users mention "magam", "mindmap", "diagram", "flowchart", or "canvas".

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

  1. Select a node by clicking it on the canvas
  2. Copy with Cmd+C (Mac) / Ctrl+C (Windows/Linux)
  3. The fully-qualified node ID is copied to clipboard (e.g., mindmap.mindmap-0.ai-table-demo)
  4. 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 id prop (e.g., <MindMap id="main">), the prefix is that ID (e.g., main.intro)
  • If no explicit id, an auto-generated ID like mindmap-0 is used (e.g., mindmap-0.root)
  • Top-level canvas elements (Shape, Sticky, Text) outside a MindMap use their own id directly
  • 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:

  1. Find the node in the TSX source by matching the id prop value (the last segment of the path)
  2. Identify the MindMap it belongs to by matching the MindMap id prop (the first segment)
  3. 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:

tsx
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.

tsx
<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:

tsx
{/* 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.

tsx
<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
tsx
<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:

tsx
{/* 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:

LevelRecommendationReason
Root nodeAlways useMain topic visibility at any zoom
Level 1-2 (children of root)RecommendedSection titles remain readable when zoomed out
Level 3SelectiveOnly for key concepts that need visibility
Level 4+Usually skipToo 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
tsx
{/* 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
tsx
{/* 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: - item or 1. item
  • Code: `inline` and code blocks with ```
  • Tables: | col1 | col2 |
  • Blockquotes: > quote
tsx
<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)

tsx
<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 to mindmapId.nodeId
  • /nodeId → navigates to nodeId (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 id props are automatically prefixed: id="app" inside <EmbedScope id="auth"> becomes "auth.app"
  • Child anchor props are automatically resolved to scoped IDs when a matching node exists in the same scope
  • Edge from/to props 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:

tsx
{/* 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:

tsx
{/* 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
tsx
{/* 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

tsx
<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

tsx
<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

tsx
<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

tsx
<Shape width={180} height={80}>Props sizing</Shape>
<Shape className="w-[200px] h-[60px]">Tailwind sizing</Shape>

Common Patterns

Architecture Diagram

tsx
<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

tsx
<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, y props to position each MindMap
  • Cross-MindMap Edges: Use dot notation mapId.nodeId for references
tsx
<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.

tsx
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

tsx
<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

  1. Always use unique IDs for all elements with id prop
  2. Use semantic IDs that describe the element's purpose
  3. Prefer relative positioning with anchor/position/gap for maintainable layouts
  4. Use Markdown for rich content in mind map nodes
  5. Do not add emojis unless the user explicitly requests them
  6. Group related content using comments in JSX
  7. Use consistent styling with Tailwind utility classes
  8. Keep node content concise - use child nodes for detailed breakdowns
  9. 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 anchor positioning to arrange them spatially.
  10. Use bubble for semantic zoom - Add bubble prop 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.
  11. Use EmbedScope for reusable components - When the same component pattern is used multiple times, wrap each instance in <EmbedScope id="..."> to isolate IDs. Internal anchor references 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:

tsx
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.