Token导航 LogoToken导航TokenDH.com
研究检索只读github未标认证来源可访问clear审计提醒

excalidraw-generationexcalidraw generation 搜索

Agent Skill

excalidraw-generation 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

20,813

周安装

497

GitHub Stars

28

下载量

5,187
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:excalidraw-generation(excalidraw generation 搜索)
来源仓库:https://github.com/rhuss/cc-slidev
仓库路径:skills/excalidraw-generation
安装命令:
npx skills add https://github.com/rhuss/cc-slidev --skill 'Excalidraw Generation'
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/rhuss/cc-slidev --skill 'Excalidraw Generation'

简介

excalidraw-generation 将文本转化为语义化的 Excalidraw 幻灯片,注重视觉表达与设计美感。

  • 适合在 Codex、Claude、Cursor、Gemini CLI 中需要制作演示文稿、培训材料或技术讲解图时使用。
  • 采用两阶段流程:内容提取与视觉重构,确保信息清晰传达。
  • 必须使用 render-excalidraw.sh 脚本转换 JSON 为 SVG,禁止手动嵌入原始数据。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Excalidraw Generation Expert

Core Philosophy: Semantic redesign, not mechanical conversion. Think like both a presentation designer (clarity, accessibility, simplicity) and an artist (creative visual expression, spatial design, aesthetic beauty).

CRITICAL - Rendering Rule

ALWAYS use render-excalidraw.sh for SVG conversion - NO EXCEPTIONS

After creating Excalidraw JSON:

  1. Save JSON to diagrams/<slug>.excalidraw
  2. MUST render using: ${CLAUDE_PLUGIN_ROOT}/scripts/render-excalidraw.sh
  3. NEVER attempt manual SVG conversion
  4. NEVER embed JSON in markdown - only reference the rendered SVG

The script handles all rendering automatically with excalidraw-brute-export-cli.

When to Use This Skill

Auto-trigger when:

  • User explicitly requests: "create excalidraw diagram", "hand-drawn diagram", "sketch", "whiteboard"
  • Slide content suggests conceptual/spatial relationships
  • Architecture with nested components
  • Brainstorming/ideation context
  • Informal, approachable style needed
  • Annotations and callouts would add value

Diagram type suitability:

  • ✅✅✅ BEST: Conceptual relationships, architecture diagrams, mind maps, timelines, comparisons
  • ✅✅ EXCELLENT: Flowcharts (with annotations), spatial layouts, nested structures
  • ⚠️ OKAY: Sequence diagrams (prefer Mermaid instead)
  • ❌ NOT RECOMMENDED: Formal UML (use PlantUML), state machines (use Mermaid)

Evidence-Based Design Constraints (HARD LIMITS)

These constraints are NON-NEGOTIABLE. Enforce strictly:

  • Cognitive load: Maximum 9 elements (7±2 rule from cognitive psychology)
  • Accessibility:

- Colorblind-safe palette ONLY: Blue #3b82f6 + Orange #f97316 - Minimum 4.5:1 contrast ratio for all text (WCAG AA) - Never rely on color alone to convey information

  • Minimal text: Under 50 words total per diagram
  • One idea per diagram: If concept is complex, split into multiple diagrams
  • Hand-drawn aesthetic: Roughness 1 for informal feel

Core Capabilities

1. Semantic Concept Extraction

Process (ALWAYS follow this order):

  1. Analyze user's description or slide content
  2. Extract core concepts (entities, relationships, flows)
  3. Identify semantic type:

- Containment: X contains Y → Use nested boxes/frames - Flow: A→B→C → Use arrows with spatial progression - Comparison: X vs Y → Use side-by-side separation - Hierarchy: Parent-child → Use vertical/spatial positioning - Grouping: Related items → Use frames or color-coded regions - Annotation: Context/explanation → Use callouts and bound text

  1. Design layout (choose from layout algorithms below)
  2. Generate JSON (use element factories below)

Example:

Input: "Kubernetes device plugin architecture"

Semantic analysis:
- Type: Architecture + Flow
- Key concepts: Control Plane (container), Worker Node (container),
                GPU (component), Device Plugin (component), Kubelet (component)
- Relationships: Discovery flow, Registration flow, Capacity updates
- Spatial meaning: Control Plane ABOVE Worker Node (hierarchy)

