Installation
Download and extract to your repository:
.github/skills/excalidraw-diagram-generator/ Extract the ZIP to .github/skills/ in your repo. The folder name must match excalidraw-diagram-generator for Copilot to auto-discover it.
Skill Files (15)
SKILL.md 23.1 KB
---
name: excalidraw-diagram-generator
description: 'Generate Excalidraw diagrams from natural language descriptions. Use when asked to "create a diagram", "make a flowchart", "visualize a process", "draw a system architecture", "create a mind map", or "generate an Excalidraw file". Supports flowcharts, relationship diagrams, mind maps, and system architecture diagrams. Outputs .excalidraw JSON files that can be opened directly in Excalidraw.'
---
# Excalidraw Diagram Generator
A skill for generating Excalidraw-format diagrams from natural language descriptions. This skill helps create visual representations of processes, systems, relationships, and ideas without manual drawing.
## When to Use This Skill
Use this skill when users request:
- "Create a diagram showing..."
- "Make a flowchart for..."
- "Visualize the process of..."
- "Draw the system architecture of..."
- "Generate a mind map about..."
- "Create an Excalidraw file for..."
- "Show the relationship between..."
- "Diagram the workflow of..."
**Supported diagram types:**
- 📊 **Flowcharts**: Sequential processes, workflows, decision trees
- 🔗 **Relationship Diagrams**: Entity relationships, system components, dependencies
- 🧠 **Mind Maps**: Concept hierarchies, brainstorming results, topic organization
- 🏗️ **Architecture Diagrams**: System design, module interactions, data flow
- 📈 **Data Flow Diagrams (DFD)**: Data flow visualization, data transformation processes
- 🏊 **Business Flow (Swimlane)**: Cross-functional workflows, actor-based process flows
- 📦 **Class Diagrams**: Object-oriented design, class structures and relationships
- 🔄 **Sequence Diagrams**: Object interactions over time, message flows
- 🗃️ **ER Diagrams**: Database entity relationships, data models
## Prerequisites
- Clear description of what should be visualized
- Identification of key entities, steps, or concepts
- Understanding of relationships or flow between elements
## Step-by-Step Workflow
### Step 1: Understand the Request
Analyze the user's description to determine:
1. **Diagram type** (flowchart, relationship, mind map, architecture)
2. **Key elements** (entities, steps, concepts)
3. **Relationships** (flow, connections, hierarchy)
4. **Complexity** (number of elements)
### Step 2: Choose the Appropriate Diagram Type
| User Intent | Diagram Type | Example Keywords |
|-------------|--------------|------------------|
| Process flow, steps, procedures | **Flowchart** | "workflow", "process", "steps", "procedure" |
| Connections, dependencies, associations | **Relationship Diagram** | "relationship", "connections", "dependencies", "structure" |
| Concept hierarchy, brainstorming | **Mind Map** | "mind map", "concepts", "ideas", "breakdown" |
| System design, components | **Architecture Diagram** | "architecture", "system", "components", "modules" |
| Data flow, transformation processes | **Data Flow Diagram (DFD)** | "data flow", "data processing", "data transformation" |
| Cross-functional processes, actor responsibilities | **Business Flow (Swimlane)** | "business process", "swimlane", "actors", "responsibilities" |
| Object-oriented design, class structures | **Class Diagram** | "class", "inheritance", "OOP", "object model" |
| Interaction sequences, message flows | **Sequence Diagram** | "sequence", "interaction", "messages", "timeline" |
| Database design, entity relationships | **ER Diagram** | "database", "entity", "relationship", "data model" |
### Step 3: Extract Structured Information
**For Flowcharts:**
- List of sequential steps
- Decision points (if any)
- Start and end points
**For Relationship Diagrams:**
- Entities/nodes (name + optional description)
- Relationships between entities (from → to, with label)
**For Mind Maps:**
- Central topic
- Main branches (3-6 recommended)
- Sub-topics for each branch (optional)
**For Data Flow Diagrams (DFD):**
- Data sources and destinations (external entities)
- Processes (data transformations)
- Data stores (databases, files)
- Data flows (arrows showing data movement from left-to-right or from top-left to bottom-right)
- **Important**: Do not represent process order, only data flow
**For Business Flow (Swimlane):**
- Actors/roles (departments, systems, people) - displayed as header columns
- Process lanes (vertical lanes under each actor)
- Process boxes (activities within each lane)
- Flow arrows (connecting process boxes, including cross-lane handoffs)
**For Class Diagrams:**
- Classes with names
- Attributes with visibility (+, -, #)
- Methods with visibility and parameters
- Relationships: inheritance (solid line + white triangle), implementation (dashed line + white triangle), association (solid line), dependency (dashed line), aggregation (solid line + white diamond), composition (solid line + filled diamond)
- Multiplicity notations (1, 0..1, 1..*, *)
**For Sequence Diagrams:**
- Objects/actors (arranged horizontally at top)
- Lifelines (vertical lines from each object)
- Messages (horizontal arrows between lifelines)
- Synchronous messages (solid arrow), asynchronous messages (dashed arrow)
- Return values (dashed arrows)
- Activation boxes (rectangles on lifelines during execution)
- Time flows from top to bottom
**For ER Diagrams:**
- Entities (rectangles with entity names)
- Attributes (listed inside entities)
- Primary keys (underlined or marked with PK)
- Foreign keys (marked with FK)
- Relationships (lines connecting entities)
- Cardinality: 1:1 (one-to-one), 1:N (one-to-many), N:M (many-to-many)
- Junction/associative entities for many-to-many relationships (dashed rectangles)
### Step 4: Generate the Excalidraw JSON
Create the `.excalidraw` file with appropriate elements:
**Available element types:**
- `rectangle`: Boxes for entities, steps, concepts
- `ellipse`: Alternative shapes for emphasis
- `diamond`: Decision points
- `arrow`: Directional connections
- `text`: Labels and annotations
**Key properties to set:**
- **Position**: `x`, `y` coordinates
- **Size**: `width`, `height`
- **Style**: `strokeColor`, `backgroundColor`, `fillStyle`
- **Font**: `fontFamily: 5` (Excalifont - **required for all text elements**)
- **Text**: Embedded text for labels
- **Connections**: `points` array for arrows
**Important**: All text elements must use `fontFamily: 5` (Excalifont) for consistent visual appearance.
### Step 5: Format the Output
Structure the complete Excalidraw file:
```json
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
// Array of diagram elements
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}
```
### Step 6: Save and Provide Instructions
1. Save as `<descriptive-name>.excalidraw`
2. Inform user how to open:
- Visit https://excalidraw.com
- Click "Open" or drag-and-drop the file
- Or use Excalidraw VS Code extension
## Best Practices
### Element Count Guidelines
| Diagram Type | Recommended Count | Maximum |
|--------------|-------------------|---------|
| Flowchart steps | 3-10 | 15 |
| Relationship entities | 3-8 | 12 |
| Mind map branches | 4-6 | 8 |
| Mind map sub-topics per branch | 2-4 | 6 |
### Layout Tips
1. **Start positions**: Center important elements, use consistent spacing
2. **Spacing**:
- Horizontal gap: 200-300px between elements
- Vertical gap: 100-150px between rows
3. **Colors**: Use consistent color scheme
- Primary elements: Light blue (`#a5d8ff`)
- Secondary elements: Light green (`#b2f2bb`)
- Important/Central: Yellow (`#ffd43b`)
- Alerts/Warnings: Light red (`#ffc9c9`)
4. **Text sizing**: 16-24px for readability
5. **Font**: Always use `fontFamily: 5` (Excalifont) for all text elements
6. **Arrow style**: Use straight arrows for simple flows, curved for complex relationships
### Complexity Management
**If user request has too many elements:**
- Suggest breaking into multiple diagrams
- Focus on main elements first
- Offer to create detailed sub-diagrams
**Example response:**
```
"Your request includes 15 components. For clarity, I recommend:
1. High-level architecture diagram (6 main components)
2. Detailed diagram for each subsystem
Would you like me to start with the high-level view?"
```
## Example Prompts and Responses
### Example 1: Simple Flowchart
**User:** "Create a flowchart for user registration"
**Agent generates:**
1. Extract steps: "Enter email" → "Verify email" → "Set password" → "Complete"
2. Create flowchart with 4 rectangles + 3 arrows
3. Save as `user-registration-flow.excalidraw`
### Example 2: Relationship Diagram
**User:** "Diagram the relationship between User, Post, and Comment entities"
**Agent generates:**
1. Entities: User, Post, Comment
2. Relationships: User → Post ("creates"), User → Comment ("writes"), Post → Comment ("contains")
3. Save as `user-content-relationships.excalidraw`
### Example 3: Mind Map
**User:** "Mind map about machine learning concepts"
**Agent generates:**
1. Center: "Machine Learning"
2. Branches: Supervised Learning, Unsupervised Learning, Reinforcement Learning, Deep Learning
3. Sub-topics under each branch
4. Save as `machine-learning-mindmap.excalidraw`
## Troubleshooting
| Issue | Solution |
|-------|----------|
| Elements overlap | Increase spacing between coordinates |
| Text doesn't fit in boxes | Increase box width or reduce font size |
| Too many elements | Break into multiple diagrams |
| Unclear layout | Use grid layout (rows/columns) or radial layout (mind maps) |
| Colors inconsistent | Define color palette upfront based on element types |
## Advanced Techniques
### Grid Layout (for Relationship Diagrams)
```javascript
const columns = Math.ceil(Math.sqrt(entityCount));
const x = startX + (index % columns) * horizontalGap;
const y = startY + Math.floor(index / columns) * verticalGap;
```
### Radial Layout (for Mind Maps)
```javascript
const angle = (2 * Math.PI * index) / branchCount;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
```
### Auto-generated IDs
Use timestamp + random string for unique IDs:
```javascript
const id = Date.now().toString(36) + Math.random().toString(36).substr(2);
```
## Output Format
Always provide:
1. ✅ Complete `.excalidraw` JSON file
2. 📊 Summary of what was created
3. 📝 Element count
4. 💡 Instructions for opening/editing
**Example summary:**
```
Created: user-workflow.excalidraw
Type: Flowchart
Elements: 7 rectangles, 6 arrows, 1 title text
Total: 14 elements
To view:
1. Visit https://excalidraw.com
2. Drag and drop user-workflow.excalidraw
3. Or use File → Open in Excalidraw VS Code extension
```
## Validation Checklist
Before delivering the diagram:
- [ ] All elements have unique IDs
- [ ] Coordinates prevent overlapping
- [ ] Text is readable (font size 16+)
- [ ] **All text elements use `fontFamily: 5` (Excalifont)**
- [ ] Arrows connect logically
- [ ] Colors follow consistent scheme
- [ ] File is valid JSON
- [ ] Element count is reasonable (<20 for clarity)
## Icon Libraries (Optional Enhancement)
For specialized diagrams (e.g., AWS/GCP/Azure architecture diagrams), you can use pre-made icon libraries from Excalidraw. This provides professional, standardized icons instead of basic shapes.
### When User Requests Icons
**If user asks for AWS/cloud architecture diagrams or mentions wanting to use specific icons:**
1. **Check if library exists**: Look for `libraries/<library-name>/reference.md`
2. **If library exists**: Proceed to use icons (see AI Assistant Workflow below)
3. **If library does NOT exist**: Respond with setup instructions:
```
To use [AWS/GCP/Azure/etc.] architecture icons, please follow these steps:
1. Visit https://libraries.excalidraw.com/
2. Search for "[AWS Architecture Icons/etc.]" and download the .excalidrawlib file
3. Create directory: skills/excalidraw-diagram-generator/libraries/[icon-set-name]/
4. Place the downloaded file in that directory
5. Run the splitter script:
python skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/[icon-set-name]/
This will split the library into individual icon files for efficient use.
After setup is complete, I can create your diagram using the actual AWS/cloud icons.
Alternatively, I can create the diagram now using simple shapes (rectangles, ellipses)
which you can later replace with icons manually in Excalidraw.
```
### User Setup Instructions (Detailed)
**Step 1: Create Library Directory**
```bash
mkdir -p skills/excalidraw-diagram-generator/libraries/aws-architecture-icons
```
**Step 2: Download Library**
- Visit: https://libraries.excalidraw.com/
- Search for your desired icon set (e.g., "AWS Architecture Icons")
- Click download to get the `.excalidrawlib` file
- Example categories (availability varies; confirm on the site):
- Cloud service icons
- UI/Material icons
- Flowchart symbols
**Step 3: Place Library File**
- Rename the downloaded file to match the directory name (e.g., `aws-architecture-icons.excalidrawlib`)
- Move it to the directory created in Step 1
**Step 4: Run Splitter Script**
```bash
python skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/
```
**Step 5: Verify Setup**
After running the script, verify the following structure exists:
```
skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/
aws-architecture-icons.excalidrawlib (original)
reference.md (generated - icon lookup table)
icons/ (generated - individual icon files)
API-Gateway.json
CloudFront.json
EC2.json
Lambda.json
RDS.json
S3.json
...
```
### AI Assistant Workflow
**When icon libraries are available in `libraries/`:**
**RECOMMENDED APPROACH: Use Python Scripts (Efficient & Reliable)**
The repository includes Python scripts that handle icon integration automatically:
1. **Create base diagram structure**:
- Create `.excalidraw` file with basic layout (title, boxes, regions)
- This establishes the canvas and overall structure
2. **Add icons using Python script**:
```bash
python skills/excalidraw-diagram-generator/scripts/add-icon-to-diagram.py \
<diagram-path> <icon-name> <x> <y> [--label "Text"] [--library-path PATH]
```
- Edit via `.excalidraw.edit` is enabled by default to avoid overwrite issues; pass `--no-use-edit-suffix` to disable.
**Examples**:
```bash
# Add EC2 icon at position (400, 300) with label
python scripts/add-icon-to-diagram.py diagram.excalidraw EC2 400 300 --label "Web Server"
# Add VPC icon at position (200, 150)
python scripts/add-icon-to-diagram.py diagram.excalidraw VPC 200 150
# Add icon from different library
python scripts/add-icon-to-diagram.py diagram.excalidraw Compute-Engine 500 200 \
--library-path libraries/gcp-icons --label "API Server"
```
3. **Add connecting arrows**:
```bash
python skills/excalidraw-diagram-generator/scripts/add-arrow.py \
<diagram-path> <from-x> <from-y> <to-x> <to-y> [--label "Text"] [--style solid|dashed|dotted] [--color HEX]
```
- Edit via `.excalidraw.edit` is enabled by default to avoid overwrite issues; pass `--no-use-edit-suffix` to disable.
**Examples**:
```bash
# Simple arrow from (300, 250) to (500, 300)
python scripts/add-arrow.py diagram.excalidraw 300 250 500 300
# Arrow with label
python scripts/add-arrow.py diagram.excalidraw 300 250 500 300 --label "HTTPS"
# Dashed arrow with custom color
python scripts/add-arrow.py diagram.excalidraw 400 350 600 400 --style dashed --color "#7950f2"
```
4. **Workflow summary**:
```bash
# Step 1: Create base diagram with title and structure
# (Create .excalidraw file with initial elements)
# Step 2: Add icons with labels
python scripts/add-icon-to-diagram.py my-diagram.excalidraw "Internet-gateway" 200 150 --label "Internet Gateway"
python scripts/add-icon-to-diagram.py my-diagram.excalidraw VPC 250 250
python scripts/add-icon-to-diagram.py my-diagram.excalidraw ELB 350 300 --label "Load Balancer"
python scripts/add-icon-to-diagram.py my-diagram.excalidraw EC2 450 350 --label "EC2 Instance"
python scripts/add-icon-to-diagram.py my-diagram.excalidraw RDS 550 400 --label "Database"
# Step 3: Add connecting arrows
python scripts/add-arrow.py my-diagram.excalidraw 250 200 300 250 # Internet → VPC
python scripts/add-arrow.py my-diagram.excalidraw 300 300 400 300 # VPC → ELB
python scripts/add-arrow.py my-diagram.excalidraw 400 330 500 350 # ELB → EC2
python scripts/add-arrow.py my-diagram.excalidraw 500 380 600 400 # EC2 → RDS
```
**Benefits of Python Script Approach**:
- ✅ **No token consumption**: Icon JSON data (200-1000 lines each) never enters AI context
- ✅ **Accurate transformations**: Coordinate calculations handled deterministically
- ✅ **ID management**: Automatic UUID generation prevents conflicts
- ✅ **Reliable**: No risk of coordinate miscalculation or ID collision
- ✅ **Fast**: Direct file manipulation, no parsing overhead
- ✅ **Reusable**: Works with any Excalidraw library you provide
**ALTERNATIVE: Manual Icon Integration (Not Recommended)**
Only use this if Python scripts are unavailable:
1. **Check for libraries**:
```
List directory: skills/excalidraw-diagram-generator/libraries/
Look for subdirectories containing reference.md files
```
2. **Read reference.md**:
```
Open: libraries/<library-name>/reference.md
This is lightweight (typically <300 lines) and lists all available icons
```
3. **Find relevant icons**:
```
Search the reference.md table for icon names matching diagram needs
Example: For AWS diagram with EC2, S3, Lambda → Find "EC2", "S3", "Lambda" in table
```
4. **Load specific icon data** (WARNING: Large files):
```
Read ONLY the needed icon files:
- libraries/aws-architecture-icons/icons/EC2.json (200-300 lines)
- libraries/aws-architecture-icons/icons/S3.json (200-300 lines)
- libraries/aws-architecture-icons/icons/Lambda.json (200-300 lines)
Note: Each icon file is 200-1000 lines - this consumes significant tokens
```
5. **Extract and transform elements**:
```
Each icon JSON contains an "elements" array
Calculate bounding box (min_x, min_y, max_x, max_y)
Apply offset to all x/y coordinates
Generate new unique IDs for all elements
Update groupIds references
Copy transformed elements into your diagram
```
6. **Position icons and add connections**:
```
Adjust x/y coordinates to position icons correctly in the diagram
Update IDs to ensure uniqueness across diagram
Add connecting arrows and labels as needed
```
**Manual Integration Challenges**:
- ⚠️ High token consumption (200-1000 lines per icon × number of icons)
- ⚠️ Complex coordinate transformation calculations
- ⚠️ Risk of ID collision if not handled carefully
- ⚠️ Time-consuming for diagrams with many icons
### Example: Creating AWS Diagram with Icons
**Request**: "Create an AWS architecture diagram with Internet Gateway, VPC, ELB, EC2, and RDS"
**Recommended Workflow (using Python scripts)**:
**Request**: "Create an AWS architecture diagram with Internet Gateway, VPC, ELB, EC2, and RDS"
**Recommended Workflow (using Python scripts)**:
```bash
# Step 1: Create base diagram file with title
# Create my-aws-diagram.excalidraw with basic structure (title, etc.)
# Step 2: Check icon availability
# Read: libraries/aws-architecture-icons/reference.md
# Confirm icons exist: Internet-gateway, VPC, ELB, EC2, RDS
# Step 3: Add icons with Python script
python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw "Internet-gateway" 150 100 --label "Internet Gateway"
python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw VPC 200 200
python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw ELB 350 250 --label "Load Balancer"
python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw EC2 500 300 --label "Web Server"
python scripts/add-icon-to-diagram.py my-aws-diagram.excalidraw RDS 650 350 --label "Database"
# Step 4: Add connecting arrows
python scripts/add-arrow.py my-aws-diagram.excalidraw 200 150 250 200 # Internet → VPC
python scripts/add-arrow.py my-aws-diagram.excalidraw 265 230 350 250 # VPC → ELB
python scripts/add-arrow.py my-aws-diagram.excalidraw 415 280 500 300 # ELB → EC2
python scripts/add-arrow.py my-aws-diagram.excalidraw 565 330 650 350 --label "SQL" --style dashed
# Result: Complete diagram with professional AWS icons, labels, and connections
```
**Benefits**:
- No manual coordinate calculation
- No token consumption for icon data
- Deterministic, reliable results
- Easy to iterate and adjust positions
**Alternative Workflow (manual, if scripts unavailable)**:
1. Check: `libraries/aws-architecture-icons/reference.md` exists → Yes
2. Read reference.md → Find entries for Internet-gateway, VPC, ELB, EC2, RDS
3. Load:
- `icons/Internet-gateway.json` (298 lines)
- `icons/VPC.json` (550 lines)
- `icons/ELB.json` (363 lines)
- `icons/EC2.json` (231 lines)
- `icons/RDS.json` (similar size)
**Total: ~2000+ lines of JSON to process**
4. Extract elements from each JSON
5. Calculate bounding boxes and offsets for each icon
6. Transform all coordinates (x, y) for positioning
7. Generate unique IDs for all elements
8. Add arrows showing data flow
9. Add text labels
10. Generate final `.excalidraw` file
**Challenges with manual approach**:
- High token consumption (~2000-5000 lines)
- Complex coordinate math
- Risk of ID conflicts
### Supported Icon Libraries (Examples — verify availability)
- This workflow works with any valid `.excalidrawlib` file you provide.
- Examples of library categories you may find on https://libraries.excalidraw.com/:
- Cloud service icons
- Kubernetes / infrastructure icons
- UI / Material icons
- Flowchart / diagram symbols
- Network diagram icons
- Availability and naming can change; verify exact library names on the site before use.
### Fallback: No Icons Available
**If no icon libraries are set up:**
- Create diagrams using basic shapes (rectangles, ellipses, arrows)
- Use color coding and text labels to distinguish components
- Inform user they can add icons later or set up libraries for future diagrams
- The diagram will still be functional and clear, just less visually polished
## References
See bundled references for:
- `references/excalidraw-schema.md` - Complete Excalidraw JSON schema
- `references/element-types.md` - Detailed element type specifications
- `templates/flowchart-template.json` - Basic flowchart starter
- `templates/relationship-template.json` - Relationship diagram starter
- `templates/mindmap-template.json` - Mind map starter
- `scripts/split-excalidraw-library.py` - Tool to split `.excalidrawlib` files
- `scripts/README.md` - Documentation for library tools
- `scripts/.gitignore` - Prevents local Python artifacts from being committed
## Limitations
- Complex curves are simplified to straight/basic curved lines
- Hand-drawn roughness is set to default (1)
- No embedded images support in auto-generation
- Maximum recommended elements: 20 per diagram
- No automatic collision detection (use spacing guidelines)
## Future Enhancements
Potential improvements:
- Auto-layout optimization algorithms
- Import from Mermaid/PlantUML syntax
- Template library expansion
- Interactive editing after generation
references/
element-types.md 8.5 KB
# Excalidraw Element Types Guide
Detailed specifications for each Excalidraw element type with visual examples and use cases.
## Element Type Overview
| Type | Visual | Primary Use | Text Support |
|------|--------|-------------|--------------|
| `rectangle` | □ | Boxes, containers, process steps | ✅ Yes |
| `ellipse` | ○ | Emphasis, terminals, states | ✅ Yes |
| `diamond` | ◇ | Decision points, choices | ✅ Yes |
| `arrow` | → | Directional flow, relationships | ❌ No (use separate text) |
| `line` | — | Connections, dividers | ❌ No |
| `text` | A | Labels, annotations, titles | ✅ (Its purpose) |
---
## Rectangle
**Best for:** Process steps, entities, data stores, components
### Properties
```typescript
{
type: "rectangle",
roundness: { type: 3 }, // Rounded corners
text: "Step Name", // Optional embedded text
fontSize: 20,
textAlign: "center",
verticalAlign: "middle"
}
```
### Use Cases
| Scenario | Configuration |
|----------|---------------|
| **Process step** | Green background (`#b2f2bb`), centered text |
| **Entity/Object** | Blue background (`#a5d8ff`), medium size |
| **System component** | Light color, descriptive text |
| **Data store** | Gray/white, database-like label |
### Size Guidelines
| Content | Width | Height |
|---------|-------|--------|
| Single word | 120-150px | 60-80px |
| Short phrase (2-4 words) | 180-220px | 80-100px |
| Sentence | 250-300px | 100-120px |
### Example
```json
{
"type": "rectangle",
"x": 100,
"y": 100,
"width": 200,
"height": 80,
"backgroundColor": "#b2f2bb",
"text": "Validate Input",
"fontSize": 20,
"textAlign": "center",
"verticalAlign": "middle",
"roundness": { "type": 3 }
}
```
---
## Ellipse
**Best for:** Start/end points, states, emphasis circles
### Properties
```typescript
{
type: "ellipse",
text: "Start",
fontSize: 18,
textAlign: "center",
verticalAlign: "middle"
}
```
### Use Cases
| Scenario | Configuration |
|----------|---------------|
| **Flow start** | Light green, "Start" text |
| **Flow end** | Light red, "End" text |
| **State** | Soft color, state name |
| **Highlight** | Bright color, emphasis text |
### Size Guidelines
For circular shapes, use `width === height`:
| Content | Diameter |
|---------|----------|
| Icon/Symbol | 60-80px |
| Short text | 100-120px |
| Longer text | 150-180px |
### Example
```json
{
"type": "ellipse",
"x": 100,
"y": 100,
"width": 120,
"height": 120,
"backgroundColor": "#d0f0c0",
"text": "Start",
"fontSize": 18,
"textAlign": "center",
"verticalAlign": "middle"
}
```
---
## Diamond
**Best for:** Decision points, conditional branches
### Properties
```typescript
{
type: "diamond",
text: "Valid?",
fontSize: 18,
textAlign: "center",
verticalAlign": "middle"
}
```
### Use Cases
| Scenario | Text Example |
|----------|--------------|
| **Yes/No decision** | "Is Valid?", "Exists?" |
| **Multiple choice** | "Type?", "Status?" |
| **Conditional** | "Score > 50?" |
### Size Guidelines
Diamonds need more space than rectangles for the same text:
| Content | Width | Height |
|---------|-------|--------|
| Yes/No | 120-140px | 120-140px |
| Short question | 160-180px | 160-180px |
| Longer question | 200-220px | 200-220px |
### Example
```json
{
"type": "diamond",
"x": 100,
"y": 100,
"width": 150,
"height": 150,
"backgroundColor": "#ffe4a3",
"text": "Valid?",
"fontSize": 18,
"textAlign": "center",
"verticalAlign": "middle"
}
```
---
## Arrow
**Best for:** Flow direction, relationships, dependencies
### Properties
```typescript
{
type: "arrow",
points: [[0, 0], [endX, endY]], // Relative coordinates
roundness: { type: 2 }, // Curved
startBinding: null, // Or { elementId, focus, gap }
endBinding: null
}
```
### Arrow Directions
#### Horizontal (Left to Right)
```json
{
"x": 100,
"y": 150,
"width": 200,
"height": 0,
"points": [[0, 0], [200, 0]]
}
```
#### Vertical (Top to Bottom)
```json
{
"x": 200,
"y": 100,
"width": 0,
"height": 150,
"points": [[0, 0], [0, 150]]
}
```
#### Diagonal
```json
{
"x": 100,
"y": 100,
"width": 200,
"height": 150,
"points": [[0, 0], [200, 150]]
}
```
### Arrow Styles
| Style | `strokeStyle` | `strokeWidth` | Use Case |
|-------|---------------|---------------|----------|
| **Normal flow** | `"solid"` | 2 | Standard connections |
| **Optional/Weak** | `"dashed"` | 2 | Optional paths |
| **Important** | `"solid"` | 3-4 | Emphasized flow |
| **Dotted** | `"dotted"` | 2 | Indirect relationships |
### Adding Arrow Labels
Use separate text elements positioned near arrow midpoint:
```json
[
{
"type": "arrow",
"id": "arrow1",
"x": 100,
"y": 150,
"points": [[0, 0], [200, 0]]
},
{
"type": "text",
"x": 180, // Near midpoint
"y": 130, // Above arrow
"text": "sends",
"fontSize": 14
}
]
```
---
## Line
**Best for:** Non-directional connections, dividers, borders
### Properties
```typescript
{
type: "line",
points: [[0, 0], [x2, y2], [x3, y3], ...],
roundness: null // Or { type: 2 } for curved
}
```
### Use Cases
| Scenario | Configuration |
|----------|---------------|
| **Divider** | Horizontal, thin stroke |
| **Border** | Closed path (polygon) |
| **Connection** | Multi-point path |
| **Underline** | Short horizontal line |
### Multi-Point Line Example
```json
{
"type": "line",
"x": 100,
"y": 100,
"points": [
[0, 0],
[100, 50],
[200, 0]
]
}
```
---
## Text
**Best for:** Labels, titles, annotations, standalone text
### Properties
```typescript
{
type: "text",
text: "Label text",
fontSize: 20,
fontFamily: 1, // 1=Virgil, 2=Helvetica, 3=Cascadia
textAlign: "left",
verticalAlign: "top"
}
```
### Font Sizes by Purpose
| Purpose | Font Size |
|---------|-----------|
| **Main title** | 28-36 |
| **Section header** | 24-28 |
| **Element label** | 18-22 |
| **Annotation** | 14-16 |
| **Small note** | 12-14 |
### Width/Height Calculation
```javascript
// Approximate width
const width = text.length * fontSize * 0.6;
// Approximate height (single line)
const height = fontSize * 1.2;
// Multi-line
const lines = text.split('\n').length;
const height = fontSize * 1.2 * lines;
```
### Text Positioning
| Position | textAlign | verticalAlign | Use Case |
|----------|-----------|---------------|----------|
| **Top-left** | `"left"` | `"top"` | Default labels |
| **Centered** | `"center"` | `"middle"` | Titles |
| **Bottom-right** | `"right"` | `"bottom"` | Footnotes |
### Example: Title
```json
{
"type": "text",
"x": 100,
"y": 50,
"width": 400,
"height": 40,
"text": "System Architecture",
"fontSize": 32,
"fontFamily": 2,
"textAlign": "center",
"verticalAlign": "top"
}
```
### Example: Annotation
```json
{
"type": "text",
"x": 150,
"y": 200,
"width": 100,
"height": 20,
"text": "User input",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
}
```
---
## Combining Elements
### Pattern: Labeled Box
```json
[
{
"type": "rectangle",
"id": "box1",
"x": 100,
"y": 100,
"width": 200,
"height": 100,
"text": "Component",
"textAlign": "center",
"verticalAlign": "middle"
}
]
```
### Pattern: Connected Boxes
```json
[
{
"type": "rectangle",
"id": "box1",
"x": 100,
"y": 100,
"width": 150,
"height": 80,
"text": "Step 1"
},
{
"type": "arrow",
"id": "arrow1",
"x": 250,
"y": 140,
"points": [[0, 0], [100, 0]]
},
{
"type": "rectangle",
"id": "box2",
"x": 350,
"y": 100,
"width": 150,
"height": 80,
"text": "Step 2"
}
]
```
### Pattern: Decision Tree
```json
[
{
"type": "diamond",
"id": "decision",
"x": 100,
"y": 100,
"width": 140,
"height": 140,
"text": "Valid?"
},
{
"type": "arrow",
"id": "yes-arrow",
"x": 240,
"y": 170,
"points": [[0, 0], [60, 0]]
},
{
"type": "text",
"id": "yes-label",
"x": 250,
"y": 150,
"text": "Yes",
"fontSize": 14
},
{
"type": "rectangle",
"id": "yes-box",
"x": 300,
"y": 140,
"width": 120,
"height": 60,
"text": "Process"
}
]
```
---
## Summary
| When you need... | Use this element |
|------------------|------------------|
| Process box | `rectangle` with text |
| Decision point | `diamond` with question |
| Flow direction | `arrow` |
| Start/End | `ellipse` |
| Title/Header | `text` (large font) |
| Annotation | `text` (small font) |
| Non-directional link | `line` |
| Divider | `line` (horizontal) |
excalidraw-schema.md 8.2 KB
# Excalidraw JSON Schema Reference
This document describes the structure of Excalidraw `.excalidraw` files for diagram generation.
## Top-Level Structure
```typescript
interface ExcalidrawFile {
type: "excalidraw";
version: number; // Always 2
source: string; // "https://excalidraw.com"
elements: ExcalidrawElement[];
appState: AppState;
files: Record<string, any>; // Usually empty {}
}
```
## AppState
```typescript
interface AppState {
viewBackgroundColor: string; // Hex color, e.g., "#ffffff"
gridSize: number; // Typically 20
}
```
## ExcalidrawElement Base Properties
All elements share these common properties:
```typescript
interface BaseElement {
id: string; // Unique identifier
type: ElementType; // See Element Types below
x: number; // X coordinate (pixels from top-left)
y: number; // Y coordinate (pixels from top-left)
width: number; // Width in pixels
height: number; // Height in pixels
angle: number; // Rotation angle in radians (usually 0)
strokeColor: string; // Hex color, e.g., "#1e1e1e"
backgroundColor: string; // Hex color or "transparent"
fillStyle: "solid" | "hachure" | "cross-hatch";
strokeWidth: number; // 1-4 typically
strokeStyle: "solid" | "dashed" | "dotted";
roughness: number; // 0-2, controls hand-drawn effect (1 = default)
opacity: number; // 0-100
groupIds: string[]; // IDs of groups this element belongs to
frameId: null; // Usually null
index: string; // Stacking order identifier
roundness: Roundness | null;
seed: number; // Random seed for deterministic rendering
version: number; // Element version (increment on edit)
versionNonce: number; // Random number changed on edit
isDeleted: boolean; // Should be false
boundElements: any; // Usually null
updated: number; // Timestamp in milliseconds
link: null; // External link (usually null)
locked: boolean; // Whether element is locked
}
```
## Element Types
### Rectangle
```typescript
interface RectangleElement extends BaseElement {
type: "rectangle";
roundness: { type: 3 }; // 3 = rounded corners
text?: string; // Optional text inside
fontSize?: number; // Font size (16-32 typical)
fontFamily?: number; // 1 = Virgil, 2 = Helvetica, 3 = Cascadia
textAlign?: "left" | "center" | "right";
verticalAlign?: "top" | "middle" | "bottom";
}
```
**Example:**
```json
{
"id": "rect1",
"type": "rectangle",
"x": 100,
"y": 100,
"width": 200,
"height": 100,
"strokeColor": "#1e1e1e",
"backgroundColor": "#a5d8ff",
"text": "My Box",
"fontSize": 20,
"textAlign": "center",
"verticalAlign": "middle",
"roundness": { "type": 3 }
}
```
### Ellipse
```typescript
interface EllipseElement extends BaseElement {
type: "ellipse";
text?: string;
fontSize?: number;
fontFamily?: number;
textAlign?: "left" | "center" | "right";
verticalAlign?: "top" | "middle" | "bottom";
}
```
### Diamond
```typescript
interface DiamondElement extends BaseElement {
type: "diamond";
text?: string;
fontSize?: number;
fontFamily?: number;
textAlign?: "left" | "center" | "right";
verticalAlign?: "top" | "middle" | "bottom";
}
```
### Arrow
```typescript
interface ArrowElement extends BaseElement {
type: "arrow";
points: [number, number][]; // Array of [x, y] coordinates relative to element
startBinding: Binding | null;
endBinding: Binding | null;
roundness: { type: 2 }; // 2 = curved arrow
}
```
**Example:**
```json
{
"id": "arrow1",
"type": "arrow",
"x": 100,
"y": 100,
"width": 200,
"height": 0,
"points": [
[0, 0],
[200, 0]
],
"roundness": { "type": 2 },
"startBinding": null,
"endBinding": null
}
```
**Points explanation:**
- First point `[0, 0]` is relative to `(x, y)`
- Subsequent points are relative to the first point
- For straight horizontal arrow: `[[0, 0], [width, 0]]`
- For straight vertical arrow: `[[0, 0], [0, height]]`
### Line
```typescript
interface LineElement extends BaseElement {
type: "line";
points: [number, number][];
startBinding: Binding | null;
endBinding: Binding | null;
roundness: { type: 2 } | null;
}
```
### Text
```typescript
interface TextElement extends BaseElement {
type: "text";
text: string;
fontSize: number;
fontFamily: number; // 1-3
textAlign: "left" | "center" | "right";
verticalAlign: "top" | "middle" | "bottom";
roundness: null; // Text has no roundness
}
```
**Example:**
```json
{
"id": "text1",
"type": "text",
"x": 100,
"y": 100,
"width": 150,
"height": 25,
"text": "Hello World",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"roundness": null
}
```
**Width/Height calculation:**
- Width ≈ `text.length * fontSize * 0.6`
- Height ≈ `fontSize * 1.2 * numberOfLines`
## Bindings
Bindings connect arrows to shapes:
```typescript
interface Binding {
elementId: string; // ID of bound element
focus: number; // -1 to 1, position along edge
gap: number; // Distance from element edge
}
```
## Common Colors
| Color Name | Hex Code | Use Case |
|------------|----------|----------|
| Black | `#1e1e1e` | Default stroke |
| Light Blue | `#a5d8ff` | Primary entities |
| Light Green | `#b2f2bb` | Process steps |
| Yellow | `#ffd43b` | Important/Central |
| Light Red | `#ffc9c9` | Warnings/Errors |
| Cyan | `#96f2d7` | Secondary items |
| Transparent | `transparent` | No fill |
| White | `#ffffff` | Background |
## ID Generation
IDs should be unique strings. Common patterns:
```javascript
// Timestamp-based
const id = Date.now().toString(36) + Math.random().toString(36).substr(2);
// Sequential
const id = "element-" + counter++;
// Descriptive
const id = "step-1", "entity-user", "arrow-1-to-2";
```
## Seed Generation
Seeds are used for deterministic randomness in hand-drawn effect:
```javascript
const seed = Math.floor(Math.random() * 2147483647);
```
## Version and VersionNonce
```javascript
const version = 1; // Increment when element is edited
const versionNonce = Math.floor(Math.random() * 2147483647);
```
## Coordinate System
- Origin `(0, 0)` is top-left corner
- X increases to the right
- Y increases downward
- All units are in pixels
## Recommended Spacing
| Context | Spacing |
|---------|---------|
| Horizontal gap between elements | 200-300px |
| Vertical gap between rows | 100-150px |
| Minimum margin from edge | 50px |
| Arrow-to-box clearance | 20-30px |
## Font Families
| ID | Name | Description |
|----|------|-------------|
| 1 | Virgil | Hand-drawn style (default) |
| 2 | Helvetica | Clean sans-serif |
| 3 | Cascadia | Monospace |
## Validation Rules
✅ **Required:**
- All IDs must be unique
- `type` must match actual element type
- `version` must be an integer ≥ 1
- `opacity` must be 0-100
⚠️ **Recommended:**
- Keep `roughness` at 1 for consistency
- Use `strokeWidth` of 2 for clarity
- Set `isDeleted` to `false`
- Set `locked` to `false`
- Keep `frameId`, `boundElements`, `link` as `null`
## Complete Minimal Example
```json
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "box1",
"type": "rectangle",
"x": 100,
"y": 100,
"width": 200,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#a5d8ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": { "type": 3 },
"seed": 1234567890,
"version": 1,
"versionNonce": 987654321,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Hello",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}
```
scripts/
README.md 6.4 KB
# Excalidraw Library Tools
This directory contains scripts for working with Excalidraw libraries.
## split-excalidraw-library.py
Splits an Excalidraw library file (`*.excalidrawlib`) into individual icon JSON files for efficient token usage by AI assistants.
### Prerequisites
- Python 3.6 or higher
- No additional dependencies required (uses only standard library)
### Usage
```bash
python split-excalidraw-library.py <path-to-library-directory>
```
### Step-by-Step Workflow
1. **Create library directory**:
```bash
mkdir -p skills/excalidraw-diagram-generator/libraries/aws-architecture-icons
```
2. **Download and place library file**:
- Visit: https://libraries.excalidraw.com/
- Search for "AWS Architecture Icons" and download the `.excalidrawlib` file
- Rename it to match the directory name: `aws-architecture-icons.excalidrawlib`
- Place it in the directory created in step 1
3. **Run the script**:
```bash
python skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/
```
### Output Structure
The script creates the following structure in the library directory:
```
skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/
aws-architecture-icons.excalidrawlib # Original file (kept)
reference.md # Generated: Quick reference table
icons/ # Generated: Individual icon files
API-Gateway.json
CloudFront.json
EC2.json
S3.json
...
```
### What the Script Does
1. **Reads** the `.excalidrawlib` file
2. **Extracts** each icon from the `libraryItems` array
3. **Sanitizes** icon names to create valid filenames (spaces → hyphens, removes special characters)
4. **Saves** each icon as a separate JSON file in the `icons/` directory
5. **Generates** a `reference.md` file with a table mapping icon names to filenames
### Benefits
- **Token Efficiency**: AI can first read the lightweight `reference.md` to find relevant icons, then load only the specific icon files needed
- **Organization**: Icons are organized in a clear directory structure
- **Extensibility**: Users can add multiple library sets side-by-side
### Recommended Workflow
1. Download desired Excalidraw libraries from https://libraries.excalidraw.com/
2. Run this script on each library file
3. Move the generated folders to `../libraries/`
4. The AI assistant will use `reference.md` files to locate and use icons efficiently
### Library Sources (Examples — verify availability)
- Examples found on https://libraries.excalidraw.com/ may include cloud/service icon sets.
- Availability changes over time; verify the exact library names on the site before use.
- This script works with any valid `.excalidrawlib` file you provide.
### Troubleshooting
**Error: File not found**
- Check that the file path is correct
- Make sure the file has a `.excalidrawlib` extension
**Error: Invalid library file format**
- Ensure the file is a valid Excalidraw library file
- Check that it contains a `libraryItems` array
### License Considerations
When using third-party icon libraries:
- **AWS Architecture Icons**: Subject to AWS Content License
- **GCP Icons**: Subject to Google's terms
- **Other libraries**: Check each library's license
This script is for personal/organizational use. Redistribution of split icon files should comply with the original library's license terms.
## add-icon-to-diagram.py
Adds a specific icon from a split Excalidraw library into an existing `.excalidraw` diagram. The script handles coordinate translation and ID collision avoidance, and can optionally add a label under the icon.
### Prerequisites
- Python 3.6 or higher
- A diagram file (`.excalidraw`)
- A split icon library directory (created by `split-excalidraw-library.py`)
### Usage
```bash
python add-icon-to-diagram.py <diagram-path> <icon-name> <x> <y> [OPTIONS]
```
**Options**
- `--library-path PATH` : Path to the icon library directory (default: `aws-architecture-icons`)
- `--label TEXT` : Add a text label below the icon
-- `--use-edit-suffix` : Edit via `.excalidraw.edit` to avoid editor overwrite issues (enabled by default; pass `--no-use-edit-suffix` to disable)
### Examples
```bash
# Add EC2 icon at position (400, 300)
python add-icon-to-diagram.py diagram.excalidraw EC2 400 300
# Add VPC icon with label
python add-icon-to-diagram.py diagram.excalidraw VPC 200 150 --label "VPC"
# Safe edit mode is enabled by default (avoids editor overwrite issues)
# Use `--no-use-edit-suffix` to disable
python add-icon-to-diagram.py diagram.excalidraw EC2 500 300
# Add icon from another library
python add-icon-to-diagram.py diagram.excalidraw Compute-Engine 500 200 \
--library-path libraries/gcp-icons --label "API Server"
```
### What the Script Does
1. **Loads** the icon JSON from the library’s `icons/` directory
2. **Calculates** the icon’s bounding box
3. **Offsets** all coordinates to the target position
4. **Generates** unique IDs for all elements and groups
5. **Appends** the transformed elements to the diagram
6. **(Optional)** Adds a label beneath the icon
---
## add-arrow.py
Adds a straight arrow between two points in an existing `.excalidraw` diagram. Supports optional labels and line styles.
### Prerequisites
- Python 3.6 or higher
- A diagram file (`.excalidraw`)
### Usage
```bash
python add-arrow.py <diagram-path> <from-x> <from-y> <to-x> <to-y> [OPTIONS]
```
**Options**
- `--style {solid|dashed|dotted}` : Line style (default: `solid`)
- `--color HEX` : Arrow color (default: `#1e1e1e`)
- `--label TEXT` : Add a text label on the arrow
-- `--use-edit-suffix` : Edit via `.excalidraw.edit` to avoid editor overwrite issues (enabled by default; pass `--no-use-edit-suffix` to disable)
### Examples
```bash
# Simple arrow
python add-arrow.py diagram.excalidraw 300 200 500 300
# Arrow with label
python add-arrow.py diagram.excalidraw 300 200 500 300 --label "HTTPS"
# Dashed arrow with custom color
python add-arrow.py diagram.excalidraw 400 350 600 400 --style dashed --color "#7950f2"
# Safe edit mode is enabled by default (avoids editor overwrite issues)
# Use `--no-use-edit-suffix` to disable
python add-arrow.py diagram.excalidraw 300 200 500 300
```
### What the Script Does
1. **Creates** an arrow element from the given coordinates
2. **(Optional)** Adds a label near the arrow midpoint
3. **Appends** elements to the diagram
4. **Saves** the updated file
add-arrow.py 9.5 KB
#!/usr/bin/env python3
"""
Add arrows (connections) between elements in Excalidraw diagrams.
Usage:
python add-arrow.py <diagram_path> <from_x> <from_y> <to_x> <to_y> [OPTIONS]
Options:
--style {solid|dashed|dotted} Arrow line style (default: solid)
--color HEX Arrow color (default: #1e1e1e)
--label TEXT Add text label on the arrow
--use-edit-suffix Edit via .excalidraw.edit to avoid editor overwrite issues (enabled by default; use --no-use-edit-suffix to disable)
Examples:
python add-arrow.py diagram.excalidraw 300 200 500 300
python add-arrow.py diagram.excalidraw 300 200 500 300 --label "HTTP"
python add-arrow.py diagram.excalidraw 300 200 500 300 --style dashed --color "#7950f2"
python add-arrow.py diagram.excalidraw 300 200 500 300 --use-edit-suffix
"""
import json
import sys
import uuid
from pathlib import Path
from typing import Dict, Any
def generate_unique_id() -> str:
"""Generate a unique ID for Excalidraw elements."""
return str(uuid.uuid4()).replace('-', '')[:16]
def prepare_edit_path(diagram_path: Path, use_edit_suffix: bool) -> tuple[Path, Path | None]:
"""
Prepare a safe edit path to avoid editor overwrite issues.
Returns:
(work_path, final_path)
- work_path: file path to read/write during edit
- final_path: file path to rename back to (or None if not used)
"""
if not use_edit_suffix:
return diagram_path, None
if diagram_path.suffix != ".excalidraw":
return diagram_path, None
edit_path = diagram_path.with_suffix(diagram_path.suffix + ".edit")
if diagram_path.exists():
if edit_path.exists():
raise FileExistsError(f"Edit file already exists: {edit_path}")
diagram_path.rename(edit_path)
return edit_path, diagram_path
def finalize_edit_path(work_path: Path, final_path: Path | None) -> None:
"""Finalize edit by renaming .edit back to .excalidraw if needed."""
if final_path is None:
return
if final_path.exists():
final_path.unlink()
work_path.rename(final_path)
def create_arrow(
from_x: float,
from_y: float,
to_x: float,
to_y: float,
style: str = "solid",
color: str = "#1e1e1e",
label: str = None
) -> list:
"""
Create an arrow element.
Args:
from_x: Starting X coordinate
from_y: Starting Y coordinate
to_x: Ending X coordinate
to_y: Ending Y coordinate
style: Line style (solid, dashed, dotted)
color: Arrow color
label: Optional text label on the arrow
Returns:
List of elements (arrow and optional label)
"""
elements = []
# Arrow element
arrow = {
"id": generate_unique_id(),
"type": "arrow",
"x": from_x,
"y": from_y,
"width": to_x - from_x,
"height": to_y - from_y,
"angle": 0,
"strokeColor": color,
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": style,
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": None,
"index": "a0",
"roundness": {
"type": 2
},
"seed": 1000000000 + hash(f"{from_x}{from_y}{to_x}{to_y}") % 1000000000,
"version": 1,
"versionNonce": 2000000000 + hash(f"{from_x}{from_y}{to_x}{to_y}") % 1000000000,
"isDeleted": False,
"boundElements": [],
"updated": 1738195200000,
"link": None,
"locked": False,
"points": [
[0, 0],
[to_x - from_x, to_y - from_y]
],
"startBinding": None,
"endBinding": None,
"startArrowhead": None,
"endArrowhead": "arrow",
"lastCommittedPoint": None
}
elements.append(arrow)
# Optional label
if label:
mid_x = (from_x + to_x) / 2 - (len(label) * 5)
mid_y = (from_y + to_y) / 2 - 10
label_element = {
"id": generate_unique_id(),
"type": "text",
"x": mid_x,
"y": mid_y,
"width": len(label) * 10,
"height": 20,
"angle": 0,
"strokeColor": color,
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": None,
"index": "a0",
"roundness": None,
"seed": 1000000000 + hash(label) % 1000000000,
"version": 1,
"versionNonce": 2000000000 + hash(label) % 1000000000,
"isDeleted": False,
"boundElements": [],
"updated": 1738195200000,
"link": None,
"locked": False,
"text": label,
"fontSize": 14,
"fontFamily": 5,
"textAlign": "center",
"verticalAlign": "top",
"containerId": None,
"originalText": label,
"autoResize": True,
"lineHeight": 1.25
}
elements.append(label_element)
return elements
def add_arrow_to_diagram(
diagram_path: Path,
from_x: float,
from_y: float,
to_x: float,
to_y: float,
style: str = "solid",
color: str = "#1e1e1e",
label: str = None
) -> None:
"""
Add an arrow to an Excalidraw diagram.
Args:
diagram_path: Path to the Excalidraw diagram file
from_x: Starting X coordinate
from_y: Starting Y coordinate
to_x: Ending X coordinate
to_y: Ending Y coordinate
style: Line style (solid, dashed, dotted)
color: Arrow color
label: Optional text label
"""
print(f"Creating arrow from ({from_x}, {from_y}) to ({to_x}, {to_y})")
arrow_elements = create_arrow(from_x, from_y, to_x, to_y, style, color, label)
if label:
print(f" With label: '{label}'")
# Load diagram
print(f"Loading diagram: {diagram_path}")
with open(diagram_path, 'r', encoding='utf-8') as f:
diagram = json.load(f)
# Add arrow elements
if 'elements' not in diagram:
diagram['elements'] = []
original_count = len(diagram['elements'])
diagram['elements'].extend(arrow_elements)
print(f" Added {len(arrow_elements)} elements (total: {original_count} -> {len(diagram['elements'])})")
# Save diagram
print(f"Saving diagram")
with open(diagram_path, 'w', encoding='utf-8') as f:
json.dump(diagram, f, indent=2, ensure_ascii=False)
print(f"✓ Successfully added arrow to diagram")
def main():
"""Main entry point."""
if len(sys.argv) < 6:
print("Usage: python add-arrow.py <diagram_path> <from_x> <from_y> <to_x> <to_y> [OPTIONS]")
print("\nOptions:")
print(" --style {solid|dashed|dotted} Line style (default: solid)")
print(" --color HEX Color (default: #1e1e1e)")
print(" --label TEXT Text label on arrow")
print(" --use-edit-suffix Edit via .excalidraw.edit to avoid editor overwrite issues (enabled by default; use --no-use-edit-suffix to disable)")
print("\nExamples:")
print(" python add-arrow.py diagram.excalidraw 300 200 500 300")
print(" python add-arrow.py diagram.excalidraw 300 200 500 300 --label 'HTTP'")
sys.exit(1)
diagram_path = Path(sys.argv[1])
from_x = float(sys.argv[2])
from_y = float(sys.argv[3])
to_x = float(sys.argv[4])
to_y = float(sys.argv[5])
# Parse optional arguments
style = "solid"
color = "#1e1e1e"
label = None
# Default: use edit suffix to avoid editor overwrite issues
use_edit_suffix = True
i = 6
while i < len(sys.argv):
if sys.argv[i] == '--style':
if i + 1 < len(sys.argv):
style = sys.argv[i + 1]
if style not in ['solid', 'dashed', 'dotted']:
print(f"Error: Invalid style '{style}'. Must be: solid, dashed, or dotted")
sys.exit(1)
i += 2
else:
print("Error: --style requires an argument")
sys.exit(1)
elif sys.argv[i] == '--color':
if i + 1 < len(sys.argv):
color = sys.argv[i + 1]
i += 2
else:
print("Error: --color requires an argument")
sys.exit(1)
elif sys.argv[i] == '--label':
if i + 1 < len(sys.argv):
label = sys.argv[i + 1]
i += 2
else:
print("Error: --label requires a text argument")
sys.exit(1)
elif sys.argv[i] == '--use-edit-suffix':
use_edit_suffix = True
i += 1
elif sys.argv[i] == '--no-use-edit-suffix':
use_edit_suffix = False
i += 1
else:
print(f"Error: Unknown option: {sys.argv[i]}")
sys.exit(1)
# Validate inputs
if not diagram_path.exists():
print(f"Error: Diagram file not found: {diagram_path}")
sys.exit(1)
try:
work_path, final_path = prepare_edit_path(diagram_path, use_edit_suffix)
add_arrow_to_diagram(work_path, from_x, from_y, to_x, to_y, style, color, label)
finalize_edit_path(work_path, final_path)
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == '__main__':
main()
add-icon-to-diagram.py 13.1 KB
#!/usr/bin/env python3
"""
Add icons from Excalidraw libraries to diagrams.
This script reads an icon JSON file from an Excalidraw library, transforms its coordinates
to a target position, generates unique IDs, and adds it to an existing Excalidraw diagram.
Works with any Excalidraw library (AWS, GCP, Azure, Kubernetes, etc.).
Usage:
python add-icon-to-diagram.py <diagram_path> <icon_name> <x> <y> [OPTIONS]
Options:
--library-path PATH Path to the icon library directory (default: aws-architecture-icons)
--label TEXT Add a text label below the icon
--use-edit-suffix Edit via .excalidraw.edit to avoid editor overwrite issues (enabled by default; use --no-use-edit-suffix to disable)
Examples:
python add-icon-to-diagram.py diagram.excalidraw EC2 500 300
python add-icon-to-diagram.py diagram.excalidraw EC2 500 300 --label "Web Server"
python add-icon-to-diagram.py diagram.excalidraw VPC 200 150 --library-path libraries/gcp-icons
python add-icon-to-diagram.py diagram.excalidraw EC2 500 300 --use-edit-suffix
"""
import json
import sys
import uuid
from pathlib import Path
from typing import Dict, List, Any, Tuple
def generate_unique_id() -> str:
"""Generate a unique ID for Excalidraw elements."""
return str(uuid.uuid4()).replace('-', '')[:16]
def calculate_bounding_box(elements: List[Dict[str, Any]]) -> Tuple[float, float, float, float]:
"""Calculate the bounding box (min_x, min_y, max_x, max_y) of icon elements."""
if not elements:
return (0, 0, 0, 0)
min_x = float('inf')
min_y = float('inf')
max_x = float('-inf')
max_y = float('-inf')
for element in elements:
if 'x' in element and 'y' in element:
x = element['x']
y = element['y']
width = element.get('width', 0)
height = element.get('height', 0)
min_x = min(min_x, x)
min_y = min(min_y, y)
max_x = max(max_x, x + width)
max_y = max(max_y, y + height)
return (min_x, min_y, max_x, max_y)
def transform_icon_elements(
elements: List[Dict[str, Any]],
target_x: float,
target_y: float
) -> List[Dict[str, Any]]:
"""
Transform icon elements to target coordinates with unique IDs.
Args:
elements: Icon elements from JSON file
target_x: Target X coordinate (top-left position)
target_y: Target Y coordinate (top-left position)
Returns:
Transformed elements with new coordinates and IDs
"""
if not elements:
return []
# Calculate bounding box
min_x, min_y, max_x, max_y = calculate_bounding_box(elements)
# Calculate offset
offset_x = target_x - min_x
offset_y = target_y - min_y
# Create ID mapping: old_id -> new_id
id_mapping = {}
for element in elements:
if 'id' in element:
old_id = element['id']
id_mapping[old_id] = generate_unique_id()
# Create group ID mapping
group_id_mapping = {}
for element in elements:
if 'groupIds' in element:
for old_group_id in element['groupIds']:
if old_group_id not in group_id_mapping:
group_id_mapping[old_group_id] = generate_unique_id()
# Transform elements
transformed = []
for element in elements:
new_element = element.copy()
# Update coordinates
if 'x' in new_element:
new_element['x'] = new_element['x'] + offset_x
if 'y' in new_element:
new_element['y'] = new_element['y'] + offset_y
# Update ID
if 'id' in new_element:
new_element['id'] = id_mapping[new_element['id']]
# Update group IDs
if 'groupIds' in new_element:
new_element['groupIds'] = [
group_id_mapping[gid] for gid in new_element['groupIds']
]
# Update binding references if they exist
if 'startBinding' in new_element and new_element['startBinding']:
if 'elementId' in new_element['startBinding']:
old_id = new_element['startBinding']['elementId']
if old_id in id_mapping:
new_element['startBinding']['elementId'] = id_mapping[old_id]
if 'endBinding' in new_element and new_element['endBinding']:
if 'elementId' in new_element['endBinding']:
old_id = new_element['endBinding']['elementId']
if old_id in id_mapping:
new_element['endBinding']['elementId'] = id_mapping[old_id]
# Update containerId if it exists
if 'containerId' in new_element and new_element['containerId']:
old_id = new_element['containerId']
if old_id in id_mapping:
new_element['containerId'] = id_mapping[old_id]
# Update boundElements if they exist
if 'boundElements' in new_element and new_element['boundElements']:
new_bound_elements = []
for bound_elem in new_element['boundElements']:
if isinstance(bound_elem, dict) and 'id' in bound_elem:
old_id = bound_elem['id']
if old_id in id_mapping:
bound_elem['id'] = id_mapping[old_id]
new_bound_elements.append(bound_elem)
new_element['boundElements'] = new_bound_elements
transformed.append(new_element)
return transformed
def load_icon(icon_name: str, library_path: Path) -> List[Dict[str, Any]]:
"""
Load icon elements from library.
Args:
icon_name: Name of the icon (e.g., "EC2", "VPC")
library_path: Path to the icon library directory
Returns:
List of icon elements
"""
icon_file = library_path / "icons" / f"{icon_name}.json"
if not icon_file.exists():
raise FileNotFoundError(f"Icon file not found: {icon_file}")
with open(icon_file, 'r', encoding='utf-8') as f:
icon_data = json.load(f)
return icon_data.get('elements', [])
def prepare_edit_path(diagram_path: Path, use_edit_suffix: bool) -> tuple[Path, Path | None]:
"""
Prepare a safe edit path to avoid editor overwrite issues.
Returns:
(work_path, final_path)
- work_path: file path to read/write during edit
- final_path: file path to rename back to (or None if not used)
"""
if not use_edit_suffix:
return diagram_path, None
if diagram_path.suffix != ".excalidraw":
return diagram_path, None
edit_path = diagram_path.with_suffix(diagram_path.suffix + ".edit")
if diagram_path.exists():
if edit_path.exists():
raise FileExistsError(f"Edit file already exists: {edit_path}")
diagram_path.rename(edit_path)
return edit_path, diagram_path
def finalize_edit_path(work_path: Path, final_path: Path | None) -> None:
"""Finalize edit by renaming .edit back to .excalidraw if needed."""
if final_path is None:
return
if final_path.exists():
final_path.unlink()
work_path.rename(final_path)
def create_text_label(text: str, x: float, y: float) -> Dict[str, Any]:
"""
Create a text label element.
Args:
text: Label text
x: X coordinate
y: Y coordinate
Returns:
Text element dictionary
"""
return {
"id": generate_unique_id(),
"type": "text",
"x": x,
"y": y,
"width": len(text) * 10, # Approximate width
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": None,
"index": "a0",
"roundness": None,
"seed": 1000000000 + hash(text) % 1000000000,
"version": 1,
"versionNonce": 2000000000 + hash(text) % 1000000000,
"isDeleted": False,
"boundElements": [],
"updated": 1738195200000,
"link": None,
"locked": False,
"text": text,
"fontSize": 16,
"fontFamily": 5, # Excalifont
"textAlign": "center",
"verticalAlign": "top",
"containerId": None,
"originalText": text,
"autoResize": True,
"lineHeight": 1.25
}
def add_icon_to_diagram(
diagram_path: Path,
icon_name: str,
x: float,
y: float,
library_path: Path,
label: str = None
) -> None:
"""
Add an icon to an Excalidraw diagram.
Args:
diagram_path: Path to the Excalidraw diagram file
icon_name: Name of the icon to add
x: Target X coordinate
y: Target Y coordinate
library_path: Path to the icon library directory
label: Optional text label to add below the icon
"""
# Load icon elements
print(f"Loading icon: {icon_name}")
icon_elements = load_icon(icon_name, library_path)
print(f" Loaded {len(icon_elements)} elements")
# Transform icon elements
print(f"Transforming to position ({x}, {y})")
transformed_elements = transform_icon_elements(icon_elements, x, y)
# Calculate icon bounding box for label positioning
if label and transformed_elements:
min_x, min_y, max_x, max_y = calculate_bounding_box(transformed_elements)
icon_width = max_x - min_x
icon_height = max_y - min_y
# Position label below icon, centered
label_x = min_x + (icon_width / 2) - (len(label) * 5)
label_y = max_y + 10
label_element = create_text_label(label, label_x, label_y)
transformed_elements.append(label_element)
print(f" Added label: '{label}'")
# Load diagram
print(f"Loading diagram: {diagram_path}")
with open(diagram_path, 'r', encoding='utf-8') as f:
diagram = json.load(f)
# Add transformed elements
if 'elements' not in diagram:
diagram['elements'] = []
original_count = len(diagram['elements'])
diagram['elements'].extend(transformed_elements)
print(f" Added {len(transformed_elements)} elements (total: {original_count} -> {len(diagram['elements'])})")
# Save diagram
print(f"Saving diagram")
with open(diagram_path, 'w', encoding='utf-8') as f:
json.dump(diagram, f, indent=2, ensure_ascii=False)
print(f"✓ Successfully added '{icon_name}' icon to diagram")
def main():
"""Main entry point."""
if len(sys.argv) < 5:
print("Usage: python add-icon-to-diagram.py <diagram_path> <icon_name> <x> <y> [OPTIONS]")
print("\nOptions:")
print(" --library-path PATH Path to icon library directory")
print(" --label TEXT Add text label below icon")
print(" --use-edit-suffix Edit via .excalidraw.edit to avoid editor overwrite issues (enabled by default; use --no-use-edit-suffix to disable)")
print("\nExamples:")
print(" python add-icon-to-diagram.py diagram.excalidraw EC2 500 300")
print(" python add-icon-to-diagram.py diagram.excalidraw EC2 500 300 --label 'Web Server'")
sys.exit(1)
diagram_path = Path(sys.argv[1])
icon_name = sys.argv[2]
x = float(sys.argv[3])
y = float(sys.argv[4])
# Default library path
script_dir = Path(__file__).parent
default_library_path = script_dir.parent / "libraries" / "aws-architecture-icons"
# Parse optional arguments
library_path = default_library_path
label = None
# Default: use edit suffix to avoid editor overwrite issues
use_edit_suffix = True
i = 5
while i < len(sys.argv):
if sys.argv[i] == '--library-path':
if i + 1 < len(sys.argv):
library_path = Path(sys.argv[i + 1])
i += 2
else:
print("Error: --library-path requires a path argument")
sys.exit(1)
elif sys.argv[i] == '--label':
if i + 1 < len(sys.argv):
label = sys.argv[i + 1]
i += 2
else:
print("Error: --label requires a text argument")
sys.exit(1)
elif sys.argv[i] == '--use-edit-suffix':
use_edit_suffix = True
i += 1
elif sys.argv[i] == '--no-use-edit-suffix':
use_edit_suffix = False
i += 1
else:
print(f"Error: Unknown option: {sys.argv[i]}")
sys.exit(1)
# Validate inputs
if not diagram_path.exists():
print(f"Error: Diagram file not found: {diagram_path}")
sys.exit(1)
if not library_path.exists():
print(f"Error: Library path not found: {library_path}")
sys.exit(1)
try:
work_path, final_path = prepare_edit_path(diagram_path, use_edit_suffix)
add_icon_to_diagram(work_path, icon_name, x, y, library_path, label)
finalize_edit_path(work_path, final_path)
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == '__main__':
main()
split-excalidraw-library.py 5.4 KB
#!/usr/bin/env python3
"""
Excalidraw Library Splitter
This script splits an Excalidraw library file (*.excalidrawlib) into individual
icon JSON files and generates a reference.md file for easy lookup.
The script expects the following structure:
skills/excalidraw-diagram-generator/libraries/{icon-set-name}/
{icon-set-name}.excalidrawlib (place this file first)
Usage:
python split-excalidraw-library.py <path-to-library-directory>
Example:
python split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/
"""
import json
import os
import re
import sys
from pathlib import Path
def sanitize_filename(name: str) -> str:
"""
Sanitize icon name to create a valid filename.
Args:
name: Original icon name
Returns:
Sanitized filename safe for all platforms
"""
# Replace spaces with hyphens
filename = name.replace(' ', '-')
# Remove or replace special characters
filename = re.sub(r'[^\w\-.]', '', filename)
# Remove multiple consecutive hyphens
filename = re.sub(r'-+', '-', filename)
# Remove leading/trailing hyphens
filename = filename.strip('-')
return filename
def find_library_file(directory: Path) -> Path:
"""
Find the .excalidrawlib file in the given directory.
Args:
directory: Directory to search
Returns:
Path to the library file
Raises:
SystemExit: If no library file or multiple library files found
"""
library_files = list(directory.glob('*.excalidrawlib'))
if len(library_files) == 0:
print(f"Error: No .excalidrawlib file found in {directory}")
print(f"Please place a .excalidrawlib file in {directory} first.")
sys.exit(1)
if len(library_files) > 1:
print(f"Error: Multiple .excalidrawlib files found in {directory}")
print(f"Please keep only one library file in {directory}.")
sys.exit(1)
return library_files[0]
def split_library(library_dir: str) -> None:
"""
Split an Excalidraw library file into individual icon files.
Args:
library_dir: Path to the directory containing the .excalidrawlib file
"""
library_dir = Path(library_dir)
if not library_dir.exists():
print(f"Error: Directory not found: {library_dir}")
sys.exit(1)
if not library_dir.is_dir():
print(f"Error: Path is not a directory: {library_dir}")
sys.exit(1)
# Find the library file
library_path = find_library_file(library_dir)
print(f"Found library: {library_path.name}")
# Load library file
print(f"Loading library data...")
with open(library_path, 'r', encoding='utf-8') as f:
library_data = json.load(f)
# Validate library structure
if 'libraryItems' not in library_data:
print("Error: Invalid library file format (missing 'libraryItems')")
sys.exit(1)
# Create icons directory
icons_dir = library_dir / 'icons'
icons_dir.mkdir(exist_ok=True)
print(f"Output directory: {library_dir}")
# Process each library item (icon)
library_items = library_data['libraryItems']
icon_list = []
print(f"Processing {len(library_items)} icons...")
for item in library_items:
# Get icon name
icon_name = item.get('name', 'Unnamed')
# Create sanitized filename
filename = sanitize_filename(icon_name) + '.json'
# Save icon data
icon_path = icons_dir / filename
with open(icon_path, 'w', encoding='utf-8') as f:
json.dump(item, f, ensure_ascii=False, indent=2)
# Add to reference list
icon_list.append({
'name': icon_name,
'filename': filename
})
print(f" ✓ {icon_name} → {filename}")
# Sort icon list by name
icon_list.sort(key=lambda x: x['name'])
# Generate reference.md
library_name = library_path.stem
reference_path = library_dir / 'reference.md'
with open(reference_path, 'w', encoding='utf-8') as f:
f.write(f"# {library_name} Reference\n\n")
f.write(f"This directory contains {len(icon_list)} icons extracted from `{library_path.name}`.\n\n")
f.write("## Available Icons\n\n")
f.write("| Icon Name | Filename |\n")
f.write("|-----------|----------|\n")
for icon in icon_list:
f.write(f"| {icon['name']} | `icons/{icon['filename']}` |\n")
f.write("\n## Usage\n\n")
f.write("Each icon JSON file contains the complete `elements` array needed to render that icon in Excalidraw.\n")
f.write("You can copy the elements from these files into your Excalidraw diagrams.\n")
print(f"\n✅ Successfully split library into {len(icon_list)} icons")
print(f"📄 Reference file created: {reference_path}")
print(f"📁 Icons directory: {icons_dir}")
def main():
"""Main entry point."""
if hasattr(sys.stdout, "reconfigure"):
# Ensure consistent UTF-8 output on Windows consoles.
sys.stdout.reconfigure(encoding="utf-8")
if len(sys.argv) != 2:
print("Usage: python split-excalidraw-library.py <path-to-library-directory>")
print("\nExample:")
print(" python split-excalidraw-library.py skills/excalidraw-diagram-generator/libraries/aws-architecture-icons/")
print("\nNote: The directory should contain a .excalidrawlib file.")
sys.exit(1)
library_dir = sys.argv[1]
split_library(library_dir)
if __name__ == '__main__':
main()
templates/
business-flow-swimlane-template.excalidraw 7.6 KB
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "title",
"type": "text",
"x": 200,
"y": 50,
"width": 300,
"height": 30,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": null,
"seed": 2001001001,
"version": 1,
"versionNonce": 3002002001,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Business Process Flow",
"fontSize": 24,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top"
},
{
"id": "lane-header-1",
"type": "rectangle",
"x": 100,
"y": 120,
"width": 200,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#e7f5ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": null,
"seed": 2001001002,
"version": 1,
"versionNonce": 3002002002,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Customer",
"fontSize": 18,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "lane-1",
"type": "rectangle",
"x": 100,
"y": 170,
"width": 200,
"height": 250,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": null,
"seed": 2001001003,
"version": 1,
"versionNonce": 3002002003,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false
},
{
"id": "process-1",
"type": "rectangle",
"x": 130,
"y": 200,
"width": 140,
"height": 70,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#b2f2bb",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": { "type": 3 },
"seed": 2001001004,
"version": 1,
"versionNonce": 3002002004,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Submit\nRequest",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "lane-header-2",
"type": "rectangle",
"x": 300,
"y": 120,
"width": 200,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#fff3bf",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a4",
"roundness": null,
"seed": 2001001005,
"version": 1,
"versionNonce": 3002002005,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Sales Team",
"fontSize": 18,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "lane-2",
"type": "rectangle",
"x": 300,
"y": 170,
"width": 200,
"height": 250,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a5",
"roundness": null,
"seed": 2001001006,
"version": 1,
"versionNonce": 3002002006,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false
},
{
"id": "process-2",
"type": "rectangle",
"x": 330,
"y": 200,
"width": 140,
"height": 70,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffd43b",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a6",
"roundness": { "type": 3 },
"seed": 2001001007,
"version": 1,
"versionNonce": 3002002007,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Review\nRequest",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "cross-lane-arrow",
"type": "arrow",
"x": 270,
"y": 235,
"width": 60,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a7",
"roundness": { "type": 2 },
"seed": 2001001008,
"version": 1,
"versionNonce": 3002002008,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[60, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "process-3",
"type": "rectangle",
"x": 330,
"y": 310,
"width": 140,
"height": 70,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffd43b",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a8",
"roundness": { "type": 3 },
"seed": 2001001009,
"version": 1,
"versionNonce": 3002002009,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Approve",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "within-lane-arrow",
"type": "arrow",
"x": 400,
"y": 270,
"width": 0,
"height": 40,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a9",
"roundness": { "type": 2 },
"seed": 2001001010,
"version": 1,
"versionNonce": 3002002010,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[0, 40]
],
"startBinding": null,
"endBinding": null
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}
class-diagram-template.excalidraw 12.6 KB
{
"type": "excalidraw",
"version": 2,
"source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor",
"elements": [
{
"id": "class-1",
"type": "rectangle",
"x": 100,
"y": 100,
"width": 200,
"height": 180,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#e7f5ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": null,
"seed": 3001001001,
"version": 1,
"versionNonce": 4002002001,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false
},
{
"id": "class-name-1",
"type": "text",
"x": 150,
"y": 110,
"width": 100,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": null,
"seed": 3001001002,
"version": 1,
"versionNonce": 4002002002,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "User",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "User",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "separator-1",
"type": "line",
"x": 100,
"y": 145,
"width": 200,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": null,
"seed": 3001001003,
"version": 1,
"versionNonce": 4002002003,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
200,
0
]
],
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "attributes-1",
"type": "text",
"x": 110,
"y": 155,
"width": 180,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": null,
"seed": 3001001004,
"version": 1,
"versionNonce": 4002002004,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "- id: number\n- name: string\n- email: string",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "- id: number\n- name: string\n- email: string",
"autoResize": true,
"lineHeight": 1.1904761904761905
},
{
"id": "separator-2",
"type": "line",
"x": 100,
"y": 215,
"width": 200,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a4",
"roundness": null,
"seed": 3001001005,
"version": 1,
"versionNonce": 4002002005,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
200,
0
]
],
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "methods-1",
"type": "text",
"x": 110,
"y": 225,
"width": 180,
"height": 45,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a5",
"roundness": null,
"seed": 3001001006,
"version": 3,
"versionNonce": 1660402375,
"isDeleted": false,
"boundElements": [],
"updated": 1769755991910,
"link": null,
"locked": false,
"text": "+ login(): void\n+ logout(): void\n+ updateProfile(): void",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "+ login(): void\n+ logout(): void\n+ updateProfile(): void",
"autoResize": true,
"lineHeight": 1.0714285714285714
},
{
"id": "class-2",
"type": "rectangle",
"x": 400,
"y": 100,
"width": 200,
"height": 180,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#fff3bf",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a6",
"roundness": null,
"seed": 3001001007,
"version": 1,
"versionNonce": 4002002007,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false
},
{
"id": "class-name-2",
"type": "text",
"x": 430,
"y": 110,
"width": 140,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a7",
"roundness": null,
"seed": 3001001008,
"version": 1,
"versionNonce": 4002002008,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "AdminUser",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "AdminUser",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "separator-3",
"type": "line",
"x": 400,
"y": 145,
"width": 200,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a8",
"roundness": null,
"seed": 3001001009,
"version": 1,
"versionNonce": 4002002009,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
200,
0
]
],
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "attributes-2",
"type": "text",
"x": 410,
"y": 155,
"width": 180,
"height": 35,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a9",
"roundness": null,
"seed": 3001001010,
"version": 1,
"versionNonce": 4002002010,
"isDeleted": false,
"boundElements": [],
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "- role: string\n- permissions: string[]",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "- role: string\n- permissions: string[]",
"autoResize": true,
"lineHeight": 1.25
},
{
"id": "separator-4",
"type": "line",
"x": 400,
"y": 200,
"width": 200,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aA",
"roundness": null,
"seed": 3001001011,
"version": 2,
"versionNonce": 873024679,
"isDeleted": false,
"boundElements": [],
"updated": 1769755880046,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
200,
0
]
],
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "methods-2",
"type": "text",
"x": 410,
"y": 210,
"width": 180,
"height": 60,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aB",
"roundness": null,
"seed": 3001001012,
"version": 2,
"versionNonce": 1702655305,
"isDeleted": false,
"boundElements": [],
"updated": 1769755880046,
"link": null,
"locked": false,
"text": "+ manageUsers(): void\n+ assignRole(): void\n+ revokePermission(): void",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "+ manageUsers(): void\n+ assignRole(): void\n+ revokePermission(): void",
"autoResize": true,
"lineHeight": 1.4285714285714286
},
{
"id": "inheritance-line",
"type": "line",
"x": 400,
"y": 190,
"width": 100,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aC",
"roundness": null,
"seed": 3001001013,
"version": 18,
"versionNonce": 1139021225,
"isDeleted": false,
"boundElements": [],
"updated": 1769755989350,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
-100,
0
]
],
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null
},
{
"id": "inheritance-triangle",
"type": "line",
"x": 314.1999816894531,
"y": 181.5,
"width": 15,
"height": 15,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "aD",
"roundness": null,
"seed": 3001001014,
"version": 21,
"versionNonce": 1468657767,
"isDeleted": false,
"boundElements": [],
"updated": 1769756005117,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
-15,
15
],
[
0,
15
],
[
0,
0
]
],
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null
}
],
"appState": {
"gridSize": 20,
"gridStep": 5,
"gridModeEnabled": false,
"viewBackgroundColor": "#ffffff"
},
"files": {}
} data-flow-diagram-template.excalidraw 6.3 KB
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "external-entity-1",
"type": "rectangle",
"x": 100,
"y": 200,
"width": 120,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffc9c9",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": { "type": 3 },
"seed": 1001001001,
"version": 1,
"versionNonce": 2002002002,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "User",
"fontSize": 18,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "data-flow-1",
"type": "arrow",
"x": 220,
"y": 240,
"width": 80,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": { "type": 2 },
"seed": 1001001002,
"version": 1,
"versionNonce": 2002002003,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[80, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "flow-label-1",
"type": "text",
"x": 230,
"y": 220,
"width": 80,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": null,
"seed": 1001001003,
"version": 1,
"versionNonce": 2002002004,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "input data",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "process-1",
"type": "ellipse",
"x": 300,
"y": 200,
"width": 120,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#a5d8ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": null,
"seed": 1001001004,
"version": 1,
"versionNonce": 2002002005,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Process\nData",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "data-flow-2",
"type": "arrow",
"x": 420,
"y": 240,
"width": 80,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a4",
"roundness": { "type": 2 },
"seed": 1001001005,
"version": 1,
"versionNonce": 2002002006,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[80, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "flow-label-2",
"type": "text",
"x": 425,
"y": 220,
"width": 100,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a5",
"roundness": null,
"seed": 1001001006,
"version": 1,
"versionNonce": 2002002007,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "processed data",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "data-store-1",
"type": "rectangle",
"x": 500,
"y": 200,
"width": 150,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#96f2d7",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a6",
"roundness": null,
"seed": 1001001007,
"version": 1,
"versionNonce": 2002002008,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Data Store\n(Database)",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "data-store-line",
"type": "line",
"x": 500,
"y": 225,
"width": 150,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a7",
"roundness": null,
"seed": 1001001008,
"version": 1,
"versionNonce": 2002002009,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[150, 0]
],
"startBinding": null,
"endBinding": null
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}
er-diagram-template.excalidraw 15.1 KB
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "entity-1",
"type": "rectangle",
"x": 100,
"y": 150,
"width": 180,
"height": 150,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#e7f5ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": null,
"seed": 5001001001,
"version": 1,
"versionNonce": 6002002001,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false
},
{
"id": "entity-name-1",
"type": "text",
"x": 150,
"y": 160,
"width": 80,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": null,
"seed": 5001001002,
"version": 1,
"versionNonce": 6002002002,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "User",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top"
},
{
"id": "entity-separator-1",
"type": "line",
"x": 100,
"y": 195,
"width": 180,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": null,
"seed": 5001001003,
"version": 1,
"versionNonce": 6002002003,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[180, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "attributes-1",
"type": "text",
"x": 110,
"y": 205,
"width": 160,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": null,
"seed": 5001001004,
"version": 1,
"versionNonce": 6002002004,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "PK: user_id\nname\nemail\ncreated_at",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "entity-2",
"type": "rectangle",
"x": 450,
"y": 150,
"width": 180,
"height": 150,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#fff3bf",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a4",
"roundness": null,
"seed": 5001001005,
"version": 1,
"versionNonce": 6002002005,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false
},
{
"id": "entity-name-2",
"type": "text",
"x": 500,
"y": 160,
"width": 80,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a5",
"roundness": null,
"seed": 5001001006,
"version": 1,
"versionNonce": 6002002006,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Order",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top"
},
{
"id": "entity-separator-2",
"type": "line",
"x": 450,
"y": 195,
"width": 180,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a6",
"roundness": null,
"seed": 5001001007,
"version": 1,
"versionNonce": 6002002007,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[180, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "attributes-2",
"type": "text",
"x": 460,
"y": 205,
"width": 160,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a7",
"roundness": null,
"seed": 5001001008,
"version": 1,
"versionNonce": 6002002008,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "PK: order_id\nFK: user_id\ntotal_amount\norder_date",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "relationship-line",
"type": "line",
"x": 280,
"y": 225,
"width": 170,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a8",
"roundness": null,
"seed": 5001001009,
"version": 1,
"versionNonce": 6002002009,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[170, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "cardinality-1",
"type": "text",
"x": 290,
"y": 205,
"width": 20,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a9",
"roundness": null,
"seed": 5001001010,
"version": 1,
"versionNonce": 6002002010,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "1",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "cardinality-2",
"type": "text",
"x": 420,
"y": 205,
"width": 20,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a10",
"roundness": null,
"seed": 5001001011,
"version": 1,
"versionNonce": 6002002011,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "N",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "relationship-label",
"type": "text",
"x": 330,
"y": 200,
"width": 80,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a11",
"roundness": null,
"seed": 5001001012,
"version": 1,
"versionNonce": 6002002012,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "places",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top"
},
{
"id": "entity-3",
"type": "rectangle",
"x": 450,
"y": 380,
"width": 180,
"height": 120,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#d0f0c0",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a12",
"roundness": null,
"seed": 5001001013,
"version": 1,
"versionNonce": 6002002013,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false
},
{
"id": "entity-name-3",
"type": "text",
"x": 480,
"y": 390,
"width": 120,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a13",
"roundness": null,
"seed": 5001001014,
"version": 1,
"versionNonce": 6002002014,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Product",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top"
},
{
"id": "entity-separator-3",
"type": "line",
"x": 450,
"y": 425,
"width": 180,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a14",
"roundness": null,
"seed": 5001001015,
"version": 1,
"versionNonce": 6002002015,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[180, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "attributes-3",
"type": "text",
"x": 460,
"y": 435,
"width": 160,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a15",
"roundness": null,
"seed": 5001001016,
"version": 1,
"versionNonce": 6002002016,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "PK: product_id\nname\nprice",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "relationship-line-2",
"type": "line",
"x": 540,
"y": 300,
"width": 0,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a16",
"roundness": null,
"seed": 5001001017,
"version": 1,
"versionNonce": 6002002017,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[0, 80]
],
"startBinding": null,
"endBinding": null
},
{
"id": "cardinality-3",
"type": "text",
"x": 550,
"y": 310,
"width": 20,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a17",
"roundness": null,
"seed": 5001001018,
"version": 1,
"versionNonce": 6002002018,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "N",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "cardinality-4",
"type": "text",
"x": 550,
"y": 350,
"width": 20,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a18",
"roundness": null,
"seed": 5001001019,
"version": 1,
"versionNonce": 6002002019,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "M",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "relationship-label-2",
"type": "text",
"x": 490,
"y": 330,
"width": 80,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffffff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a19",
"roundness": null,
"seed": 5001001020,
"version": 1,
"versionNonce": 6002002020,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "contains",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "top"
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}
flowchart-template.excalidraw 4.0 KB
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "step1",
"type": "rectangle",
"x": 400,
"y": 200,
"width": 200,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#b2f2bb",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": { "type": 3 },
"seed": 1234567890,
"version": 1,
"versionNonce": 987654321,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Step 1",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "arrow1",
"type": "arrow",
"x": 500,
"y": 280,
"width": 0,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": { "type": 2 },
"seed": 1234567891,
"version": 1,
"versionNonce": 987654322,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[0, 100]
],
"startBinding": null,
"endBinding": null
},
{
"id": "step2",
"type": "rectangle",
"x": 400,
"y": 380,
"width": 200,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#b2f2bb",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": { "type": 3 },
"seed": 1234567892,
"version": 1,
"versionNonce": 987654323,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Step 2",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "arrow2",
"type": "arrow",
"x": 500,
"y": 460,
"width": 0,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": { "type": 2 },
"seed": 1234567893,
"version": 1,
"versionNonce": 987654324,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[0, 100]
],
"startBinding": null,
"endBinding": null
},
{
"id": "step3",
"type": "rectangle",
"x": 400,
"y": 560,
"width": 200,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#b2f2bb",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a4",
"roundness": { "type": 3 },
"seed": 1234567894,
"version": 1,
"versionNonce": 987654325,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Step 3",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}
mindmap-template.excalidraw 5.3 KB
{
"type": "excalidraw",
"version": 2,
"source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor",
"elements": [
{
"id": "center",
"type": "rectangle",
"x": 500,
"y": 350,
"width": 200,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffd43b",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": {
"type": 3
},
"seed": 3333333333,
"version": 3,
"versionNonce": 641024845,
"isDeleted": false,
"boundElements": [
{
"id": "arrow1",
"type": "arrow"
},
{
"id": "arrow2",
"type": "arrow"
}
],
"updated": 1769755916717,
"link": null,
"locked": false,
"text": "Central Topic",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "branch1",
"type": "rectangle",
"x": 250,
"y": 150,
"width": 150,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#96f2d7",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": {
"type": 3
},
"seed": 3333333334,
"version": 2,
"versionNonce": 2040232045,
"isDeleted": false,
"boundElements": [
{
"id": "arrow1",
"type": "arrow"
}
],
"updated": 1769755912840,
"link": null,
"locked": false,
"text": "Branch 1",
"fontSize": 18,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "arrow1",
"type": "arrow",
"x": 600,
"y": 350,
"width": 246.39999389648438,
"height": 111.20001220703125,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": {
"type": 2
},
"seed": 3333333335,
"version": 23,
"versionNonce": 308894189,
"isDeleted": false,
"boundElements": [],
"updated": 1769755914127,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
-246.39999389648438,
-111.20001220703125
]
],
"startBinding": {
"elementId": "center",
"focus": 0.5255972360761778,
"gap": 1
},
"endBinding": {
"elementId": "branch1",
"focus": 0.48604063201707415,
"gap": 8.79998779296875
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"id": "branch2",
"type": "rectangle",
"x": 750,
"y": 150,
"width": 150,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#96f2d7",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": {
"type": 3
},
"seed": 3333333336,
"version": 2,
"versionNonce": 1459929741,
"isDeleted": false,
"boundElements": [
{
"id": "arrow2",
"type": "arrow"
}
],
"updated": 1769755916716,
"link": null,
"locked": false,
"text": "Branch 2",
"fontSize": 18,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "arrow2",
"type": "arrow",
"x": 600,
"y": 350,
"width": 216,
"height": 112.80001831054688,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a4",
"roundness": {
"type": 2
},
"seed": 3333333337,
"version": 41,
"versionNonce": 1447859213,
"isDeleted": false,
"boundElements": [],
"updated": 1769756030188,
"link": null,
"locked": false,
"points": [
[
0,
0
],
[
216,
-112.80001831054688
]
],
"startBinding": {
"elementId": "center",
"focus": -0.48913039421990545,
"gap": 1
},
"endBinding": {
"elementId": "branch2",
"focus": -0.5368418212214556,
"gap": 7.199981689453125
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
}
],
"appState": {
"gridSize": 20,
"gridStep": 5,
"gridModeEnabled": false,
"viewBackgroundColor": "#ffffff"
},
"files": {}
} relationship-template.excalidraw 3.2 KB
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "entity1",
"type": "rectangle",
"x": 300,
"y": 300,
"width": 180,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#a5d8ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": { "type": 3 },
"seed": 1111111111,
"version": 1,
"versionNonce": 2222222222,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Entity A",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "entity2",
"type": "rectangle",
"x": 600,
"y": 300,
"width": 180,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#a5d8ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": { "type": 3 },
"seed": 1111111112,
"version": 1,
"versionNonce": 2222222223,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Entity B",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "relationship",
"type": "arrow",
"x": 480,
"y": 350,
"width": 120,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": { "type": 2 },
"seed": 1111111113,
"version": 1,
"versionNonce": 2222222224,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[120, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "label",
"type": "text",
"x": 510,
"y": 325,
"width": 60,
"height": 24,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": null,
"seed": 1111111114,
"version": 1,
"versionNonce": 2222222225,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "relates to",
"fontSize": 16,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}
sequence-diagram-template.excalidraw 11.4 KB
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "object-1",
"type": "rectangle",
"x": 150,
"y": 100,
"width": 120,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#e7f5ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": null,
"seed": 4001001001,
"version": 1,
"versionNonce": 5002002001,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Client",
"fontSize": 18,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "lifeline-1",
"type": "line",
"x": 210,
"y": 150,
"width": 0,
"height": 300,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": null,
"seed": 4001001002,
"version": 1,
"versionNonce": 5002002002,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[0, 300]
],
"startBinding": null,
"endBinding": null
},
{
"id": "object-2",
"type": "rectangle",
"x": 350,
"y": 100,
"width": 120,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#fff3bf",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": null,
"seed": 4001001003,
"version": 1,
"versionNonce": 5002002003,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Server",
"fontSize": 18,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "lifeline-2",
"type": "line",
"x": 410,
"y": 150,
"width": 0,
"height": 300,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a3",
"roundness": null,
"seed": 4001001004,
"version": 1,
"versionNonce": 5002002004,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[0, 300]
],
"startBinding": null,
"endBinding": null
},
{
"id": "object-3",
"type": "rectangle",
"x": 550,
"y": 100,
"width": 120,
"height": 50,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#d0f0c0",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a4",
"roundness": null,
"seed": 4001001005,
"version": 1,
"versionNonce": 5002002005,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Database",
"fontSize": 18,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
},
{
"id": "lifeline-3",
"type": "line",
"x": 610,
"y": 150,
"width": 0,
"height": 300,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a5",
"roundness": null,
"seed": 4001001006,
"version": 1,
"versionNonce": 5002002006,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[0, 300]
],
"startBinding": null,
"endBinding": null
},
{
"id": "message-1",
"type": "arrow",
"x": 210,
"y": 200,
"width": 200,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a6",
"roundness": { "type": 2 },
"seed": 4001001007,
"version": 1,
"versionNonce": 5002002007,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[200, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "message-label-1",
"type": "text",
"x": 250,
"y": 180,
"width": 120,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a7",
"roundness": null,
"seed": 4001001008,
"version": 1,
"versionNonce": 5002002008,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "1: request()",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "activation-1",
"type": "rectangle",
"x": 405,
"y": 200,
"width": 10,
"height": 80,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#ffd43b",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a8",
"roundness": null,
"seed": 4001001009,
"version": 1,
"versionNonce": 5002002009,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false
},
{
"id": "message-2",
"type": "arrow",
"x": 415,
"y": 230,
"width": 195,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a9",
"roundness": { "type": 2 },
"seed": 4001001010,
"version": 1,
"versionNonce": 5002002010,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[195, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "message-label-2",
"type": "text",
"x": 450,
"y": 210,
"width": 120,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a10",
"roundness": null,
"seed": 4001001011,
"version": 1,
"versionNonce": 5002002011,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "2: query()",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "return-message-1",
"type": "arrow",
"x": 610,
"y": 250,
"width": 195,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a11",
"roundness": { "type": 2 },
"seed": 4001001012,
"version": 1,
"versionNonce": 5002002012,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[-195, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "return-label-1",
"type": "text",
"x": 450,
"y": 255,
"width": 120,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a12",
"roundness": null,
"seed": 4001001013,
"version": 1,
"versionNonce": 5002002013,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "3: result",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
},
{
"id": "return-message-2",
"type": "arrow",
"x": 410,
"y": 280,
"width": 200,
"height": 0,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a13",
"roundness": { "type": 2 },
"seed": 4001001014,
"version": 1,
"versionNonce": 5002002014,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"points": [
[0, 0],
[-200, 0]
],
"startBinding": null,
"endBinding": null
},
{
"id": "return-label-2",
"type": "text",
"x": 250,
"y": 285,
"width": 120,
"height": 20,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a14",
"roundness": null,
"seed": 4001001015,
"version": 1,
"versionNonce": 5002002015,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "4: response",
"fontSize": 14,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top"
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}
License (MIT)
View full license text
MIT License Copyright GitHub, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.