docs: update AGENTS.md and README.md for rules system, remove beads
- Add rules/ directory documentation to both files - Update skill count from 25 to 15 modules - Remove beads references (issue tracking removed) - Update skills list with current active skills - Document flake.nix as proper Nix flake (not flake=false) - Add rules system integration section - Clean up sisyphus planning artifacts - Remove deprecated skills (memory, msteams, outlook)
This commit is contained in:
@@ -1,266 +1,544 @@
|
||||
---
|
||||
name: excalidraw
|
||||
description: Generate architecture diagrams as .excalidraw files from codebase analysis. Use when the user asks to create architecture diagrams, system diagrams, visualize codebase structure, or generate excalidraw files.
|
||||
description: "Create Excalidraw diagram JSON files that make visual arguments. Use when: (1) user wants to visualize workflows, architectures, or concepts, (2) creating system diagrams, (3) generating .excalidraw files. Triggers: excalidraw, diagram, visualize, architecture diagram, system diagram."
|
||||
compatibility: opencode
|
||||
---
|
||||
|
||||
# Excalidraw Diagram Generator
|
||||
# Excalidraw Diagram Creator
|
||||
|
||||
Generate architecture diagrams as `.excalidraw` files directly from codebase analysis.
|
||||
Generate `.excalidraw` JSON files that **argue visually**, not just display information.
|
||||
|
||||
## Customization
|
||||
|
||||
**All colors and brand-specific styles live in one file:** `references/color-palette.md`. Read it before generating any diagram and use it as the single source of truth for all color choices — shape fills, strokes, text colors, evidence artifact backgrounds, everything.
|
||||
|
||||
To make this skill produce diagrams in your own brand style, edit `color-palette.md`. Everything else in this file is universal design methodology and Excalidraw best practices.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
## Core Philosophy
|
||||
|
||||
**User just asks:**
|
||||
```
|
||||
"Generate an architecture diagram for this project"
|
||||
"Create an excalidraw diagram of the system"
|
||||
"Visualize this codebase as an excalidraw file"
|
||||
```
|
||||
**Diagrams should ARGUE, not DISPLAY.**
|
||||
|
||||
**Claude Code will:**
|
||||
1. Analyze the codebase (any language/framework)
|
||||
2. Identify components, services, databases, APIs
|
||||
3. Map relationships and data flows
|
||||
4. Generate valid `.excalidraw` JSON with dynamic IDs and labels
|
||||
A diagram isn't formatted text. It's a visual argument that shows relationships, causality, and flow that words alone can't express. The shape should BE the meaning.
|
||||
|
||||
**No prerequisites:** Works without existing diagrams, Terraform, or specific file types.
|
||||
**The Isomorphism Test**: If you removed all text, would the structure alone communicate the concept? If not, redesign.
|
||||
|
||||
**The Education Test**: Could someone learn something concrete from this diagram, or does it just label boxes? A good diagram teaches—it shows actual formats, real event names, concrete examples.
|
||||
|
||||
---
|
||||
|
||||
## Critical Rules
|
||||
## Depth Assessment (Do This First)
|
||||
|
||||
### 1. NEVER Use Diamond Shapes
|
||||
Before designing, determine what level of detail this diagram needs:
|
||||
|
||||
Diamond arrow connections are broken in raw Excalidraw JSON. Use styled rectangles instead:
|
||||
### Simple/Conceptual Diagrams
|
||||
Use abstract shapes when:
|
||||
- Explaining a mental model or philosophy
|
||||
- The audience doesn't need technical specifics
|
||||
- The concept IS the abstraction (e.g., "separation of concerns")
|
||||
|
||||
| Semantic Meaning | Rectangle Style |
|
||||
|------------------|-----------------|
|
||||
| Orchestrator/Hub | Coral (`#ffa8a8`/`#c92a2a`) + strokeWidth: 3 |
|
||||
| Decision Point | Orange (`#ffd8a8`/`#e8590c`) + dashed stroke |
|
||||
### Comprehensive/Technical Diagrams
|
||||
Use concrete examples when:
|
||||
- Diagramming a real system, protocol, or architecture
|
||||
- The diagram will be used to teach or explain (e.g., YouTube video)
|
||||
- The audience needs to understand what things actually look like
|
||||
- You're showing how multiple technologies integrate
|
||||
|
||||
### 2. Labels Require TWO Elements
|
||||
**For technical diagrams, you MUST include evidence artifacts** (see below).
|
||||
|
||||
The `label` property does NOT work in raw JSON. Every labeled shape needs:
|
||||
---
|
||||
|
||||
```json
|
||||
// 1. Shape with boundElements reference
|
||||
{
|
||||
"id": "my-box",
|
||||
"type": "rectangle",
|
||||
"boundElements": [{ "type": "text", "id": "my-box-text" }]
|
||||
}
|
||||
## Research Mandate (For Technical Diagrams)
|
||||
|
||||
// 2. Separate text element with containerId
|
||||
{
|
||||
"id": "my-box-text",
|
||||
"type": "text",
|
||||
"containerId": "my-box",
|
||||
"text": "My Label"
|
||||
}
|
||||
**Before drawing anything technical, research the actual specifications.**
|
||||
|
||||
If you're diagramming a protocol, API, or framework:
|
||||
1. Look up the actual JSON/data formats
|
||||
2. Find the real event names, method names, or API endpoints
|
||||
3. Understand how the pieces actually connect
|
||||
4. Use real terminology, not generic placeholders
|
||||
|
||||
Bad: "Protocol" → "Frontend"
|
||||
Good: "AG-UI streams events (RUN_STARTED, STATE_DELTA, A2UI_UPDATE)" → "CopilotKit renders via createA2UIMessageRenderer()"
|
||||
|
||||
**Research makes diagrams accurate AND educational.**
|
||||
|
||||
---
|
||||
|
||||
## Evidence Artifacts
|
||||
|
||||
Evidence artifacts are concrete examples that prove your diagram is accurate and help viewers learn. Include them in technical diagrams.
|
||||
|
||||
**Types of evidence artifacts** (choose what's relevant to your diagram):
|
||||
|
||||
| Artifact Type | When to Use | How to Render |
|
||||
|---------------|-------------|---------------|
|
||||
| **Code snippets** | APIs, integrations, implementation details | Dark rectangle + syntax-colored text (see color palette for evidence artifact colors) |
|
||||
| **Data/JSON examples** | Data formats, schemas, payloads | Dark rectangle + colored text (see color palette) |
|
||||
| **Event/step sequences** | Protocols, workflows, lifecycles | Timeline pattern (line + dots + labels) |
|
||||
| **UI mockups** | Showing actual output/results | Nested rectangles mimicking real UI |
|
||||
| **Real input content** | Showing what goes IN to a system | Rectangle with sample content visible |
|
||||
| **API/method names** | Real function calls, endpoints | Use actual names from docs, not placeholders |
|
||||
|
||||
**Example**: For a diagram about a streaming protocol, you might show:
|
||||
- The actual event names from the spec (not just "Event 1", "Event 2")
|
||||
- A code snippet showing how to connect
|
||||
- What the streamed data actually looks like
|
||||
|
||||
**Example**: For a diagram about a data transformation pipeline:
|
||||
- Show sample input data (actual format, not "Input")
|
||||
- Show sample output data (actual format, not "Output")
|
||||
- Show intermediate states if relevant
|
||||
|
||||
The key principle: **show what things actually look like**, not just what they're called.
|
||||
|
||||
---
|
||||
|
||||
## Multi-Zoom Architecture
|
||||
|
||||
Comprehensive diagrams operate at multiple zoom levels simultaneously. Think of it like a map that shows both the country borders AND the street names.
|
||||
|
||||
### Level 1: Summary Flow
|
||||
A simplified overview showing the full pipeline or process at a glance. Often placed at the top or bottom of the diagram.
|
||||
|
||||
*Example*: `Input → Processing → Output` or `Client → Server → Database`
|
||||
|
||||
### Level 2: Section Boundaries
|
||||
Labeled regions that group related components. These create visual "rooms" that help viewers understand what belongs together.
|
||||
|
||||
*Example*: Grouping by responsibility (Backend / Frontend), by phase (Setup / Execution / Cleanup), or by team (User / System / External)
|
||||
|
||||
### Level 3: Detail Inside Sections
|
||||
Evidence artifacts, code snippets, and concrete examples within each section. This is where the educational value lives.
|
||||
|
||||
*Example*: Inside a "Backend" section, you might show the actual API response format, not just a box labeled "API Response"
|
||||
|
||||
**For comprehensive diagrams, aim to include all three levels.** The summary gives context, the sections organize, and the details teach.
|
||||
|
||||
### Bad vs Good
|
||||
|
||||
| Bad (Displaying) | Good (Arguing) |
|
||||
|------------------|----------------|
|
||||
| 5 equal boxes with labels | Each concept has a shape that mirrors its behavior |
|
||||
| Card grid layout | Visual structure matches conceptual structure |
|
||||
| Icons decorating text | Shapes that ARE the meaning |
|
||||
| Same container for everything | Distinct visual vocabulary per concept |
|
||||
| Everything in a box | Free-floating text with selective containers |
|
||||
|
||||
### Simple vs Comprehensive (Know Which You Need)
|
||||
|
||||
| Simple Diagram | Comprehensive Diagram |
|
||||
|----------------|----------------------|
|
||||
| Generic labels: "Input" → "Process" → "Output" | Specific: shows what the input/output actually looks like |
|
||||
| Named boxes: "API", "Database", "Client" | Named boxes + examples of actual requests/responses |
|
||||
| "Events" or "Messages" label | Timeline with real event/message names from the spec |
|
||||
| "UI" or "Dashboard" rectangle | Mockup showing actual UI elements and content |
|
||||
| ~30 seconds to explain | ~2-3 minutes of teaching content |
|
||||
| Viewer learns the structure | Viewer learns the structure AND the details |
|
||||
|
||||
**Simple diagrams** are fine for abstract concepts, quick overviews, or when the audience already knows the details. **Comprehensive diagrams** are needed for technical architectures, tutorials, educational content, or when you want the diagram itself to teach.
|
||||
|
||||
---
|
||||
|
||||
## Container vs. Free-Floating Text
|
||||
|
||||
**Not every piece of text needs a shape around it.** Default to free-floating text. Add containers only when they serve a purpose.
|
||||
|
||||
| Use a Container When... | Use Free-Floating Text When... |
|
||||
|------------------------|-------------------------------|
|
||||
| It's the focal point of a section | It's a label or description |
|
||||
| It needs visual grouping with other elements | It's supporting detail or metadata |
|
||||
| Arrows need to connect to it | It describes something nearby |
|
||||
| The shape itself carries meaning (decision diamond, etc.) | It's a section title, subtitle, or annotation |
|
||||
| It represents a distinct "thing" in the system | It's a section title, subtitle, or annotation |
|
||||
|
||||
**Typography as hierarchy**: Use font size, weight, and color to create visual hierarchy without boxes. A 28px title doesn't need a rectangle around it.
|
||||
|
||||
**The container test**: For each boxed element, ask "Would this work as free-floating text?" If yes, remove the container.
|
||||
|
||||
---
|
||||
|
||||
## Design Process (Do This BEFORE Generating JSON)
|
||||
|
||||
### Step 0: Assess Depth Required
|
||||
Before anything else, determine if this needs to be:
|
||||
- **Simple/Conceptual**: Abstract shapes, labels, relationships (mental models, philosophies)
|
||||
- **Comprehensive/Technical**: Concrete examples, code snippets, real data (systems, architectures, tutorials)
|
||||
|
||||
**If comprehensive**: Do research first. Look up actual specs, formats, event names, APIs.
|
||||
|
||||
### Step 1: Understand Deeply
|
||||
Read the content. For each concept, ask:
|
||||
- What does this concept **DO**? (not what IS it)
|
||||
- What relationships exist between concepts?
|
||||
- What's the core transformation or flow?
|
||||
- **What would someone need to SEE to understand this?** (not just read about)
|
||||
|
||||
### Step 2: Map Concepts to Patterns
|
||||
For each concept, find the visual pattern that mirrors its behavior:
|
||||
|
||||
| If the concept... | Use this pattern |
|
||||
|-------------------|------------------|
|
||||
| Spawns multiple outputs | **Fan-out** (radial arrows from center) |
|
||||
| Combines inputs into one | **Convergence** (funnel, arrows merging) |
|
||||
| Has hierarchy/nesting | **Tree** (lines + free-floating text) |
|
||||
| Is a sequence of steps | **Timeline** (line + dots + free-floating labels) |
|
||||
| Loops or improves continuously | **Spiral/Cycle** (arrow returning to start) |
|
||||
| Is an abstract state or context | **Cloud** (overlapping ellipses) |
|
||||
| Transforms input to output | **Assembly line** (before → process → after) |
|
||||
| Compares two things | **Side-by-side** (parallel with contrast) |
|
||||
| Separates into phases | **Gap/Break** (visual separation between sections) |
|
||||
|
||||
### Step 3: Ensure Variety
|
||||
For multi-concept diagrams: **each major concept must use a different visual pattern**. No uniform cards or grids.
|
||||
|
||||
### Step 4: Sketch the Flow
|
||||
Before JSON, mentally trace how the eye moves through the diagram. There should be a clear visual story.
|
||||
|
||||
### Step 5: Generate JSON
|
||||
Only now create the Excalidraw elements. **See below for how to handle large diagrams.**
|
||||
|
||||
### Step 6: Render & Validate (MANDATORY)
|
||||
After generating the JSON, you MUST run the render-view-fix loop until the diagram looks right. This is not optional — see the **Render & Validate** section below for the full process.
|
||||
|
||||
---
|
||||
|
||||
## Large / Comprehensive Diagram Strategy
|
||||
|
||||
**For comprehensive or technical diagrams, you MUST build the JSON one section at a time.** Do NOT attempt to generate the entire file in a single pass. This is a hard constraint — output token limits mean a comprehensive diagram easily exceeds capacity in one shot. Even if it didn't, generating everything at once leads to worse quality. Section-by-section is better in every way.
|
||||
|
||||
### The Section-by-Section Workflow
|
||||
|
||||
**Phase 1: Build each section**
|
||||
|
||||
1. **Create the base file** with the JSON wrapper (`type`, `version`, `appState`, `files`) and the first section of elements.
|
||||
2. **Add one section per edit.** Each section gets its own dedicated pass — take your time with it. Think carefully about the layout, spacing, and how this section connects to what's already there.
|
||||
3. **Use descriptive string IDs** (e.g., `"trigger_rect"`, `"arrow_fan_left"`) so cross-section references are readable.
|
||||
4. **Namespace seeds by section** (e.g., section 1 uses 100xxx, section 2 uses 200xxx) to avoid collisions.
|
||||
5. **Update cross-section bindings** as you go. When a new section's element needs to bind to an element from a previous section (e.g., an arrow connecting sections), edit the earlier element's `boundElements` array at the same time.
|
||||
|
||||
**Phase 2: Review the whole**
|
||||
|
||||
After all sections are in place, read through the complete JSON and check:
|
||||
- Are cross-section arrows bound correctly on both ends?
|
||||
- Is the overall spacing balanced, or are some sections cramped while others have too much whitespace?
|
||||
- Do IDs and bindings all reference elements that actually exist?
|
||||
|
||||
Fix any alignment or binding issues before rendering.
|
||||
|
||||
**Phase 3: Render & validate**
|
||||
|
||||
Now run the render-view-fix loop from the Render & Validate section. This is where you'll catch visual issues that aren't obvious from JSON — overlaps, clipping, imbalanced composition.
|
||||
|
||||
### Section Boundaries
|
||||
|
||||
Plan your sections around natural visual groupings from the diagram plan. A typical large diagram might split into:
|
||||
|
||||
- **Section 1**: Entry point / trigger
|
||||
- **Section 2**: First decision or routing
|
||||
- **Section 3**: Main content (hero section — may be the largest single section)
|
||||
- **Section 4-N**: Remaining phases, outputs, etc.
|
||||
|
||||
Each section should be independently understandable: its elements, internal arrows, and any cross-references to adjacent sections.
|
||||
|
||||
### What NOT to Do
|
||||
|
||||
- **Don't generate the entire diagram in one response.** You will hit the output token limit and produce truncated, broken JSON. Even if the diagram is small enough to fit, splitting into sections produces better results.
|
||||
- **Don't write a Python generator script.** The templating and coordinate math seem helpful but introduce a layer of indirection that makes debugging harder. Hand-crafted JSON with descriptive IDs is more maintainable.
|
||||
|
||||
---
|
||||
|
||||
## Visual Pattern Library
|
||||
|
||||
### Fan-Out (One-to-Many)
|
||||
Central element with arrows radiating to multiple targets. Use for: sources, PRDs, root causes, central hubs.
|
||||
```
|
||||
○
|
||||
↗
|
||||
□ → ○
|
||||
↘
|
||||
○
|
||||
```
|
||||
|
||||
### 3. Elbow Arrows Need Three Properties
|
||||
### Convergence (Many-to-One)
|
||||
Multiple inputs merging through arrows to single output. Use for: aggregation, funnels, synthesis.
|
||||
```
|
||||
○ ↘
|
||||
○ → □
|
||||
○ ↗
|
||||
```
|
||||
|
||||
For 90-degree corners (not curved):
|
||||
### Tree (Hierarchy)
|
||||
Parent-child branching with connecting lines and free-floating text (no boxes needed). Use for: file systems, org charts, taxonomies.
|
||||
```
|
||||
label
|
||||
├── label
|
||||
│ ├── label
|
||||
│ └── label
|
||||
└── label
|
||||
```
|
||||
Use `line` elements for the trunk and branches, free-floating text for labels.
|
||||
|
||||
### Spiral/Cycle (Continuous Loop)
|
||||
Elements in sequence with arrow returning to start. Use for: feedback loops, iterative processes, evolution.
|
||||
```
|
||||
□ → □
|
||||
↑ ↓
|
||||
□ ← □
|
||||
```
|
||||
|
||||
### Cloud (Abstract State)
|
||||
Overlapping ellipses with varied sizes. Use for: context, memory, conversations, mental states.
|
||||
|
||||
### Assembly Line (Transformation)
|
||||
Input → Process Box → Output with clear before/after. Use for: transformations, processing, conversion.
|
||||
```
|
||||
○○○ → [PROCESS] → □□□
|
||||
chaos order
|
||||
```
|
||||
|
||||
### Side-by-Side (Comparison)
|
||||
Two parallel structures with visual contrast. Use for: before/after, options, trade-offs.
|
||||
|
||||
### Gap/Break (Separation)
|
||||
Visual whitespace or barrier between sections. Use for: phase changes, context resets, boundaries.
|
||||
|
||||
### Lines as Structure
|
||||
Use lines (type: `line`, not arrows) as primary structural elements instead of boxes:
|
||||
- **Timelines**: Vertical or horizontal line with small dots (10-20px ellipses) at intervals, free-floating labels beside each dot
|
||||
- **Tree structures**: Vertical trunk line + horizontal branch lines, with free-floating text labels (no boxes needed)
|
||||
- **Dividers**: Thin dashed lines to separate sections
|
||||
- **Flow spines**: A central line that elements relate to, rather than connecting boxes
|
||||
|
||||
```
|
||||
Timeline: Tree:
|
||||
●─── Label 1 │
|
||||
│ ├── item
|
||||
●─── Label 2 │ ├── sub
|
||||
│ │ └── sub
|
||||
●─── Label 3 └── item
|
||||
```
|
||||
|
||||
Lines + free-floating text often creates a cleaner result than boxes + contained text.
|
||||
|
||||
---
|
||||
|
||||
## Shape Meaning
|
||||
|
||||
Choose shape based on what it represents—or use no shape at all:
|
||||
|
||||
| Concept Type | Shape | Why |
|
||||
|--------------|-------|-----|
|
||||
| Labels, descriptions, details | **none** (free-floating text) | Typography creates hierarchy |
|
||||
| Section titles, annotations | **none** (free-floating text) | Font size/weight is enough |
|
||||
| Markers on a timeline | small `ellipse` (10-20px) | Visual anchor, not container |
|
||||
| Start, trigger, input | `ellipse` | Soft, origin-like |
|
||||
| End, output, result | `ellipse` | Completion, destination |
|
||||
| Decision, condition | `diamond` | Classic decision symbol |
|
||||
| Process, action, step | `rectangle` | Contained action |
|
||||
| Abstract state, context | overlapping `ellipse` | Fuzzy, cloud-like |
|
||||
| Hierarchy node | lines + text (no boxes) | Structure through lines |
|
||||
|
||||
**Rule**: Default to no container. Add shapes only when they carry meaning. Aim for <30% of text elements to be inside containers.
|
||||
|
||||
---
|
||||
|
||||
## Color as Meaning
|
||||
|
||||
Colors encode information, not decoration. Every color choice should come from `references/color-palette.md` — the semantic shape colors, text hierarchy colors, and evidence artifact colors are all defined there.
|
||||
|
||||
**Key principles:**
|
||||
- Each semantic purpose (start, end, decision, AI, error, etc.) has a specific fill/stroke pair
|
||||
- Free-floating text uses color for hierarchy (titles, subtitles, details — each at a different level)
|
||||
- Evidence artifacts (code snippets, JSON examples) use their own dark background + colored text scheme
|
||||
- Always pair a darker stroke with a lighter fill for contrast
|
||||
|
||||
**Do not invent new colors.** If a concept doesn't fit an existing semantic category, use Primary/Neutral or Secondary.
|
||||
|
||||
---
|
||||
|
||||
## Modern Aesthetics
|
||||
|
||||
For clean, professional diagrams:
|
||||
|
||||
### Roughness
|
||||
- `roughness: 0` — Clean, crisp edges. Use for modern/technical diagrams.
|
||||
- `roughness: 1` — Hand-drawn, organic feel. Use for brainstorming/informal diagrams.
|
||||
|
||||
**Default to 0** for most professional use cases.
|
||||
|
||||
### Stroke Width
|
||||
- `strokeWidth: 1` — Thin, elegant. Good for lines, dividers, subtle connections.
|
||||
- `strokeWidth: 2` — Standard. Good for shapes and primary arrows.
|
||||
- `strokeWidth: 3` — Bold. Use sparingly for emphasis (main flow line, key connections).
|
||||
|
||||
### Opacity
|
||||
**Always use `opacity: 100` for all elements.** Use color, size, and stroke width to create hierarchy instead of transparency.
|
||||
|
||||
### Small Markers Instead of Shapes
|
||||
Instead of full shapes, use small dots (10-20px ellipses) as:
|
||||
- Timeline markers
|
||||
- Bullet points
|
||||
- Connection nodes
|
||||
- Visual anchors for free-floating text
|
||||
|
||||
---
|
||||
|
||||
## Layout Principles
|
||||
|
||||
### Hierarchy Through Scale
|
||||
- **Hero**: 300×150 - visual anchor, most important
|
||||
- **Primary**: 180×90
|
||||
- **Secondary**: 120×60
|
||||
- **Small**: 60×40
|
||||
|
||||
### Whitespace = Importance
|
||||
The most important element has the most empty space around it (200px+).
|
||||
|
||||
### Flow Direction
|
||||
Guide the eye: typically left→right or top→bottom for sequences, radial for hub-and-spoke.
|
||||
|
||||
### Connections Required
|
||||
Position alone doesn't show relationships. If A relates to B, there must be an arrow.
|
||||
|
||||
---
|
||||
|
||||
## Text Rules
|
||||
|
||||
**CRITICAL**: The JSON `text` property contains ONLY readable words.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "arrow",
|
||||
"roughness": 0, // Clean lines
|
||||
"roundness": null, // Sharp corners
|
||||
"elbowed": true // 90-degree mode
|
||||
"id": "myElement1",
|
||||
"text": "Start",
|
||||
"originalText": "Start"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Arrow Edge Calculations
|
||||
|
||||
Arrows must start/end at shape edges, not centers:
|
||||
|
||||
| Edge | Formula |
|
||||
|------|---------|
|
||||
| Top | `(x + width/2, y)` |
|
||||
| Bottom | `(x + width/2, y + height)` |
|
||||
| Left | `(x, y + height/2)` |
|
||||
| Right | `(x + width, y + height/2)` |
|
||||
|
||||
**Detailed arrow routing:** See `references/arrows.md`
|
||||
Settings: `fontSize: 16`, `fontFamily: 3`, `textAlign: "center"`, `verticalAlign: "middle"`
|
||||
|
||||
---
|
||||
|
||||
## Element Types
|
||||
## JSON Structure
|
||||
|
||||
| Type | Use For |
|
||||
|------|---------|
|
||||
| `rectangle` | Services, databases, containers, orchestrators |
|
||||
| `ellipse` | Users, external systems, start/end points |
|
||||
| `text` | Labels inside shapes, titles, annotations |
|
||||
| `arrow` | Data flow, connections, dependencies |
|
||||
| `line` | Grouping boundaries, separators |
|
||||
|
||||
**Full JSON format:** See `references/json-format.md`
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Analyze Codebase
|
||||
|
||||
Discover components by looking for:
|
||||
|
||||
| Codebase Type | What to Look For |
|
||||
|---------------|------------------|
|
||||
| Monorepo | `packages/*/package.json`, workspace configs |
|
||||
| Microservices | `docker-compose.yml`, k8s manifests |
|
||||
| IaC | Terraform/Pulumi resource definitions |
|
||||
| Backend API | Route definitions, controllers, DB models |
|
||||
| Frontend | Component hierarchy, API calls |
|
||||
|
||||
**Use tools:**
|
||||
- `Glob` → `**/package.json`, `**/Dockerfile`, `**/*.tf`
|
||||
- `Grep` → `app.get`, `@Controller`, `CREATE TABLE`
|
||||
- `Read` → README, config files, entry points
|
||||
|
||||
### Step 2: Plan Layout
|
||||
|
||||
**Vertical flow (most common):**
|
||||
```
|
||||
Row 1: Users/Entry points (y: 100)
|
||||
Row 2: Frontend/Gateway (y: 230)
|
||||
Row 3: Orchestration (y: 380)
|
||||
Row 4: Services (y: 530)
|
||||
Row 5: Data layer (y: 680)
|
||||
|
||||
Columns: x = 100, 300, 500, 700, 900
|
||||
Element size: 160-200px x 80-90px
|
||||
```
|
||||
|
||||
**Other patterns:** See `references/examples.md`
|
||||
|
||||
### Step 3: Generate Elements
|
||||
|
||||
For each component:
|
||||
1. Create shape with unique `id`
|
||||
2. Add `boundElements` referencing text
|
||||
3. Create text with `containerId`
|
||||
4. Choose color based on type
|
||||
|
||||
**Color palettes:** See `references/colors.md`
|
||||
|
||||
### Step 4: Add Connections
|
||||
|
||||
For each relationship:
|
||||
1. Calculate source edge point
|
||||
2. Plan elbow route (avoid overlaps)
|
||||
3. Create arrow with `points` array
|
||||
4. Match stroke color to destination type
|
||||
|
||||
**Arrow patterns:** See `references/arrows.md`
|
||||
|
||||
### Step 5: Add Grouping (Optional)
|
||||
|
||||
For logical groupings:
|
||||
- Large transparent rectangle with `strokeStyle: "dashed"`
|
||||
- Standalone text label at top-left
|
||||
|
||||
### Step 6: Validate and Write
|
||||
|
||||
Run validation before writing. Save to `docs/` or user-specified path.
|
||||
|
||||
**Validation checklist:** See `references/validation.md`
|
||||
|
||||
---
|
||||
|
||||
## Quick Arrow Reference
|
||||
|
||||
**Straight down:**
|
||||
```json
|
||||
{ "points": [[0, 0], [0, 110]], "x": 590, "y": 290 }
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [...],
|
||||
"appState": {
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"gridSize": 20
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
```
|
||||
|
||||
**L-shape (left then down):**
|
||||
```json
|
||||
{ "points": [[0, 0], [-325, 0], [-325, 125]], "x": 525, "y": 420 }
|
||||
```
|
||||
## Element Templates
|
||||
|
||||
**U-turn (callback):**
|
||||
```json
|
||||
{ "points": [[0, 0], [50, 0], [50, -125], [20, -125]], "x": 710, "y": 440 }
|
||||
```
|
||||
|
||||
**Arrow width/height** = bounding box of points:
|
||||
```
|
||||
points [[0,0], [-440,0], [-440,70]] → width=440, height=70
|
||||
```
|
||||
|
||||
**Multiple arrows from same edge** - stagger positions:
|
||||
```
|
||||
5 arrows: 20%, 35%, 50%, 65%, 80% across edge width
|
||||
```
|
||||
See `references/element-templates.md` for copy-paste JSON templates for each element type (text, line, dot, rectangle, arrow). Pull colors from `references/color-palette.md` based on each element's semantic purpose.
|
||||
|
||||
---
|
||||
|
||||
## Default Color Palette
|
||||
## Render & Validate (MANDATORY)
|
||||
|
||||
| Component | Background | Stroke |
|
||||
|-----------|------------|--------|
|
||||
| Frontend | `#a5d8ff` | `#1971c2` |
|
||||
| Backend/API | `#d0bfff` | `#7048e8` |
|
||||
| Database | `#b2f2bb` | `#2f9e44` |
|
||||
| Storage | `#ffec99` | `#f08c00` |
|
||||
| AI/ML | `#e599f7` | `#9c36b5` |
|
||||
| External APIs | `#ffc9c9` | `#e03131` |
|
||||
| Orchestration | `#ffa8a8` | `#c92a2a` |
|
||||
| Message Queue | `#fff3bf` | `#fab005` |
|
||||
| Cache | `#ffe8cc` | `#fd7e14` |
|
||||
| Users | `#e7f5ff` | `#1971c2` |
|
||||
You cannot judge a diagram from JSON alone. After generating or editing the Excalidraw JSON, you MUST render it to PNG, view the image, and fix what you see — in a loop until it's right. This is a core part of the workflow, not a final check.
|
||||
|
||||
**Cloud-specific palettes:** See `references/colors.md`
|
||||
### How to Render
|
||||
|
||||
Run the render script from the skill's `references/` directory:
|
||||
|
||||
```bash
|
||||
python3 <skill-references-dir>/render_excalidraw.py <path-to-file.excalidraw>
|
||||
```
|
||||
|
||||
This outputs a PNG next to the `.excalidraw` file. Then use the **Read tool** on the PNG to actually view it.
|
||||
|
||||
### The Loop
|
||||
|
||||
After generating the initial JSON, run this cycle:
|
||||
|
||||
**1. Render & View** — Run the render script, then Read the PNG.
|
||||
|
||||
**2. Audit against your original vision** — Before looking for bugs, compare the rendered result to what you designed in Steps 1-4. Ask:
|
||||
- Does the visual structure match the conceptual structure you planned?
|
||||
- Does each section use the pattern you intended (fan-out, convergence, timeline, etc.)?
|
||||
- Does the eye flow through the diagram in the order you designed?
|
||||
- Is the visual hierarchy correct — hero elements dominant, supporting elements smaller?
|
||||
- For technical diagrams: are the evidence artifacts (code snippets, data examples) readable and properly placed?
|
||||
|
||||
**3. Check for visual defects:**
|
||||
- Text clipped by or overflowing its container
|
||||
- Text or shapes overlapping other elements
|
||||
- Arrows crossing through elements instead of routing around them
|
||||
- Arrows landing on the wrong element or pointing into empty space
|
||||
- Labels floating ambiguously (not clearly anchored to what they describe)
|
||||
- Uneven spacing between elements that should be evenly spaced
|
||||
- Sections with too much whitespace next to sections that are too cramped
|
||||
- Text too small to read at the rendered size
|
||||
- Overall composition feels lopsided or unbalanced
|
||||
|
||||
**4. Fix** — Edit the JSON to address everything you found. Common fixes:
|
||||
- Widen containers when text is clipped
|
||||
- Adjust `x`/`y` coordinates to fix spacing and alignment
|
||||
- Add intermediate waypoints to arrow `points` arrays to route around elements
|
||||
- Reposition labels closer to the element they describe
|
||||
- Resize elements to rebalance visual weight across sections
|
||||
|
||||
**5. Re-render & re-view** — Run the render script again and Read the new PNG.
|
||||
|
||||
**6. Repeat** — Keep cycling until the diagram passes both the vision check (Step 2) and the defect check (Step 3). Typically takes 2-4 iterations. Don't stop after one pass just because there are no critical bugs — if the composition could be better, improve it.
|
||||
|
||||
### When to Stop
|
||||
|
||||
The loop is done when:
|
||||
- The rendered diagram matches the conceptual design from your planning steps
|
||||
- No text is clipped, overlapping, or unreadable
|
||||
- Arrows route cleanly and connect to the right elements
|
||||
- Spacing is consistent and the composition is balanced
|
||||
- You'd be comfortable showing it to someone without caveats
|
||||
|
||||
---
|
||||
|
||||
## Quick Validation Checklist
|
||||
## Quality Checklist
|
||||
|
||||
Before writing file:
|
||||
- [ ] Every shape with label has boundElements + text element
|
||||
- [ ] Text elements have containerId matching shape
|
||||
- [ ] Multi-point arrows have `elbowed: true`, `roundness: null`
|
||||
- [ ] Arrow x,y = source shape edge point
|
||||
- [ ] Arrow final point offset reaches target edge
|
||||
- [ ] No diamond shapes
|
||||
- [ ] No duplicate IDs
|
||||
### Depth & Evidence (Check First for Technical Diagrams)
|
||||
1. **Research done**: Did you look up actual specs, formats, event names?
|
||||
2. **Evidence artifacts**: Are there code snippets, JSON examples, or real data?
|
||||
3. **Multi-zoom**: Does it have summary flow + section boundaries + detail?
|
||||
4. **Concrete over abstract**: Real content shown, not just labeled boxes?
|
||||
5. **Educational value**: Could someone learn something concrete from this?
|
||||
|
||||
**Full validation algorithm:** See `references/validation.md`
|
||||
### Conceptual
|
||||
6. **Isomorphism**: Does each visual structure mirror its concept's behavior?
|
||||
7. **Argument**: Does the diagram SHOW something text alone couldn't?
|
||||
8. **Variety**: Does each major concept use a different visual pattern?
|
||||
9. **No uniform containers**: Avoided card grids and equal boxes?
|
||||
|
||||
---
|
||||
### Container Discipline
|
||||
10. **Minimal containers**: Could any boxed element work as free-floating text instead?
|
||||
11. **Lines as structure**: Are tree/timeline patterns using lines + text rather than boxes?
|
||||
12. **Typography hierarchy**: Are font size and color creating visual hierarchy (reducing need for boxes)?
|
||||
|
||||
## Common Issues
|
||||
### Structural
|
||||
13. **Connections**: Every relationship has an arrow or line
|
||||
14. **Flow**: Clear visual path for the eye to follow
|
||||
15. **Hierarchy**: Important elements are larger/more isolated
|
||||
|
||||
| Issue | Fix |
|
||||
|-------|-----|
|
||||
| Labels don't appear | Use TWO elements (shape + text), not `label` property |
|
||||
| Arrows curved | Add `elbowed: true`, `roundness: null`, `roughness: 0` |
|
||||
| Arrows floating | Calculate x,y from shape edge, not center |
|
||||
| Arrows overlapping | Stagger start positions across edge |
|
||||
### Technical
|
||||
16. **Text clean**: `text` contains only readable words
|
||||
17. **Font**: `fontFamily: 3`
|
||||
18. **Roughness**: `roughness: 0` for clean/modern (unless hand-drawn style requested)
|
||||
19. **Opacity**: `opacity: 100` for all elements (no transparency)
|
||||
20. **Container ratio**: <30% of text elements should be inside containers
|
||||
|
||||
**Detailed bug fixes:** See `references/validation.md`
|
||||
|
||||
---
|
||||
|
||||
## Reference Files
|
||||
|
||||
| File | Contents |
|
||||
|------|----------|
|
||||
| `references/json-format.md` | Element types, required properties, text bindings |
|
||||
| `references/arrows.md` | Routing algorithm, patterns, bindings, staggering |
|
||||
| `references/colors.md` | Default, AWS, Azure, GCP, K8s palettes |
|
||||
| `references/examples.md` | Complete JSON examples, layout patterns |
|
||||
| `references/validation.md` | Checklists, validation algorithm, bug fixes |
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
- **Location:** `docs/architecture/` or user-specified
|
||||
- **Filename:** Descriptive, e.g., `system-architecture.excalidraw`
|
||||
- **Testing:** Open in https://excalidraw.com or VS Code extension
|
||||
### Visual Validation (Render Required)
|
||||
21. **Rendered to PNG**: Diagram has been rendered and visually inspected
|
||||
22. **No text overflow**: All text fits within its container
|
||||
23. **No overlapping elements**: Shapes and text don't overlap unintentionally
|
||||
24. **Even spacing**: Similar elements have consistent spacing
|
||||
25. **Arrows land correctly**: Arrows connect to intended elements without crossing others
|
||||
26. **Readable at export size**: Text is legible in the rendered PNG
|
||||
27. **Balanced composition**: No large empty voids or overcrowded regions
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
# Arrow Routing Reference
|
||||
|
||||
Complete guide for creating elbow arrows with proper connections.
|
||||
|
||||
---
|
||||
|
||||
## Critical: Elbow Arrow Properties
|
||||
|
||||
Three required properties for 90-degree corners:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "arrow",
|
||||
"roughness": 0, // Clean lines
|
||||
"roundness": null, // Sharp corners (not curved)
|
||||
"elbowed": true // Enables elbow mode
|
||||
}
|
||||
```
|
||||
|
||||
**Without these, arrows will be curved, not 90-degree elbows.**
|
||||
|
||||
---
|
||||
|
||||
## Edge Calculation Formulas
|
||||
|
||||
| Shape Type | Edge | Formula |
|
||||
|------------|------|---------|
|
||||
| Rectangle | Top | `(x + width/2, y)` |
|
||||
| Rectangle | Bottom | `(x + width/2, y + height)` |
|
||||
| Rectangle | Left | `(x, y + height/2)` |
|
||||
| Rectangle | Right | `(x + width, y + height/2)` |
|
||||
| Ellipse | Top | `(x + width/2, y)` |
|
||||
| Ellipse | Bottom | `(x + width/2, y + height)` |
|
||||
|
||||
---
|
||||
|
||||
## Universal Arrow Routing Algorithm
|
||||
|
||||
```
|
||||
FUNCTION createArrow(source, target, sourceEdge, targetEdge):
|
||||
// Step 1: Get source edge point
|
||||
sourcePoint = getEdgePoint(source, sourceEdge)
|
||||
|
||||
// Step 2: Get target edge point
|
||||
targetPoint = getEdgePoint(target, targetEdge)
|
||||
|
||||
// Step 3: Calculate offsets
|
||||
dx = targetPoint.x - sourcePoint.x
|
||||
dy = targetPoint.y - sourcePoint.y
|
||||
|
||||
// Step 4: Determine routing pattern
|
||||
IF sourceEdge == "bottom" AND targetEdge == "top":
|
||||
IF abs(dx) < 10: // Nearly aligned
|
||||
points = [[0, 0], [0, dy]]
|
||||
ELSE: // Need L-shape
|
||||
points = [[0, 0], [dx, 0], [dx, dy]]
|
||||
|
||||
ELSE IF sourceEdge == "right" AND targetEdge == "left":
|
||||
IF abs(dy) < 10:
|
||||
points = [[0, 0], [dx, 0]]
|
||||
ELSE:
|
||||
points = [[0, 0], [0, dy], [dx, dy]]
|
||||
|
||||
ELSE IF sourceEdge == targetEdge: // U-turn
|
||||
clearance = 50
|
||||
IF sourceEdge == "right":
|
||||
points = [[0, 0], [clearance, 0], [clearance, dy], [dx, dy]]
|
||||
ELSE IF sourceEdge == "bottom":
|
||||
points = [[0, 0], [0, clearance], [dx, clearance], [dx, dy]]
|
||||
|
||||
// Step 5: Calculate bounding box
|
||||
width = max(abs(p[0]) for p in points)
|
||||
height = max(abs(p[1]) for p in points)
|
||||
|
||||
RETURN {x: sourcePoint.x, y: sourcePoint.y, points, width, height}
|
||||
|
||||
FUNCTION getEdgePoint(shape, edge):
|
||||
SWITCH edge:
|
||||
"top": RETURN (shape.x + shape.width/2, shape.y)
|
||||
"bottom": RETURN (shape.x + shape.width/2, shape.y + shape.height)
|
||||
"left": RETURN (shape.x, shape.y + shape.height/2)
|
||||
"right": RETURN (shape.x + shape.width, shape.y + shape.height/2)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Arrow Patterns Reference
|
||||
|
||||
| Pattern | Points | Use Case |
|
||||
|---------|--------|----------|
|
||||
| Down | `[[0,0], [0,h]]` | Vertical connection |
|
||||
| Right | `[[0,0], [w,0]]` | Horizontal connection |
|
||||
| L-left-down | `[[0,0], [-w,0], [-w,h]]` | Go left, then down |
|
||||
| L-right-down | `[[0,0], [w,0], [w,h]]` | Go right, then down |
|
||||
| L-down-left | `[[0,0], [0,h], [-w,h]]` | Go down, then left |
|
||||
| L-down-right | `[[0,0], [0,h], [w,h]]` | Go down, then right |
|
||||
| S-shape | `[[0,0], [0,h1], [w,h1], [w,h2]]` | Navigate around obstacles |
|
||||
| U-turn | `[[0,0], [w,0], [w,-h], [0,-h]]` | Callback/return arrows |
|
||||
|
||||
---
|
||||
|
||||
## Worked Examples
|
||||
|
||||
### Vertical Connection (Bottom to Top)
|
||||
|
||||
```
|
||||
Source: x=500, y=200, width=180, height=90
|
||||
Target: x=500, y=400, width=180, height=90
|
||||
|
||||
source_bottom = (500 + 180/2, 200 + 90) = (590, 290)
|
||||
target_top = (500 + 180/2, 400) = (590, 400)
|
||||
|
||||
Arrow x = 590, y = 290
|
||||
Distance = 400 - 290 = 110
|
||||
Points = [[0, 0], [0, 110]]
|
||||
```
|
||||
|
||||
### Fan-out (One to Many)
|
||||
|
||||
```
|
||||
Orchestrator: x=570, y=400, width=140, height=80
|
||||
Target: x=120, y=550, width=160, height=80
|
||||
|
||||
orchestrator_bottom = (570 + 140/2, 400 + 80) = (640, 480)
|
||||
target_top = (120 + 160/2, 550) = (200, 550)
|
||||
|
||||
Arrow x = 640, y = 480
|
||||
Horizontal offset = 200 - 640 = -440
|
||||
Vertical offset = 550 - 480 = 70
|
||||
|
||||
Points = [[0, 0], [-440, 0], [-440, 70]] // Left first, then down
|
||||
```
|
||||
|
||||
### U-turn (Callback)
|
||||
|
||||
```
|
||||
Source: x=570, y=400, width=140, height=80
|
||||
Target: x=550, y=270, width=180, height=90
|
||||
Connection: Right of source -> Right of target
|
||||
|
||||
source_right = (570 + 140, 400 + 80/2) = (710, 440)
|
||||
target_right = (550 + 180, 270 + 90/2) = (730, 315)
|
||||
|
||||
Arrow x = 710, y = 440
|
||||
Vertical distance = 315 - 440 = -125
|
||||
Final x offset = 730 - 710 = 20
|
||||
|
||||
Points = [[0, 0], [50, 0], [50, -125], [20, -125]]
|
||||
// Right 50px (clearance), up 125px, left 30px
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Staggering Multiple Arrows
|
||||
|
||||
When N arrows leave from same edge, spread evenly:
|
||||
|
||||
```
|
||||
FUNCTION getStaggeredPositions(shape, edge, numArrows):
|
||||
positions = []
|
||||
FOR i FROM 0 TO numArrows-1:
|
||||
percentage = 0.2 + (0.6 * i / (numArrows - 1))
|
||||
|
||||
IF edge == "bottom" OR edge == "top":
|
||||
x = shape.x + shape.width * percentage
|
||||
y = (edge == "bottom") ? shape.y + shape.height : shape.y
|
||||
ELSE:
|
||||
x = (edge == "right") ? shape.x + shape.width : shape.x
|
||||
y = shape.y + shape.height * percentage
|
||||
|
||||
positions.append({x, y})
|
||||
RETURN positions
|
||||
|
||||
// Examples:
|
||||
// 2 arrows: 20%, 80%
|
||||
// 3 arrows: 20%, 50%, 80%
|
||||
// 5 arrows: 20%, 35%, 50%, 65%, 80%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Arrow Bindings
|
||||
|
||||
For better visual attachment, use `startBinding` and `endBinding`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "arrow-workflow-convert",
|
||||
"type": "arrow",
|
||||
"x": 525,
|
||||
"y": 420,
|
||||
"width": 325,
|
||||
"height": 125,
|
||||
"points": [[0, 0], [-325, 0], [-325, 125]],
|
||||
"roughness": 0,
|
||||
"roundness": null,
|
||||
"elbowed": true,
|
||||
"startBinding": {
|
||||
"elementId": "cloud-workflows",
|
||||
"focus": 0,
|
||||
"gap": 1,
|
||||
"fixedPoint": [0.5, 1]
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "convert-pdf-service",
|
||||
"focus": 0,
|
||||
"gap": 1,
|
||||
"fixedPoint": [0.5, 0]
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
}
|
||||
```
|
||||
|
||||
### fixedPoint Values
|
||||
|
||||
- Top center: `[0.5, 0]`
|
||||
- Bottom center: `[0.5, 1]`
|
||||
- Left center: `[0, 0.5]`
|
||||
- Right center: `[1, 0.5]`
|
||||
|
||||
### Update Shape boundElements
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "cloud-workflows",
|
||||
"boundElements": [
|
||||
{ "type": "text", "id": "cloud-workflows-text" },
|
||||
{ "type": "arrow", "id": "arrow-workflow-convert" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bidirectional Arrows
|
||||
|
||||
For two-way data flows:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "arrow",
|
||||
"startArrowhead": "arrow",
|
||||
"endArrowhead": "arrow"
|
||||
}
|
||||
```
|
||||
|
||||
Arrowhead options: `null`, `"arrow"`, `"bar"`, `"dot"`, `"triangle"`
|
||||
|
||||
---
|
||||
|
||||
## Arrow Labels
|
||||
|
||||
Position standalone text near arrow midpoint:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "arrow-api-db-label",
|
||||
"type": "text",
|
||||
"x": 305, // Arrow x + offset
|
||||
"y": 245, // Arrow midpoint
|
||||
"text": "SQL",
|
||||
"fontSize": 12,
|
||||
"containerId": null,
|
||||
"backgroundColor": "#ffffff"
|
||||
}
|
||||
```
|
||||
|
||||
**Positioning formula:**
|
||||
- Vertical: `label.y = arrow.y + (total_height / 2)`
|
||||
- Horizontal: `label.x = arrow.x + (total_width / 2)`
|
||||
- L-shaped: Position at corner or longest segment midpoint
|
||||
|
||||
---
|
||||
|
||||
## Width/Height Calculation
|
||||
|
||||
Arrow `width` and `height` = bounding box of path:
|
||||
|
||||
```
|
||||
points = [[0, 0], [-440, 0], [-440, 70]]
|
||||
width = abs(-440) = 440
|
||||
height = abs(70) = 70
|
||||
|
||||
points = [[0, 0], [50, 0], [50, -125], [20, -125]]
|
||||
width = max(abs(50), abs(20)) = 50
|
||||
height = abs(-125) = 125
|
||||
```
|
||||
67
skills/excalidraw/references/color-palette.md
Normal file
67
skills/excalidraw/references/color-palette.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Color Palette & Brand Style
|
||||
|
||||
**This is the single source of truth for all colors and brand-specific styles.** To customize diagrams for your own brand, edit this file — everything else in the skill is universal.
|
||||
|
||||
---
|
||||
|
||||
## Shape Colors (Semantic)
|
||||
|
||||
Colors encode meaning, not decoration. Each semantic purpose has a fill/stroke pair.
|
||||
|
||||
| Semantic Purpose | Fill | Stroke |
|
||||
|------------------|------|--------|
|
||||
| Primary/Neutral | `#3b82f6` | `#1e3a5f` |
|
||||
| Secondary | `#60a5fa` | `#1e3a5f` |
|
||||
| Tertiary | `#93c5fd` | `#1e3a5f` |
|
||||
| Start/Trigger | `#fed7aa` | `#c2410c` |
|
||||
| End/Success | `#a7f3d0` | `#047857` |
|
||||
| Warning/Reset | `#fee2e2` | `#dc2626` |
|
||||
| Decision | `#fef3c7` | `#b45309` |
|
||||
| AI/LLM | `#ddd6fe` | `#6d28d9` |
|
||||
| Inactive/Disabled | `#dbeafe` | `#1e40af` (use dashed stroke) |
|
||||
| Error | `#fecaca` | `#b91c1c` |
|
||||
|
||||
**Rule**: Always pair a darker stroke with a lighter fill for contrast.
|
||||
|
||||
---
|
||||
|
||||
## Text Colors (Hierarchy)
|
||||
|
||||
Use color on free-floating text to create visual hierarchy without containers.
|
||||
|
||||
| Level | Color | Use For |
|
||||
|-------|-------|---------|
|
||||
| Title | `#1e40af` | Section headings, major labels |
|
||||
| Subtitle | `#3b82f6` | Subheadings, secondary labels |
|
||||
| Body/Detail | `#64748b` | Descriptions, annotations, metadata |
|
||||
| On light fills | `#374151` | Text inside light-colored shapes |
|
||||
| On dark fills | `#ffffff` | Text inside dark-colored shapes |
|
||||
|
||||
---
|
||||
|
||||
## Evidence Artifact Colors
|
||||
|
||||
Used for code snippets, data examples, and other concrete evidence inside technical diagrams.
|
||||
|
||||
| Artifact | Background | Text Color |
|
||||
|----------|-----------|------------|
|
||||
| Code snippet | `#1e293b` | Syntax-colored (language-appropriate) |
|
||||
| JSON/data example | `#1e293b` | `#22c55e` (green) |
|
||||
|
||||
---
|
||||
|
||||
## Default Stroke & Line Colors
|
||||
|
||||
| Element | Color |
|
||||
|---------|-------|
|
||||
| Arrows | Use the stroke color of the source element's semantic purpose |
|
||||
| Structural lines (dividers, trees, timelines) | Primary stroke (`#1e3a5f`) or Slate (`#64748b`) |
|
||||
| Marker dots (fill + stroke) | Primary fill (`#3b82f6`) |
|
||||
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Canvas background | `#ffffff` |
|
||||
@@ -1,91 +0,0 @@
|
||||
# Color Palettes Reference
|
||||
|
||||
Color schemes for different platforms and component types.
|
||||
|
||||
---
|
||||
|
||||
## Default Palette (Platform-Agnostic)
|
||||
|
||||
| Component Type | Background | Stroke | Example |
|
||||
|----------------|------------|--------|---------|
|
||||
| Frontend/UI | `#a5d8ff` | `#1971c2` | Next.js, React apps |
|
||||
| Backend/API | `#d0bfff` | `#7048e8` | API servers, processors |
|
||||
| Database | `#b2f2bb` | `#2f9e44` | PostgreSQL, MySQL, MongoDB |
|
||||
| Storage | `#ffec99` | `#f08c00` | Object storage, file systems |
|
||||
| AI/ML Services | `#e599f7` | `#9c36b5` | ML models, AI APIs |
|
||||
| External APIs | `#ffc9c9` | `#e03131` | Third-party services |
|
||||
| Orchestration | `#ffa8a8` | `#c92a2a` | Workflows, schedulers |
|
||||
| Validation | `#ffd8a8` | `#e8590c` | Validators, checkers |
|
||||
| Network/Security | `#dee2e6` | `#495057` | VPC, IAM, firewalls |
|
||||
| Classification | `#99e9f2` | `#0c8599` | Routers, classifiers |
|
||||
| Users/Actors | `#e7f5ff` | `#1971c2` | User ellipses |
|
||||
| Message Queue | `#fff3bf` | `#fab005` | Kafka, RabbitMQ, SQS |
|
||||
| Cache | `#ffe8cc` | `#fd7e14` | Redis, Memcached |
|
||||
| Monitoring | `#d3f9d8` | `#40c057` | Prometheus, Grafana |
|
||||
|
||||
---
|
||||
|
||||
## AWS Palette
|
||||
|
||||
| Service Category | Background | Stroke |
|
||||
|-----------------|------------|--------|
|
||||
| Compute (EC2, Lambda, ECS) | `#ff9900` | `#cc7a00` |
|
||||
| Storage (S3, EBS) | `#3f8624` | `#2d6119` |
|
||||
| Database (RDS, DynamoDB) | `#3b48cc` | `#2d3899` |
|
||||
| Networking (VPC, Route53) | `#8c4fff` | `#6b3dcc` |
|
||||
| Security (IAM, KMS) | `#dd344c` | `#b12a3d` |
|
||||
| Analytics (Kinesis, Athena) | `#8c4fff` | `#6b3dcc` |
|
||||
| ML (SageMaker, Bedrock) | `#01a88d` | `#017d69` |
|
||||
|
||||
---
|
||||
|
||||
## Azure Palette
|
||||
|
||||
| Service Category | Background | Stroke |
|
||||
|-----------------|------------|--------|
|
||||
| Compute | `#0078d4` | `#005a9e` |
|
||||
| Storage | `#50e6ff` | `#3cb5cc` |
|
||||
| Database | `#0078d4` | `#005a9e` |
|
||||
| Networking | `#773adc` | `#5a2ca8` |
|
||||
| Security | `#ff8c00` | `#cc7000` |
|
||||
| AI/ML | `#50e6ff` | `#3cb5cc` |
|
||||
|
||||
---
|
||||
|
||||
## GCP Palette
|
||||
|
||||
| Service Category | Background | Stroke |
|
||||
|-----------------|------------|--------|
|
||||
| Compute (GCE, Cloud Run) | `#4285f4` | `#3367d6` |
|
||||
| Storage (GCS) | `#34a853` | `#2d8e47` |
|
||||
| Database (Cloud SQL, Firestore) | `#ea4335` | `#c53929` |
|
||||
| Networking | `#fbbc04` | `#d99e04` |
|
||||
| AI/ML (Vertex AI) | `#9334e6` | `#7627b8` |
|
||||
|
||||
---
|
||||
|
||||
## Kubernetes Palette
|
||||
|
||||
| Component | Background | Stroke |
|
||||
|-----------|------------|--------|
|
||||
| Pod | `#326ce5` | `#2756b8` |
|
||||
| Service | `#326ce5` | `#2756b8` |
|
||||
| Deployment | `#326ce5` | `#2756b8` |
|
||||
| ConfigMap/Secret | `#7f8c8d` | `#626d6e` |
|
||||
| Ingress | `#00d4aa` | `#00a888` |
|
||||
| Node | `#303030` | `#1a1a1a` |
|
||||
| Namespace | `#f0f0f0` | `#c0c0c0` (dashed) |
|
||||
|
||||
---
|
||||
|
||||
## Diagram Type Suggestions
|
||||
|
||||
| Diagram Type | Recommended Layout | Key Elements |
|
||||
|--------------|-------------------|--------------|
|
||||
| Microservices | Vertical flow | Services, databases, queues, API gateway |
|
||||
| Data Pipeline | Horizontal flow | Sources, transformers, sinks, storage |
|
||||
| Event-Driven | Hub-and-spoke | Event bus center, producers/consumers |
|
||||
| Kubernetes | Layered groups | Namespace boxes, pods inside deployments |
|
||||
| CI/CD | Horizontal flow | Source -> Build -> Test -> Deploy -> Monitor |
|
||||
| Network | Hierarchical | Internet -> LB -> VPC -> Subnets -> Instances |
|
||||
| User Flow | Swimlanes | User actions, system responses, external calls |
|
||||
182
skills/excalidraw/references/element-templates.md
Normal file
182
skills/excalidraw/references/element-templates.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Element Templates
|
||||
|
||||
Copy-paste JSON templates for each Excalidraw element type. The `strokeColor` and `backgroundColor` values are placeholders — always pull actual colors from `color-palette.md` based on the element's semantic purpose.
|
||||
|
||||
## Free-Floating Text (no container)
|
||||
```json
|
||||
{
|
||||
"type": "text",
|
||||
"id": "label1",
|
||||
"x": 100, "y": 100,
|
||||
"width": 200, "height": 25,
|
||||
"text": "Section Title",
|
||||
"originalText": "Section Title",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"strokeColor": "<title color from palette>",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 11111,
|
||||
"version": 1,
|
||||
"versionNonce": 22222,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": null,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": null,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
```
|
||||
|
||||
## Line (structural, not arrow)
|
||||
```json
|
||||
{
|
||||
"type": "line",
|
||||
"id": "line1",
|
||||
"x": 100, "y": 100,
|
||||
"width": 0, "height": 200,
|
||||
"strokeColor": "<structural line color from palette>",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 44444,
|
||||
"version": 1,
|
||||
"versionNonce": 55555,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": null,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [[0, 0], [0, 200]]
|
||||
}
|
||||
```
|
||||
|
||||
## Small Marker Dot
|
||||
```json
|
||||
{
|
||||
"type": "ellipse",
|
||||
"id": "dot1",
|
||||
"x": 94, "y": 94,
|
||||
"width": 12, "height": 12,
|
||||
"strokeColor": "<marker dot color from palette>",
|
||||
"backgroundColor": "<marker dot color from palette>",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 66666,
|
||||
"version": 1,
|
||||
"versionNonce": 77777,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": null,
|
||||
"link": null,
|
||||
"locked": false
|
||||
}
|
||||
```
|
||||
|
||||
## Rectangle
|
||||
```json
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "elem1",
|
||||
"x": 100, "y": 100, "width": 180, "height": 90,
|
||||
"strokeColor": "<stroke from palette based on semantic purpose>",
|
||||
"backgroundColor": "<fill from palette based on semantic purpose>",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 12345,
|
||||
"version": 1,
|
||||
"versionNonce": 67890,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": [{"id": "text1", "type": "text"}],
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"roundness": {"type": 3}
|
||||
}
|
||||
```
|
||||
|
||||
## Text (centered in shape)
|
||||
```json
|
||||
{
|
||||
"type": "text",
|
||||
"id": "text1",
|
||||
"x": 130, "y": 132,
|
||||
"width": 120, "height": 25,
|
||||
"text": "Process",
|
||||
"originalText": "Process",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 3,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "<text color — match parent shape's stroke or use 'on light/dark fills' from palette>",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 11111,
|
||||
"version": 1,
|
||||
"versionNonce": 22222,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": null,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"containerId": "elem1",
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
```
|
||||
|
||||
## Arrow
|
||||
```json
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "arrow1",
|
||||
"x": 282, "y": 145, "width": 118, "height": 0,
|
||||
"strokeColor": "<arrow color — typically matches source element's stroke from palette>",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"seed": 33333,
|
||||
"version": 1,
|
||||
"versionNonce": 44444,
|
||||
"isDeleted": false,
|
||||
"groupIds": [],
|
||||
"boundElements": null,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [[0, 0], [118, 0]],
|
||||
"startBinding": {"elementId": "elem1", "focus": 0, "gap": 2},
|
||||
"endBinding": {"elementId": "elem2", "focus": 0, "gap": 2},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
}
|
||||
```
|
||||
|
||||
For curves: use 3+ points in `points` array.
|
||||
@@ -1,381 +0,0 @@
|
||||
# Complete Examples Reference
|
||||
|
||||
Full JSON examples showing proper element structure.
|
||||
|
||||
---
|
||||
|
||||
## 3-Tier Architecture Example
|
||||
|
||||
This is a REFERENCE showing JSON structure. Replace IDs, labels, positions, and colors based on discovered components.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "claude-code-excalidraw-skill",
|
||||
"elements": [
|
||||
{
|
||||
"id": "user",
|
||||
"type": "ellipse",
|
||||
"x": 150,
|
||||
"y": 50,
|
||||
"width": 100,
|
||||
"height": 60,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1971c2",
|
||||
"backgroundColor": "#e7f5ff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": { "type": 2 },
|
||||
"seed": 1,
|
||||
"version": 1,
|
||||
"versionNonce": 1,
|
||||
"isDeleted": false,
|
||||
"boundElements": [{ "type": "text", "id": "user-text" }],
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "user-text",
|
||||
"type": "text",
|
||||
"x": 175,
|
||||
"y": 67,
|
||||
"width": 50,
|
||||
"height": 25,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"seed": 2,
|
||||
"version": 1,
|
||||
"versionNonce": 2,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "User",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 1,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"baseline": 14,
|
||||
"containerId": "user",
|
||||
"originalText": "User",
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "frontend",
|
||||
"type": "rectangle",
|
||||
"x": 100,
|
||||
"y": 180,
|
||||
"width": 200,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1971c2",
|
||||
"backgroundColor": "#a5d8ff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": { "type": 3 },
|
||||
"seed": 3,
|
||||
"version": 1,
|
||||
"versionNonce": 3,
|
||||
"isDeleted": false,
|
||||
"boundElements": [{ "type": "text", "id": "frontend-text" }],
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "frontend-text",
|
||||
"type": "text",
|
||||
"x": 105,
|
||||
"y": 195,
|
||||
"width": 190,
|
||||
"height": 50,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"seed": 4,
|
||||
"version": 1,
|
||||
"versionNonce": 4,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Frontend\nNext.js",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 1,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"baseline": 14,
|
||||
"containerId": "frontend",
|
||||
"originalText": "Frontend\nNext.js",
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "database",
|
||||
"type": "rectangle",
|
||||
"x": 100,
|
||||
"y": 330,
|
||||
"width": 200,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#2f9e44",
|
||||
"backgroundColor": "#b2f2bb",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": { "type": 3 },
|
||||
"seed": 5,
|
||||
"version": 1,
|
||||
"versionNonce": 5,
|
||||
"isDeleted": false,
|
||||
"boundElements": [{ "type": "text", "id": "database-text" }],
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"id": "database-text",
|
||||
"type": "text",
|
||||
"x": 105,
|
||||
"y": 345,
|
||||
"width": 190,
|
||||
"height": 50,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"seed": 6,
|
||||
"version": 1,
|
||||
"versionNonce": 6,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"text": "Database\nPostgreSQL",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 1,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"baseline": 14,
|
||||
"containerId": "database",
|
||||
"originalText": "Database\nPostgreSQL",
|
||||
"lineHeight": 1.25
|
||||
},
|
||||
{
|
||||
"id": "arrow-user-frontend",
|
||||
"type": "arrow",
|
||||
"x": 200,
|
||||
"y": 115,
|
||||
"width": 0,
|
||||
"height": 60,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1971c2",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"seed": 7,
|
||||
"version": 1,
|
||||
"versionNonce": 7,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [[0, 0], [0, 60]],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": null,
|
||||
"endBinding": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
},
|
||||
{
|
||||
"id": "arrow-frontend-database",
|
||||
"type": "arrow",
|
||||
"x": 200,
|
||||
"y": 265,
|
||||
"width": 0,
|
||||
"height": 60,
|
||||
"angle": 0,
|
||||
"strokeColor": "#2f9e44",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 0,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": null,
|
||||
"seed": 8,
|
||||
"version": 1,
|
||||
"versionNonce": 8,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false,
|
||||
"points": [[0, 0], [0, 60]],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": null,
|
||||
"endBinding": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"elbowed": true
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": 20,
|
||||
"viewBackgroundColor": "#ffffff"
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layout Patterns
|
||||
|
||||
### Vertical Flow (Most Common)
|
||||
|
||||
```
|
||||
Grid positioning:
|
||||
- Column width: 200-250px
|
||||
- Row height: 130-150px
|
||||
- Element size: 160-200px x 80-90px
|
||||
- Spacing: 40-50px between elements
|
||||
|
||||
Row positions (y):
|
||||
Row 0: 20 (title)
|
||||
Row 1: 100 (users/entry points)
|
||||
Row 2: 230 (frontend/gateway)
|
||||
Row 3: 380 (orchestration)
|
||||
Row 4: 530 (services)
|
||||
Row 5: 680 (data layer)
|
||||
Row 6: 830 (external services)
|
||||
|
||||
Column positions (x):
|
||||
Col 0: 100
|
||||
Col 1: 300
|
||||
Col 2: 500
|
||||
Col 3: 700
|
||||
Col 4: 900
|
||||
```
|
||||
|
||||
### Horizontal Flow (Pipelines)
|
||||
|
||||
```
|
||||
Stage positions (x):
|
||||
Stage 0: 100 (input/source)
|
||||
Stage 1: 350 (transform 1)
|
||||
Stage 2: 600 (transform 2)
|
||||
Stage 3: 850 (transform 3)
|
||||
Stage 4: 1100 (output/sink)
|
||||
|
||||
All stages at same y: 200
|
||||
Arrows: "right" -> "left" connections
|
||||
```
|
||||
|
||||
### Hub-and-Spoke
|
||||
|
||||
```
|
||||
Center hub: x=500, y=350
|
||||
8 positions at 45° increments:
|
||||
N: (500, 150)
|
||||
NE: (640, 210)
|
||||
E: (700, 350)
|
||||
SE: (640, 490)
|
||||
S: (500, 550)
|
||||
SW: (360, 490)
|
||||
W: (300, 350)
|
||||
NW: (360, 210)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complex Architecture Layout
|
||||
|
||||
```
|
||||
Row 0: Title/Header (y: 20)
|
||||
Row 1: Users/Clients (y: 80)
|
||||
Row 2: Frontend/Gateway (y: 200)
|
||||
Row 3: Orchestration (y: 350)
|
||||
Row 4: Processing Services (y: 550)
|
||||
Row 5: Data Layer (y: 680)
|
||||
Row 6: External Services (y: 830)
|
||||
|
||||
Columns (x):
|
||||
Col 0: 120
|
||||
Col 1: 320
|
||||
Col 2: 520
|
||||
Col 3: 720
|
||||
Col 4: 920
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Diagram Complexity Guidelines
|
||||
|
||||
| Complexity | Max Elements | Max Arrows | Approach |
|
||||
|------------|-------------|------------|----------|
|
||||
| Simple | 5-10 | 5-10 | Single file, no groups |
|
||||
| Medium | 10-25 | 15-30 | Use grouping rectangles |
|
||||
| Complex | 25-50 | 30-60 | Split into multiple diagrams |
|
||||
| Very Complex | 50+ | 60+ | Multiple focused diagrams |
|
||||
|
||||
**When to split:**
|
||||
- More than 50 elements
|
||||
- Create: `architecture-overview.excalidraw`, `architecture-data-layer.excalidraw`
|
||||
|
||||
**When to use groups:**
|
||||
- 3+ related services
|
||||
- Same deployment unit
|
||||
- Logical boundaries (VPC, Security Zone)
|
||||
@@ -1,210 +0,0 @@
|
||||
# Excalidraw JSON Format Reference
|
||||
|
||||
Complete reference for Excalidraw JSON structure and element types.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "claude-code-excalidraw-skill",
|
||||
"elements": [],
|
||||
"appState": {
|
||||
"gridSize": 20,
|
||||
"viewBackgroundColor": "#ffffff"
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Element Types
|
||||
|
||||
| Type | Use For | Arrow Reliability |
|
||||
|------|---------|-------------------|
|
||||
| `rectangle` | Services, components, databases, containers, orchestrators, decision points | Excellent |
|
||||
| `ellipse` | Users, external systems, start/end points | Good |
|
||||
| `text` | Labels inside shapes, titles, annotations | N/A |
|
||||
| `arrow` | Data flow, connections, dependencies | N/A |
|
||||
| `line` | Grouping boundaries, separators | N/A |
|
||||
|
||||
### BANNED: Diamond Shapes
|
||||
|
||||
**NEVER use `type: "diamond"` in generated diagrams.**
|
||||
|
||||
Diamond arrow connections are fundamentally broken in raw Excalidraw JSON:
|
||||
- Excalidraw applies `roundness` to diamond vertices during rendering
|
||||
- Visual edges appear offset from mathematical edge points
|
||||
- No offset formula reliably compensates
|
||||
- Arrows appear disconnected/floating
|
||||
|
||||
**Use styled rectangles instead** for visual distinction:
|
||||
|
||||
| Semantic Meaning | Rectangle Style |
|
||||
|------------------|-----------------|
|
||||
| Orchestrator/Hub | Coral (`#ffa8a8`/`#c92a2a`) + strokeWidth: 3 |
|
||||
| Decision Point | Orange (`#ffd8a8`/`#e8590c`) + dashed stroke |
|
||||
| Central Router | Larger size + bold color |
|
||||
|
||||
---
|
||||
|
||||
## Required Element Properties
|
||||
|
||||
Every element MUST have these properties:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-id-string",
|
||||
"type": "rectangle",
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"width": 200,
|
||||
"height": 80,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1971c2",
|
||||
"backgroundColor": "#a5d8ff",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"frameId": null,
|
||||
"roundness": { "type": 3 },
|
||||
"seed": 1,
|
||||
"version": 1,
|
||||
"versionNonce": 1,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Text Inside Shapes (Labels)
|
||||
|
||||
**Every labeled shape requires TWO elements:**
|
||||
|
||||
### Shape with boundElements
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "{component-id}",
|
||||
"type": "rectangle",
|
||||
"x": 500,
|
||||
"y": 200,
|
||||
"width": 200,
|
||||
"height": 90,
|
||||
"strokeColor": "#1971c2",
|
||||
"backgroundColor": "#a5d8ff",
|
||||
"boundElements": [{ "type": "text", "id": "{component-id}-text" }],
|
||||
// ... other required properties
|
||||
}
|
||||
```
|
||||
|
||||
### Text with containerId
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "{component-id}-text",
|
||||
"type": "text",
|
||||
"x": 505, // shape.x + 5
|
||||
"y": 220, // shape.y + (shape.height - text.height) / 2
|
||||
"width": 190, // shape.width - 10
|
||||
"height": 50,
|
||||
"text": "{Component Name}\n{Subtitle}",
|
||||
"fontSize": 16,
|
||||
"fontFamily": 1,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": "{component-id}",
|
||||
"originalText": "{Component Name}\n{Subtitle}",
|
||||
"lineHeight": 1.25,
|
||||
// ... other required properties
|
||||
}
|
||||
```
|
||||
|
||||
### DO NOT Use the `label` Property
|
||||
|
||||
The `label` property is for the JavaScript API, NOT raw JSON files:
|
||||
|
||||
```json
|
||||
// WRONG - will show empty boxes
|
||||
{ "type": "rectangle", "label": { "text": "My Label" } }
|
||||
|
||||
// CORRECT - requires TWO elements
|
||||
// 1. Shape with boundElements reference
|
||||
// 2. Separate text element with containerId
|
||||
```
|
||||
|
||||
### Text Positioning
|
||||
|
||||
- Text `x` = shape `x` + 5
|
||||
- Text `y` = shape `y` + (shape.height - text.height) / 2
|
||||
- Text `width` = shape `width` - 10
|
||||
- Use `\n` for multi-line labels
|
||||
- Always use `textAlign: "center"` and `verticalAlign: "middle"`
|
||||
|
||||
### ID Naming Convention
|
||||
|
||||
Always use pattern: `{shape-id}-text` for text element IDs.
|
||||
|
||||
---
|
||||
|
||||
## Dynamic ID Generation
|
||||
|
||||
IDs and labels are generated from codebase analysis:
|
||||
|
||||
| Discovered Component | Generated ID | Generated Label |
|
||||
|---------------------|--------------|-----------------|
|
||||
| Express API server | `express-api` | `"API Server\nExpress.js"` |
|
||||
| PostgreSQL database | `postgres-db` | `"PostgreSQL\nDatabase"` |
|
||||
| Redis cache | `redis-cache` | `"Redis\nCache Layer"` |
|
||||
| S3 bucket for uploads | `s3-uploads` | `"S3 Bucket\nuploads/"` |
|
||||
| Lambda function | `lambda-processor` | `"Lambda\nProcessor"` |
|
||||
| React frontend | `react-frontend` | `"React App\nFrontend"` |
|
||||
|
||||
---
|
||||
|
||||
## Grouping with Dashed Rectangles
|
||||
|
||||
For logical groupings (namespaces, VPCs, pipelines):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "group-ai-pipeline",
|
||||
"type": "rectangle",
|
||||
"x": 100,
|
||||
"y": 500,
|
||||
"width": 1000,
|
||||
"height": 280,
|
||||
"strokeColor": "#9c36b5",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeStyle": "dashed",
|
||||
"roughness": 0,
|
||||
"roundness": null,
|
||||
"boundElements": null
|
||||
}
|
||||
```
|
||||
|
||||
Group labels are standalone text (no containerId) at top-left:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "group-ai-pipeline-label",
|
||||
"type": "text",
|
||||
"x": 120,
|
||||
"y": 510,
|
||||
"text": "AI Processing Pipeline (Cloud Run)",
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"containerId": null
|
||||
}
|
||||
```
|
||||
71
skills/excalidraw/references/json-schema.md
Normal file
71
skills/excalidraw/references/json-schema.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Excalidraw JSON Schema
|
||||
|
||||
## Element Types
|
||||
|
||||
| Type | Use For |
|
||||
|------|---------|
|
||||
| `rectangle` | Processes, actions, components |
|
||||
| `ellipse` | Entry/exit points, external systems |
|
||||
| `diamond` | Decisions, conditionals |
|
||||
| `arrow` | Connections between shapes |
|
||||
| `text` | Labels inside shapes |
|
||||
| `line` | Non-arrow connections |
|
||||
| `frame` | Grouping containers |
|
||||
|
||||
## Common Properties
|
||||
|
||||
All elements share these:
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `id` | string | Unique identifier |
|
||||
| `type` | string | Element type |
|
||||
| `x`, `y` | number | Position in pixels |
|
||||
| `width`, `height` | number | Size in pixels |
|
||||
| `strokeColor` | string | Border color (hex) |
|
||||
| `backgroundColor` | string | Fill color (hex or "transparent") |
|
||||
| `fillStyle` | string | "solid", "hachure", "cross-hatch" |
|
||||
| `strokeWidth` | number | 1, 2, or 4 |
|
||||
| `strokeStyle` | string | "solid", "dashed", "dotted" |
|
||||
| `roughness` | number | 0 (smooth), 1 (default), 2 (rough) |
|
||||
| `opacity` | number | 0-100 |
|
||||
| `seed` | number | Random seed for roughness |
|
||||
|
||||
## Text-Specific Properties
|
||||
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `text` | The display text |
|
||||
| `originalText` | Same as text |
|
||||
| `fontSize` | Size in pixels (16-20 recommended) |
|
||||
| `fontFamily` | 3 for monospace (use this) |
|
||||
| `textAlign` | "left", "center", "right" |
|
||||
| `verticalAlign` | "top", "middle", "bottom" |
|
||||
| `containerId` | ID of parent shape |
|
||||
|
||||
## Arrow-Specific Properties
|
||||
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `points` | Array of [x, y] coordinates |
|
||||
| `startBinding` | Connection to start shape |
|
||||
| `endBinding` | Connection to end shape |
|
||||
| `startArrowhead` | null, "arrow", "bar", "dot", "triangle" |
|
||||
| `endArrowhead` | null, "arrow", "bar", "dot", "triangle" |
|
||||
|
||||
## Binding Format
|
||||
|
||||
```json
|
||||
{
|
||||
"elementId": "shapeId",
|
||||
"focus": 0,
|
||||
"gap": 2
|
||||
}
|
||||
```
|
||||
|
||||
## Rectangle Roundness
|
||||
|
||||
Add for rounded corners:
|
||||
```json
|
||||
"roundness": { "type": 3 }
|
||||
```
|
||||
205
skills/excalidraw/references/render_excalidraw.py
Normal file
205
skills/excalidraw/references/render_excalidraw.py
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render Excalidraw JSON to PNG using Playwright + headless Chromium.
|
||||
|
||||
Usage:
|
||||
python3 render_excalidraw.py <path-to-file.excalidraw> [--output path.png] [--scale 2] [--width 1920]
|
||||
|
||||
Dependencies (playwright, chromium) are provided by the Nix flake / direnv environment.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def validate_excalidraw(data: dict) -> list[str]:
|
||||
"""Validate Excalidraw JSON structure. Returns list of errors (empty = valid)."""
|
||||
errors: list[str] = []
|
||||
|
||||
if data.get("type") != "excalidraw":
|
||||
errors.append(f"Expected type 'excalidraw', got '{data.get('type')}'")
|
||||
|
||||
if "elements" not in data:
|
||||
errors.append("Missing 'elements' array")
|
||||
elif not isinstance(data["elements"], list):
|
||||
errors.append("'elements' must be an array")
|
||||
elif len(data["elements"]) == 0:
|
||||
errors.append("'elements' array is empty — nothing to render")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def compute_bounding_box(elements: list[dict]) -> tuple[float, float, float, float]:
|
||||
"""Compute bounding box (min_x, min_y, max_x, max_y) across all elements."""
|
||||
min_x = float("inf")
|
||||
min_y = float("inf")
|
||||
max_x = float("-inf")
|
||||
max_y = float("-inf")
|
||||
|
||||
for el in elements:
|
||||
if el.get("isDeleted"):
|
||||
continue
|
||||
x = el.get("x", 0)
|
||||
y = el.get("y", 0)
|
||||
w = el.get("width", 0)
|
||||
h = el.get("height", 0)
|
||||
|
||||
# For arrows/lines, points array defines the shape relative to x,y
|
||||
if el.get("type") in ("arrow", "line") and "points" in el:
|
||||
for px, py in el["points"]:
|
||||
min_x = min(min_x, x + px)
|
||||
min_y = min(min_y, y + py)
|
||||
max_x = max(max_x, x + px)
|
||||
max_y = max(max_y, y + py)
|
||||
else:
|
||||
min_x = min(min_x, x)
|
||||
min_y = min(min_y, y)
|
||||
max_x = max(max_x, x + abs(w))
|
||||
max_y = max(max_y, y + abs(h))
|
||||
|
||||
if min_x == float("inf"):
|
||||
return (0, 0, 800, 600)
|
||||
|
||||
return (min_x, min_y, max_x, max_y)
|
||||
|
||||
|
||||
def render(
|
||||
excalidraw_path: Path,
|
||||
output_path: Path | None = None,
|
||||
scale: int = 2,
|
||||
max_width: int = 1920,
|
||||
) -> Path:
|
||||
"""Render an .excalidraw file to PNG. Returns the output PNG path."""
|
||||
# Import playwright here so validation errors show before import errors
|
||||
try:
|
||||
from playwright.sync_api import sync_playwright
|
||||
except ImportError:
|
||||
print("ERROR: playwright not installed.", file=sys.stderr)
|
||||
print("Ensure the Nix dev shell is active (direnv allow).", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Read and validate
|
||||
raw = excalidraw_path.read_text(encoding="utf-8")
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"ERROR: Invalid JSON in {excalidraw_path}: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
errors = validate_excalidraw(data)
|
||||
if errors:
|
||||
print(f"ERROR: Invalid Excalidraw file:", file=sys.stderr)
|
||||
for err in errors:
|
||||
print(f" - {err}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Compute viewport size from element bounding box
|
||||
elements = [e for e in data["elements"] if not e.get("isDeleted")]
|
||||
min_x, min_y, max_x, max_y = compute_bounding_box(elements)
|
||||
padding = 80
|
||||
diagram_w = max_x - min_x + padding * 2
|
||||
diagram_h = max_y - min_y + padding * 2
|
||||
|
||||
# Cap viewport width, let height be natural
|
||||
vp_width = min(int(diagram_w), max_width)
|
||||
vp_height = max(int(diagram_h), 600)
|
||||
|
||||
# Output path
|
||||
if output_path is None:
|
||||
output_path = excalidraw_path.with_suffix(".png")
|
||||
|
||||
# Template path (same directory as this script)
|
||||
template_path = Path(__file__).parent / "render_template.html"
|
||||
if not template_path.exists():
|
||||
print(f"ERROR: Template not found at {template_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
template_url = template_path.as_uri()
|
||||
|
||||
with sync_playwright() as p:
|
||||
try:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
except Exception as e:
|
||||
if "Executable doesn't exist" in str(e) or "browserType.launch" in str(e):
|
||||
print("ERROR: Chromium not installed for Playwright.", file=sys.stderr)
|
||||
print("Ensure the Nix dev shell is active (direnv allow).", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
raise
|
||||
|
||||
page = browser.new_page(
|
||||
viewport={"width": vp_width, "height": vp_height},
|
||||
device_scale_factor=scale,
|
||||
)
|
||||
|
||||
# Load the template
|
||||
page.goto(template_url)
|
||||
|
||||
# Wait for the ES module to load (imports from esm.sh)
|
||||
page.wait_for_function("window.__moduleReady === true", timeout=30000)
|
||||
|
||||
# Inject the diagram data and render
|
||||
json_str = json.dumps(data)
|
||||
result = page.evaluate(f"window.renderDiagram({json_str})")
|
||||
|
||||
if not result or not result.get("success"):
|
||||
error_msg = (
|
||||
result.get("error", "Unknown render error")
|
||||
if result
|
||||
else "renderDiagram returned null"
|
||||
)
|
||||
print(f"ERROR: Render failed: {error_msg}", file=sys.stderr)
|
||||
browser.close()
|
||||
sys.exit(1)
|
||||
|
||||
# Wait for render completion signal
|
||||
page.wait_for_function("window.__renderComplete === true", timeout=15000)
|
||||
|
||||
# Screenshot the SVG element
|
||||
svg_el = page.query_selector("#root svg")
|
||||
if svg_el is None:
|
||||
print("ERROR: No SVG element found after render.", file=sys.stderr)
|
||||
browser.close()
|
||||
sys.exit(1)
|
||||
|
||||
svg_el.screenshot(path=str(output_path))
|
||||
browser.close()
|
||||
|
||||
return output_path
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Entry point for rendering Excalidraw JSON files to PNG."""
|
||||
parser = argparse.ArgumentParser(description="Render Excalidraw JSON to PNG")
|
||||
parser.add_argument("input", type=Path, help="Path to .excalidraw JSON file")
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
"-o",
|
||||
type=Path,
|
||||
default=None,
|
||||
help="Output PNG path (default: same name with .png)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--scale", "-s", type=int, default=2, help="Device scale factor (default: 2)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--width",
|
||||
"-w",
|
||||
type=int,
|
||||
default=1920,
|
||||
help="Max viewport width (default: 1920)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.input.exists():
|
||||
print(f"ERROR: File not found: {args.input}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
png_path = render(args.input, args.output, args.scale, args.width)
|
||||
print(str(png_path))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
57
skills/excalidraw/references/render_template.html
Normal file
57
skills/excalidraw/references/render_template.html
Normal file
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { background: #ffffff; overflow: hidden; }
|
||||
#root { display: inline-block; }
|
||||
#root svg { display: block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
<script type="module">
|
||||
import { exportToSvg } from "https://esm.sh/@excalidraw/excalidraw?bundle";
|
||||
|
||||
window.renderDiagram = async function(jsonData) {
|
||||
try {
|
||||
const data = typeof jsonData === "string" ? JSON.parse(jsonData) : jsonData;
|
||||
const elements = data.elements || [];
|
||||
const appState = data.appState || {};
|
||||
const files = data.files || {};
|
||||
|
||||
// Force white background in appState
|
||||
appState.viewBackgroundColor = appState.viewBackgroundColor || "#ffffff";
|
||||
appState.exportWithDarkMode = false;
|
||||
|
||||
const svg = await exportToSvg({
|
||||
elements: elements,
|
||||
appState: {
|
||||
...appState,
|
||||
exportBackground: true,
|
||||
},
|
||||
files: files,
|
||||
});
|
||||
|
||||
// Clear any previous render
|
||||
const root = document.getElementById("root");
|
||||
root.innerHTML = "";
|
||||
root.appendChild(svg);
|
||||
|
||||
window.__renderComplete = true;
|
||||
window.__renderError = null;
|
||||
return { success: true, width: svg.getAttribute("width"), height: svg.getAttribute("height") };
|
||||
} catch (err) {
|
||||
window.__renderComplete = true;
|
||||
window.__renderError = err.message;
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
};
|
||||
|
||||
// Signal that the module is loaded and ready
|
||||
window.__moduleReady = true;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,182 +0,0 @@
|
||||
# Validation Reference
|
||||
|
||||
Checklists, validation algorithms, and common bug fixes.
|
||||
|
||||
---
|
||||
|
||||
## Pre-Flight Validation Algorithm
|
||||
|
||||
Run BEFORE writing the file:
|
||||
|
||||
```
|
||||
FUNCTION validateDiagram(elements):
|
||||
errors = []
|
||||
|
||||
// 1. Validate shape-text bindings
|
||||
FOR each shape IN elements WHERE shape.boundElements != null:
|
||||
FOR each binding IN shape.boundElements:
|
||||
textElement = findById(elements, binding.id)
|
||||
IF textElement == null:
|
||||
errors.append("Shape {shape.id} references missing text {binding.id}")
|
||||
ELSE IF textElement.containerId != shape.id:
|
||||
errors.append("Text containerId doesn't match shape")
|
||||
|
||||
// 2. Validate arrow connections
|
||||
FOR each arrow IN elements WHERE arrow.type == "arrow":
|
||||
sourceShape = findShapeNear(elements, arrow.x, arrow.y)
|
||||
IF sourceShape == null:
|
||||
errors.append("Arrow {arrow.id} doesn't start from shape edge")
|
||||
|
||||
finalPoint = arrow.points[arrow.points.length - 1]
|
||||
endX = arrow.x + finalPoint[0]
|
||||
endY = arrow.y + finalPoint[1]
|
||||
targetShape = findShapeNear(elements, endX, endY)
|
||||
IF targetShape == null:
|
||||
errors.append("Arrow {arrow.id} doesn't end at shape edge")
|
||||
|
||||
IF arrow.points.length > 2:
|
||||
IF arrow.elbowed != true:
|
||||
errors.append("Arrow {arrow.id} missing elbowed:true")
|
||||
IF arrow.roundness != null:
|
||||
errors.append("Arrow {arrow.id} should have roundness:null")
|
||||
|
||||
// 3. Validate unique IDs
|
||||
ids = [el.id for el in elements]
|
||||
duplicates = findDuplicates(ids)
|
||||
IF duplicates.length > 0:
|
||||
errors.append("Duplicate IDs: {duplicates}")
|
||||
|
||||
// 4. Validate bounding boxes
|
||||
FOR each arrow IN elements WHERE arrow.type == "arrow":
|
||||
maxX = max(abs(p[0]) for p in arrow.points)
|
||||
maxY = max(abs(p[1]) for p in arrow.points)
|
||||
IF arrow.width < maxX OR arrow.height < maxY:
|
||||
errors.append("Arrow {arrow.id} bounding box too small")
|
||||
|
||||
RETURN errors
|
||||
|
||||
FUNCTION findShapeNear(elements, x, y, tolerance=15):
|
||||
FOR each shape IN elements WHERE shape.type IN ["rectangle", "ellipse"]:
|
||||
edges = [
|
||||
(shape.x + shape.width/2, shape.y), // top
|
||||
(shape.x + shape.width/2, shape.y + shape.height), // bottom
|
||||
(shape.x, shape.y + shape.height/2), // left
|
||||
(shape.x + shape.width, shape.y + shape.height/2) // right
|
||||
]
|
||||
FOR each edge IN edges:
|
||||
IF abs(edge.x - x) < tolerance AND abs(edge.y - y) < tolerance:
|
||||
RETURN shape
|
||||
RETURN null
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklists
|
||||
|
||||
### Before Generating
|
||||
|
||||
- [ ] Identified all components from codebase
|
||||
- [ ] Mapped all connections/data flows
|
||||
- [ ] Chose layout pattern (vertical, horizontal, hub-and-spoke)
|
||||
- [ ] Selected color palette (default, AWS, Azure, K8s)
|
||||
- [ ] Planned grid positions
|
||||
- [ ] Created ID naming scheme
|
||||
|
||||
### During Generation
|
||||
|
||||
- [ ] Every labeled shape has BOTH shape AND text elements
|
||||
- [ ] Shape has `boundElements: [{ "type": "text", "id": "{id}-text" }]`
|
||||
- [ ] Text has `containerId: "{shape-id}"`
|
||||
- [ ] Multi-point arrows have `elbowed: true`, `roundness: null`, `roughness: 0`
|
||||
- [ ] Arrows have `startBinding` and `endBinding`
|
||||
- [ ] No diamond shapes used
|
||||
- [ ] Applied staggering formula for multiple arrows
|
||||
|
||||
### Arrow Validation (Every Arrow)
|
||||
|
||||
- [ ] Arrow `x,y` calculated from shape edge
|
||||
- [ ] Final point offset = `targetEdge - sourceEdge`
|
||||
- [ ] Arrow `width` = `max(abs(point[0]))`
|
||||
- [ ] Arrow `height` = `max(abs(point[1]))`
|
||||
- [ ] U-turn arrows have 40-60px clearance
|
||||
|
||||
### After Generation
|
||||
|
||||
- [ ] All `boundElements` IDs reference valid text elements
|
||||
- [ ] All `containerId` values reference valid shapes
|
||||
- [ ] All arrows start within 15px of shape edge
|
||||
- [ ] All arrows end within 15px of shape edge
|
||||
- [ ] No duplicate IDs
|
||||
- [ ] Arrow bounding boxes match points
|
||||
- [ ] File is valid JSON
|
||||
|
||||
---
|
||||
|
||||
## Common Bugs and Fixes
|
||||
|
||||
### Bug: Arrow appears disconnected/floating
|
||||
|
||||
**Cause**: Arrow `x,y` not calculated from shape edge.
|
||||
|
||||
**Fix**:
|
||||
```
|
||||
Rectangle bottom: arrow_x = shape.x + shape.width/2
|
||||
arrow_y = shape.y + shape.height
|
||||
```
|
||||
|
||||
### Bug: Arrow endpoint doesn't reach target
|
||||
|
||||
**Cause**: Final point offset calculated incorrectly.
|
||||
|
||||
**Fix**:
|
||||
```
|
||||
target_edge = (target.x + target.width/2, target.y)
|
||||
offset_x = target_edge.x - arrow.x
|
||||
offset_y = target_edge.y - arrow.y
|
||||
Final point = [offset_x, offset_y]
|
||||
```
|
||||
|
||||
### Bug: Multiple arrows from same source overlap
|
||||
|
||||
**Cause**: All arrows start from identical `x,y`.
|
||||
|
||||
**Fix**: Stagger start positions:
|
||||
```
|
||||
For 5 arrows from bottom edge:
|
||||
arrow1.x = shape.x + shape.width * 0.2
|
||||
arrow2.x = shape.x + shape.width * 0.35
|
||||
arrow3.x = shape.x + shape.width * 0.5
|
||||
arrow4.x = shape.x + shape.width * 0.65
|
||||
arrow5.x = shape.x + shape.width * 0.8
|
||||
```
|
||||
|
||||
### Bug: Callback arrow doesn't loop correctly
|
||||
|
||||
**Cause**: U-turn path lacks clearance.
|
||||
|
||||
**Fix**: Use 4-point path:
|
||||
```
|
||||
Points = [[0, 0], [clearance, 0], [clearance, -vert], [final_x, -vert]]
|
||||
clearance = 40-60px
|
||||
```
|
||||
|
||||
### Bug: Labels don't appear inside shapes
|
||||
|
||||
**Cause**: Using `label` property instead of separate text element.
|
||||
|
||||
**Fix**: Create TWO elements:
|
||||
1. Shape with `boundElements` referencing text
|
||||
2. Text with `containerId` referencing shape
|
||||
|
||||
### Bug: Arrows are curved, not 90-degree
|
||||
|
||||
**Cause**: Missing elbow properties.
|
||||
|
||||
**Fix**: Add all three:
|
||||
```json
|
||||
{
|
||||
"roughness": 0,
|
||||
"roundness": null,
|
||||
"elbowed": true
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user