Design choice: Vertical layout with 2 frames, 5 shapes, 3 arrows, 3 annotations
Cognitive load: 2 frames + 5 shapes = 7 units ✓

2. Element Factories

These functions generate valid Excalidraw JSON elements. Use them to build diagrams.

ID Generation

function generateId() {
  // Excalidraw uses random alphanumeric IDs (12+ chars)
  return Math.random().toString(36).substring(2, 15) +
         Math.random().toString(36).substring(2, 15);
}

Rectangle Factory

function createRectangle(x, y, width, height, text = null, options = {}) {
  const id = generateId();

  const element = {
    type: "rectangle",
    version: 1,
    versionNonce: Math.floor(Math.random() * 1000000),
    isDeleted: false,
    id: id,
    fillStyle: options.fillStyle || "hachure",
    strokeWidth: options.strokeWidth || 2,
    strokeStyle: "solid",
    roughness: options.roughness !== undefined ? options.roughness : 1,
    opacity: 100,
    angle: options.angle || 0,
    x: x,
    y: y,
    strokeColor: options.strokeColor || THEME_COLORS.primary,
    backgroundColor: options.backgroundColor || "transparent",
    width: width,
    height: height,
    seed: Math.floor(Math.random() * 1000000),
    groupIds: options.groupIds || [],
    frameId: options.frameId || null,
    roundness: { type: 3 },
    boundElements: [],
    updated: Date.now(),
    link: null,
    locked: false
  };

  // If text provided, create bound text element
  if (text) {
    const textElement = createBoundText(text, id, x, y, width, height);
    element.boundElements.push({ type: "text", id: textElement.id });
    return [element, textElement];  // Return array
  }

  return element;  // Return single element
}

Text Factory (Standalone and Bound)

function createText(text, x, y, options = {}) {
  return {
    type: "text",
    version: 1,
    versionNonce: Math.floor(Math.random() * 1000000),
    isDeleted: false,
    id: options.id || generateId(),
    fillStyle: "hachure",
    strokeWidth: 1,
    strokeStyle: "solid",
    roughness: 0,  // Text is always smooth
    opacity: 100,
    angle: 0,
    x: x,
    y: y,
    strokeColor: options.strokeColor || THEME_COLORS.text,
    backgroundColor: "transparent",
    width: options.width || 200,
    height: options.height || 25,
    seed: Math.floor(Math.random() * 1000000),
    groupIds: options.groupIds || [],
    frameId: options.frameId || null,
    roundness: null,
    boundElements: [],
    updated: Date.now(),
    link: null,
    locked: false,
    fontSize: options.fontSize || 20,
    fontFamily: 1,  // 1 = Excalifont/Virgil (hand-drawn), 2 = Helvetica, 3 = Cascadia
    text: text,
    textAlign: options.textAlign || "center",
    verticalAlign: options.verticalAlign || "middle",
    containerId: options.containerId || null,
    originalText: text,
    lineHeight: 1.25,
    baseline: 18
  };
}

// Font family mapping for reference:
// fontFamily: 1 → Excalifont/Virgil (hand-drawn, default for Excalidraw aesthetic)
// fontFamily: 2 → Helvetica (clean, modern)
// fontFamily: 3 → Cascadia (monospace, code)
// When rendering to SVG: font-family: 'Excalifont', 'Virgil', cursive, sans-serif

function createBoundText(text, containerId, containerX, containerY, containerWidth, containerHeight) {
  // Calculate centered position inside container
  const textWidth = Math.min(containerWidth - 20, 200);
  const textHeight = 25;
  const textX = containerX + (containerWidth - textWidth) / 2;
  const textY = containerY + (containerHeight - textHeight) / 2;

  return createText(text, textX, textY, {
    width: textWidth,
    height: textHeight,
    containerId: containerId,
    textAlign: "center",
    verticalAlign: "middle"
  });
}

Arrow Factory

function createArrow(startX, startY, endX, endY, options = {}) {
  const points = [
    [0, 0],  // Start point (relative to x, y)
    [endX - startX, endY - startY]  // End point (relative)
  ];

  return {
    type: "arrow",
    version: 1,
    versionNonce: Math.floor(Math.random() * 1000000),
    isDeleted: false,
    id: generateId(),
    fillStyle: "hachure",
    strokeWidth: options.strokeWidth || 2,
    strokeStyle: "solid",
    roughness: options.roughness !== undefined ? options.roughness : 1,
    opacity: 100,
    angle: 0,
    x: startX,
    y: startY,
    strokeColor: options.strokeColor || THEME_COLORS.neutral,
    backgroundColor: "transparent",
    width: Math.abs(endX - startX),
    height: Math.abs(endY - startY),
    seed: Math.floor(Math.random() * 1000000),
    groupIds: options.groupIds || [],
    frameId: options.frameId || null,
    roundness: { type: 2 },
    boundElements: [],
    updated: Date.now(),
    link: null,
    locked: false,
    startBinding: options.startBinding || null,
    endBinding: options.endBinding || null,
    lastCommittedPoint: null,
    startArrowhead: null,
    endArrowhead: "arrow",
    points: points
  };
}

Frame Factory (Containers)

function createFrame(x, y, width, height, name, options = {}) {
  return {
    type: "frame",
    version: 1,
    versionNonce: Math.floor(Math.random() * 1000000),
    isDeleted: false,
    id: generateId(),
    fillStyle: "hachure",
    strokeWidth: 2,
    strokeStyle: "solid",
    roughness: 0,  // Frames are clean, not hand-drawn
    opacity: 100,
    angle: 0,
    x: x,
    y: y,
    strokeColor: options.strokeColor || THEME_COLORS.neutral,
    backgroundColor: options.backgroundColor || THEME_COLORS.light_bg,
    width: width,
    height: height,
    seed: Math.floor(Math.random() * 1000000),
    groupIds: [],
    frameId: null,
    roundness: null,
    boundElements: [],
    updated: Date.now(),
    link: null,
    locked: false,
    name: name
  };
}

Ellipse Factory

function createEllipse(x, y, width, height, text = null, options = {}) {
  const id = generateId();

  const element = {
    type: "ellipse",
    version: 1,
    versionNonce: Math.floor(Math.random() * 1000000),
    isDeleted: false,
    id: id,
    fillStyle: options.fillStyle || "hachure",
    strokeWidth: options.strokeWidth || 2,
    strokeStyle: "solid",
    roughness: options.roughness !== undefined ? options.roughness : 1,
    opacity: 100,
    angle: 0,
    x: x,
    y: y,
    strokeColor: options.strokeColor || THEME_COLORS.primary,
    backgroundColor: options.backgroundColor || "transparent",
    width: width,
    height: height,
    seed: Math.floor(Math.random() * 1000000),
    groupIds: options.groupIds || [],
    frameId: options.frameId || null,
    roundness: null,
    boundElements: [],
    updated: Date.now(),
    link: null,
    locked: false
  };

  if (text) {
    const textElement = createBoundText(text, id, x, y, width, height);
    element.boundElements.push({ type: "text", id: textElement.id });
    return [element, textElement];
  }

  return element;
}

Callout Factory (Annotation)

function createCallout(targetX, targetY, text, direction = "top-right") {
  const offsets = {
    "top-right": { dx: 100, dy: -80 },
    "top-left": { dx: -100, dy: -80 },
    "bottom-right": { dx: 100, dy: 80 },
    "bottom-left": { dx: -100, dy: 80 }
  };

  const offset = offsets[direction];
  const textX = targetX + offset.dx;
  const textY = targetY + offset.dy;

  const calloutText = createText(text, textX, textY, {
    fontSize: 16,
    strokeColor: THEME_COLORS.accent
  });

  const arrow = createArrow(textX, textY + 12, targetX, targetY, {
    strokeColor: THEME_COLORS.accent,
    strokeWidth: 1.5
  });

  // Group them together
  const groupId = generateId();
  calloutText.groupIds.push(groupId);
  arrow.groupIds.push(groupId);

  return [calloutText, arrow];
}

3. Layout Algorithms

Layout Constants

const LAYOUT = {
  MARGIN: 50,          // Canvas edge margin
  PADDING: 40,         // Between elements
  NODE_WIDTH: 180,     // Standard node width
  NODE_HEIGHT: 80,     // Standard node height
  ARROW_GAP: 10        // Gap for arrow binding
};

Horizontal Flow Layout

function layoutHorizontalFlow(nodes) {
  // Left-to-right progression
  const positions = [];
  let currentX = LAYOUT.MARGIN;
  const baseY = 200;  // Vertical center

  nodes.forEach((node, index) => {
    positions.push({
      x: currentX,
      y: baseY,
      width: LAYOUT.NODE_WIDTH,
      height: LAYOUT.NODE_HEIGHT,
      text: node
    });
    currentX += LAYOUT.NODE_WIDTH + LAYOUT.PADDING;
  });

  return positions;
}

Vertical Flow Layout

function layoutVerticalFlow(nodes) {
  // Top-to-bottom progression
  const positions = [];
  const baseX = 300;  // Horizontal center
  let currentY = LAYOUT.MARGIN;

  nodes.forEach((node, index) => {
    positions.push({
      x: baseX,
      y: currentY,
      width: LAYOUT.NODE_WIDTH,
      height: LAYOUT.NODE_HEIGHT,
      text: node
    });
    currentY += LAYOUT.NODE_HEIGHT + LAYOUT.PADDING;
  });

  return positions;
}

Radial Layout (Mind Map)

function layoutRadial(centerNode, childNodes) {
  const positions = [];
  const centerX = 400;
  const centerY = 300;
  const radius = 200;

  // Center node
  positions.push({
    x: centerX - LAYOUT.NODE_WIDTH / 2,
    y: centerY - LAYOUT.NODE_HEIGHT / 2,
    width: LAYOUT.NODE_WIDTH,
    height: LAYOUT.NODE_HEIGHT,
    text: centerNode
  });

  // Child nodes in circle
  const angleStep = (2 * Math.PI) / childNodes.length;
  childNodes.forEach((node, index) => {
    const angle = index * angleStep;
    const x = centerX + radius * Math.cos(angle) - LAYOUT.NODE_WIDTH / 2;
    const y = centerY + radius * Math.sin(angle) - LAYOUT.NODE_HEIGHT / 2;

    positions.push({
      x: x,
      y: y,
      width: LAYOUT.NODE_WIDTH,
      height: LAYOUT.NODE_HEIGHT,
      text: node
    });
  });

  return positions;
}

4. Connection Binding Logic

Create Binding Point

function createBindingPoint(shapeId, shapeX, shapeY, shapeWidth, shapeHeight, side) {
  // side: "top", "bottom", "left", "right"
  let focus = { x: 0, y: 0 };

  switch(side) {
    case "right":
      focus = { x: 1, y: 0 };  // Right edge, centered
      break;
    case "left":
      focus = { x: -1, y: 0 };  // Left edge, centered
      break;
    case "bottom":
      focus = { x: 0, y: 1 };  // Bottom edge, centered
      break;
    case "top":
      focus = { x: 0, y: -1 };  // Top edge, centered
      break;
  }

  return {
    elementId: shapeId,
    focus: focus,
    gap: LAYOUT.ARROW_GAP
  };
}

Connect Shapes Horizontally

function connectShapesHorizontal(shapeA, shapeB) {
  // Bind arrow from right edge of A to left edge of B
  const startX = shapeA.x + shapeA.width;
  const startY = shapeA.y + shapeA.height / 2;
  const endX = shapeB.x;
  const endY = shapeB.y + shapeB.height / 2;

  return createArrow(startX, startY, endX, endY, {
    startBinding: createBindingPoint(shapeA.id, shapeA.x, shapeA.y, shapeA.width, shapeA.height, "right"),
    endBinding: createBindingPoint(shapeB.id, shapeB.x, shapeB.y, shapeB.width, shapeB.height, "left")
  });
}

5. Color Palette (Colorblind-Safe)

const THEME_COLORS = {
  primary: "#3b82f6",      // Blue (8.6:1 contrast) - Main shapes
  secondary: "#f97316",    // Orange (3.4:1, ≥24pt only) - Emphasis
  neutral: "#6b7280",      // Gray - Arrows, frames
  text: "#1f2937",         // Dark gray (16.1:1 contrast) - ALL text
  background: "#ffffff",   // White canvas
  accent: "#8b5cf6",       // Purple - Annotations
  light_bg: "#f3f4f6"      // Light gray - Frame fills
};

Color Usage Rules:

  • Primary shapes: Blue stroke, transparent or light fill
  • Emphasis shapes: Orange stroke (use sparingly)
  • Containers/frames: Gray stroke, light gray fill
  • Arrows: Gray (neutral, never distracting)
  • All text: Dark gray #1f2937 (maximum readability)
  • Annotations: Purple for visual distinction

6. Validation Functions

JSON Structure Validation

function validateExcalidrawJSON(json) {
  const errors = [];

  // Check required top-level fields
  if (json.type !== "excalidraw") {
    errors.push("Missing or invalid 'type' (must be 'excalidraw')");
  }
  if (json.version !== 2) {
    errors.push("Version should be 2");
  }
  if (!Array.isArray(json.elements)) {
    errors.push("'elements' must be array");
  }
  if (typeof json.appState !== "object") {
    errors.push("'appState' must be object");
  }

  // Validate each element
  json.elements.forEach((element, index) => {
    if (!element.id) errors.push(`Element ${index} missing 'id'`);
    if (!element.type) errors.push(`Element ${index} missing 'type'`);
    if (typeof element.x !== "number") errors.push(`Element ${index} missing 'x'`);
    if (typeof element.y !== "number") errors.push(`Element ${index} missing 'y'`);

    // Check bound text references
    if (element.boundElements) {
      element.boundElements.forEach(bound => {
        if (bound.type === "text") {
          const textElement = json.elements.find(e => e.id === bound.id);
          if (!textElement) {
            errors.push(`Bound text ${bound.id} not found`);
          }
          if (textElement && textElement.containerId !== element.id) {
            errors.push(`Bound text ${bound.id} containerId mismatch`);
          }
        }
      });
    }

    // Check arrow bindings
    if (element.type === "arrow") {
      if (element.startBinding && element.startBinding.elementId) {
        const target = json.elements.find(e => e.id === element.startBinding.elementId);
        if (!target) {
          errors.push(`Arrow ${element.id} startBinding target not found`);
        }
      }
      if (element.endBinding && element.endBinding.elementId) {
        const target = json.elements.find(e => e.id === element.endBinding.elementId);
        if (!target) {
          errors.push(`Arrow ${element.id} endBinding target not found`);
        }
      }
    }
  });

  return {
    valid: errors.length === 0,
    errors: errors
  };
}

Cognitive Load Validation

function countCognitiveElements(json) {
  // Count distinct visual concepts (not total elements)
  const cognitiveUnits = {
    shapes: 0,
    arrows: 0,
    annotations: 0,
    frames: 0
  };

  const groupedElements = new Set();

  json.elements.forEach(element => {
    // Skip if part of counted group
    if (element.groupIds && element.groupIds.length > 0) {
      if (groupedElements.has(element.groupIds[0])) {
        return;  // Already counted
      }
      groupedElements.add(element.groupIds[0]);
    }

    // Skip bound text (counted with parent)
    if (element.containerId) return;

    switch(element.type) {
      case "rectangle":
      case "ellipse":
      case "diamond":
        cognitiveUnits.shapes++;
        break;
      case "arrow":
      case "line":
        cognitiveUnits.arrows++;
        break;
      case "text":
        cognitiveUnits.annotations++;
        break;
      case "frame":
        cognitiveUnits.frames++;
        break;
    }
  });

  const total = cognitiveUnits.shapes +
                cognitiveUnits.arrows +
                cognitiveUnits.annotations +
                cognitiveUnits.frames;

  return {
    breakdown: cognitiveUnits,
    total: total,
    withinLimit: total <= 9,  // 7±2 rule
    recommendation: total > 9 ? "SPLIT into multiple diagrams" : "Good"
  };
}

7. Assembly Function

function assembleExcalidrawJSON(elements) {
  return {
    type: "excalidraw",
    version: 2,
    source: "https://excalidraw.com",
    elements: elements,
    appState: {
      viewBackgroundColor: THEME_COLORS.background,
      gridSize: null,
      theme: "light"
    },
    files: {}
  };
}

Interactive Workflow

Follow this workflow when generating diagrams:

Step 1: Analyze Concept

Ask the user (if not clear from context):

  1. What's the main concept to convey?
  2. Who's the audience? (beginners, experts, mixed)
  3. Should this be formal or informal?
  4. Are there specific relationships/flows to highlight?

Step 2: Design Proposal

Show the user your design approach:

## Diagram Analysis

Semantic type: [Architecture/Flow/Mind Map/etc.]
Best platform: Excalidraw

Design approach:
- Layout: [Horizontal/Vertical/Radial]
- Elements: [List main shapes and their purpose]
- Frames: [If using containers]
- Arrows: [Key flows to show]
- Annotations: [Callouts for context]
- Hand-drawn aesthetic: roughness 1
- Colors: Colorblind-safe blue/orange

Element count: [N] shapes + [M] arrows + [P] annotations = [Total]
Grouped into [X] logical units → within cognitive limit ✓ / ⚠️ OVER LIMIT

Step 3: Show ASCII Preview

Show a text-based preview:

┌─────────────────────────────────┐
│  Container Name                 │
│  ┌──────────┐    ┌──────────┐  │
│  │ Shape A  │───→│ Shape B  │  │
│  └──────────┘    └──────────┘  │
└─────────────────────────────────┘
     ↑ "Annotation explaining flow"

Ask: "Proceed with JSON generation?"

Step 4: Generate JSON

  1. Create frames (if needed)
  2. Create shapes with bound text
  3. Position using layout algorithm
  4. Create arrows with bindings
  5. Add annotation callouts
  6. Apply grouping (if needed)
  7. Assemble final JSON
  8. Validate structure
  9. Check cognitive load
  10. Save to file

Step 5: Save and Render

File structure:

diagrams/
  └── <slide-title-slug>.excalidraw    # JSON source (editable)

public/images/<slide-title-slug>/
  └── diagram-excalidraw.svg           # Rendered SVG (for slide)

IMPORTANT: Always save source files to ./diagrams/ directory.

CRITICAL - Rendering Process:

  1. Save JSON: Use Write tool to save JSON to diagrams/<slug>.excalidraw
  2. Render to SVG: ALWAYS use render-excalidraw.sh script - NEVER attempt manual rendering: ${CLAUDE_PLUGIN_ROOT}/scripts/render-excalidraw.sh \ diagrams/<slug>.excalidraw \ public/images/<slug>/diagram-excalidraw.svg
  3. Script handles: The script automatically:

- Installs excalidraw-brute-export-cli if missing - Installs playwright chromium dependencies - Renders with correct parameters (--background 1, --embed-scene 0, etc.) - Ensures proper font rendering (Excalifont → Virgil → cursive → sans-serif)

DO NOT attempt to render Excalidraw any other way. ALWAYS use the script.

Step 6: Offer Iterations

✅ Excalidraw Diagram Generated!

Source: diagrams/<slug>.excalidraw
Rendered: public/images/<slug>/diagram-excalidraw.svg

Edit online: https://excalidraw.com (drag diagrams/<slug>.excalidraw file)

After editing, re-render with:
${CLAUDE_PLUGIN_ROOT}/scripts/render-excalidraw.sh \
  diagrams/<slug>.excalidraw \
  public/images/<slug>/diagram-excalidraw.svg

Refinement options:
- Adjust layout (horizontal ↔ vertical)
- Add more annotations
- Change colors/emphasis
- Simplify (remove elements)
- Add more detail

What would you like to adjust?

Example Generation: Architecture Diagram

Input: "Show Kubernetes device plugin architecture"

Step 1: Semantic Analysis

Type: Architecture + Flow
Concepts:
- Control Plane (container) - top
- Worker Node (container) - bottom
- Inside Worker Node: GPU, Device Plugin, Kubelet
- Flow: Discovery → Registration → Capacity Updates

Layout: Vertical (hierarchy)
Element count: 2 frames + 5 shapes + 3 arrows + 2 annotations = 12 base
BUT: Grouped into 2 logical units (control plane, worker node) = 7 cognitive units ✓

Step 2: JSON Generation

const elements = [];

// Create frames
const controlPlaneFrame = createFrame(50, 50, 700, 200, "Control Plane");
const workerNodeFrame = createFrame(50, 300, 700, 300, "Worker Node");
elements.push(controlPlaneFrame, workerNodeFrame);

// Control plane components
const [scheduler, schedulerText] = createRectangle(100, 100, 180, 80, "Scheduler", {
  frameId: controlPlaneFrame.id,
  strokeColor: THEME_COLORS.primary
});
const [apiServer, apiServerText] = createRectangle(400, 100, 180, 80, "API Server", {
  frameId: controlPlaneFrame.id,
  strokeColor: THEME_COLORS.primary
});
elements.push(scheduler, schedulerText, apiServer, apiServerText);

// Worker node components
const [gpu, gpuText] = createRectangle(100, 350, 180, 80, "GPU 0\nGPU 1", {
  frameId: workerNodeFrame.id,
  strokeColor: THEME_COLORS.secondary  // Orange for emphasis
});
const [plugin, pluginText] = createRectangle(350, 350, 180, 80, "Device Plugin", {
  frameId: workerNodeFrame.id,
  strokeColor: THEME_COLORS.primary
});
const [kubelet, kubeletText] = createRectangle(350, 480, 180, 80, "Kubelet", {
  frameId: workerNodeFrame.id,
  strokeColor: THEME_COLORS.primary
});
elements.push(gpu, gpuText, plugin, pluginText, kubelet, kubeletText);

// Arrows with bindings
const arrow1 = connectShapesHorizontal(gpu, plugin);  // Discovery
const arrow2 = createArrow(
  plugin.x + plugin.width / 2, plugin.y + plugin.height,
  kubelet.x + kubelet.width / 2, kubelet.y,
  {
    startBinding: createBindingPoint(plugin.id, plugin.x, plugin.y, plugin.width, plugin.height, "bottom"),
    endBinding: createBindingPoint(kubelet.id, kubelet.x, kubelet.y, kubelet.width, kubelet.height, "top")
  }
);
const arrow3 = createArrow(
  kubelet.x + kubelet.width / 2, kubelet.y,
  apiServer.x + apiServer.width / 2, apiServer.y + apiServer.height,
  {
    startBinding: createBindingPoint(kubelet.id, kubelet.x, kubelet.y, kubelet.width, kubelet.height, "top"),
    endBinding: createBindingPoint(apiServer.id, apiServer.x, apiServer.y, apiServer.width, apiServer.height, "bottom")
  }
);
elements.push(arrow1, arrow2, arrow3);

// Annotations
const [annotation1Text, annotation1Arrow] = createCallout(
  plugin.x + plugin.width, plugin.y + plugin.height / 2,
  "Your code",
  "top-right"
);
const [annotation2Text, annotation2Arrow] = createCallout(
  scheduler.x + scheduler.width / 2, scheduler.y,
  "Now aware!",
  "top-left"
);
elements.push(annotation1Text, annotation1Arrow, annotation2Text, annotation2Arrow);

// Assemble and validate
const json = assembleExcalidrawJSON(elements);
const validation = validateExcalidrawJSON(json);
const cognitiveCheck = countCognitiveElements(json);

if (!validation.valid) {
  console.error("Validation errors:", validation.errors);
  // Fix or abort
}

if (!cognitiveCheck.withinLimit) {
  console.warn(`Cognitive overload: ${cognitiveCheck.total} elements`);
  // Suggest splitting
}

// Save to file (source goes in diagrams/)
const filePath = "diagrams/device-plugin-architecture.excalidraw";
writeFile(filePath, JSON.stringify(json, null, 2));

Diagram Type Patterns

Flowchart Pattern

// Horizontal left-to-right flow
const nodes = ["Start", "Process", "Transform", "Output", "End"];
const positions = layoutHorizontalFlow(nodes);
const elements = [];

// Create shapes
const shapes = positions.map(pos => {
  const [rect, text] = createRectangle(pos.x, pos.y, pos.width, pos.height, pos.text);
  elements.push(rect, text);
  return rect;
});

// Connect with arrows
for (let i = 0; i < shapes.length - 1; i++) {
  const arrow = connectShapesHorizontal(shapes[i], shapes[i + 1]);
  elements.push(arrow);
}

// Add annotation at critical step
const [calloutText, calloutArrow] = createCallout(
  shapes[2].x + shapes[2].width / 2,
  shapes[2].y + shapes[2].height,
  "Key transformation!",
  "bottom-right"
);
elements.push(calloutText, calloutArrow);

Mind Map Pattern

// Radial layout from center
const centerConcept = "GPU Scheduling";
const branches = ["Device Plugin", "MIG", "Time-Slicing", "MPS", "Virtual GPUs"];

const positions = layoutRadial(centerConcept, branches);
const elements = [];

// Center ellipse
const [centerEllipse, centerText] = createEllipse(
  positions[0].x, positions[0].y,
  positions[0].width, positions[0].height,
  centerConcept,
  { strokeColor: THEME_COLORS.secondary, strokeWidth: 3 }
);
elements.push(centerEllipse, centerText);

// Branch ellipses with arrows
for (let i = 1; i < positions.length; i++) {
  const [ellipse, text] = createEllipse(
    positions[i].x, positions[i].y,
    positions[i].width, positions[i].height,
    positions[i].text
  );
  elements.push(ellipse, text);

  // Arrow from center to branch
  const arrow = createArrow(
    centerEllipse.x + centerEllipse.width / 2,
    centerEllipse.y + centerEllipse.height / 2,
    ellipse.x + ellipse.width / 2,
    ellipse.y + ellipse.height / 2
  );
  elements.push(arrow);
}

Quality Checklist

Before saving any diagram, verify:

  • Cognitive load: ≤9 elements total
  • Colors: Only approved palette (blue, orange, gray, purple)
  • Contrast: All text uses #1f2937 on white background
  • Text: Under 50 words total
  • Bindings: All arrows have startBinding and endBinding
  • Bound text: All text has correct containerId
  • JSON valid: Passes validateExcalidrawJSON()
  • One idea: Diagram conveys single clear concept
  • Frames: Used for containers/boundaries where appropriate
  • Hand-drawn: Roughness 1 for shapes (0 for frames and text)

Error Handling

If JSON generation fails:

  1. Log error details
  2. Attempt to fix (adjust positions, fix bindings)
  3. If unfixable: offer simpler design or Mermaid fallback
  4. Always save JSON even if rendering fails (user can edit at excalidraw.com)

If cognitive load exceeded:

  1. Warn user: "Diagram has X elements (limit: 9)"
  2. Suggest: "Split into 2 diagrams" or "Use progressive disclosure"
  3. Ask user to approve anyway or redesign

If validation fails:

  1. Show specific errors
  2. Attempt auto-fix for common issues
  3. If critical: abort and redesign

Integration Points

With /slidev:diagram Command

When diagram command analyzes a slide and determines Excalidraw is best fit:

Invoke Skill tool: skill: "slidev:excalidraw-generation"

The skill will take over generation process.

Auto-Suggestion Logic

Monitor for these triggers in slide content:

  • Keywords: "architecture", "components", "system design", "overview"
  • Nested structures detected
  • Spatial relationships described
  • Informal context ("brainstorm", "ideation", "workshop")

When detected, suggest: "I recommend creating an Excalidraw diagram for this - it excels at spatial layouts and informal designs. Proceed?"

Tools Available

  • Read: Read slide content, existing diagrams
  • Write: Save generated JSON to file
  • Bash: Execute rendering scripts if needed
  • AskUserQuestion: Interactive workflow questions

Next Steps After Generation

After successfully generating a diagram:

  1. Inform user of file locations
  2. Run render-excalidraw.sh to generate SVG
  3. Provide excalidraw.com editing instructions
  4. Offer refinement options
  5. Ask if they want to generate for another slide
  6. Suggest integration into slide (markdown image reference to SVG, NEVER embed JSON)

Remember: You are both a presentation designer (enforcing evidence-based constraints) AND an artist (creating beautiful, spatial, hand-drawn diagrams). Every diagram should be accessible, minimal, and convey exactly one clear idea.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

OpenCode

31.52%
按下载量换算1,635

Codex

21.33%
按下载量换算1,106

Claude Code

17.81%
按下载量换算924

Gemini CLI

13%
按下载量换算674

windsurf

8.21%
按下载量换算426

trae

3.92%
按下载量换算203

安全审计

Gen Agent Trust Hub

通过

Socket

可疑

Snyk

通过

权限和风险

只读

该 Skill 主要提供规则、说明或参考内容,本身偏只读;真正读写文件、联网或执行命令仍取决于宿主 Agent 的任务。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。来源字段存在多来源差异,先按来源优先级自动处理,无法消解时进入异常复核队列。

来源信息

继续浏览同类 Skills