Installation

Install with CLI Recommended
gh skills-hub install draw-io-diagram-generator

Don't have the extension? Run gh extension install samueltauil/skills-hub first.

Download and extract to your repository:

.github/skills/draw-io-diagram-generator/

Extract the ZIP to .github/skills/ in your repo. The folder name must match draw-io-diagram-generator for Copilot to auto-discover it.

Skill Files (12)

SKILL.md 19.1 KB
---
name: draw-io-diagram-generator
description: Use when creating, editing, or generating draw.io diagram files (.drawio, .drawio.svg, .drawio.png). Covers mxGraph XML authoring, shape libraries, style strings, flowcharts, system architecture, sequence diagrams, ER diagrams, UML class diagrams, network topology, layout strategy, the hediet.vscode-drawio VS Code extension, and the full agent workflow from request to a ready-to-open file.
---

# Draw.io Diagram Generator

This skill enables you to generate, edit, and validate draw.io (`.drawio`) diagram files with
correct mxGraph XML structure. All generated files open immediately in the
[Draw.io VS Code extension](https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio)
(`hediet.vscode-drawio`) without any manual fixes required. You can also open the files in the draw.io web app or desktop app if you prefer.

---

## 1. When to Use This Skill

**Trigger phrases (load this skill when you see these)**

- "create a diagram", "draw a flowchart", "generate an architecture diagram"
- "design a sequence diagram", "make a UML class diagram", "build an ER diagram"
- "add a .drawio file", "update the diagram", "visualise the flow"
- "document the architecture", "show the data model", "diagram the service interactions"
- Any request to produce or modify a `.drawio`, `.drawio.svg`, or `.drawio.png` file

**Supported diagram types**

| Diagram Type | Template Available | Description |
|---|---|---|
| Flowchart | `assets/templates/flowchart.drawio` | Process flows with decisions and branches |
| System Architecture | `assets/templates/architecture.drawio` | Multi-tier / layered service architecture |
| Sequence Diagram | `assets/templates/sequence.drawio` | Actor lifelines and timed message flows |
| ER Diagram | `assets/templates/er-diagram.drawio` | Database tables with relationships |
| UML Class Diagram | `assets/templates/uml-class.drawio` | Classes, interfaces, enums, relationships |
| Network Topology | (use shape library) | Routers, servers, firewalls, subnets |
| BPMN Workflow | (use shape library) | Business process events, tasks, gateways |
| Mind Map | (manual) | Central topic with radiating branches |

---

## 2. Prerequisites

- If running with VS Code integration enabled, Install the drawio extension: **draw.io VS Code extension** β€” `hediet.vscode-drawio` (extension id). Install with:
  ```
  ext install hediet.vscode-drawio
  ```
- **Supported file extensions**: `.drawio`, `.drawio.svg`, `.drawio.png`
- **Python 3.8+** (optional) β€” for the validation and shape-insertion scripts in `scripts/`

---

## 3. Step-by-Step Agent Workflow

Follow these steps in order for every diagram generation task.

### Step 1 β€” Understand the Request

Ask or infer:
1. **Diagram type** β€” What kind of diagram? (flowchart, architecture, UML, ER, sequence, network...)
2. **Entities / actors** β€” What are the main components, actors, classes, or tables?
3. **Relationships** β€” How are they connected? What direction? What cardinality?
4. **Output path** β€” Where should the `.drawio` file be saved?
5. **Existing file** β€” Are we creating new or editing an existing file?

If the request is ambiguous, infer the most sensible diagram type from context (e.g. "show the tables" β†’ ER diagram, "show how the API call flows" β†’ sequence diagram).

### Step 2 β€” Select a Template or Start Fresh

- **Use a template** when the diagram type matches one in `assets/templates/`. Copy the template structure and replace placeholder values.
- **Start fresh** for novel layouts. Begin with the minimal valid skeleton:

```xml
<!-- Set modified="" to the current ISO 8601 timestamp when generating a new file -->
<mxfile host="Electron" modified="" version="26.0.0">
  <diagram id="page-1" name="Page-1">
    <mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1"
                  tooltips="1" connect="1" arrows="1" fold="1"
                  page="1" pageScale="1" pageWidth="1169" pageHeight="827"
                  math="0" shadow="0">
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />
        <!-- Your cells go here -->
      </root>
    </mxGraphModel>
  </diagram>
</mxfile>
```

> **Rule**: ids `0` and `1` are ALWAYS required and must be the first two cells. Never reuse them.

### Step 3 β€” Plan the Layout

Before generating XML, sketch the logical placement:
- Organise into **rows** or **tiers** (use swimlanes for layers)
- **Horizontal spacing**: 40–60px between same-row shapes
- **Vertical spacing**: 80–120px between tier rows
- Standard shape size: `120x60` px for process boxes, `160x80` px for swimlanes
- Default canvas: A4 landscape = `1169 x 827` px

### Step 4 β€” Generate the mxGraph XML

**Vertex cell** (every shape):
```xml
<mxCell id="unique-id" value="Label"
        style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;"
        vertex="1" parent="1">
  <mxGeometry x="100" y="100" width="120" height="60" as="geometry" />
</mxCell>
```

**Edge cell** (every connector):
```xml
<mxCell id="edge-id" value="Label (optional)"
        style="edgeStyle=orthogonalEdgeStyle;html=1;"
        edge="1" source="source-id" target="target-id" parent="1">
  <mxGeometry relative="1" as="geometry" />
</mxCell>
```

**Critical rules**:
- Every cell id must be **globally unique** within the file
- Every vertex must have an `mxGeometry` child with `x`, `y`, `width`, `height`, `as="geometry"`
- Every edge must have `source` and `target` matching existing vertex ids β€” **exception**: floating edges (e.g. sequence diagram lifelines) use `sourcePoint`/`targetPoint` inside `<mxGeometry>` instead; see Β§4 Sequence Diagram
- Every cell's `parent` must reference an existing cell id
- Use `html=1` in style when the label contains HTML (`<b>`, `<i>`, `<br>`)
- Escape XML special characters in labels: `&` => `&amp;`, `<` => `&lt;`, `>` => `&gt;`

### Step 5 β€” Apply Correct Styles

Use the standard semantic color palette for consistency:

| Purpose | fillColor | strokeColor |
|---|---|---|
| Primary / Info | `#dae8fc` | `#6c8ebf` |
| Success / Start | `#d5e8d4` | `#82b366` |
| Warning / Decision | `#fff2cc` | `#d6b656` |
| Error / End | `#f8cecc` | `#b85450` |
| Neutral | `#f5f5f5` | `#666666` |
| External / Partner | `#e1d5e7` | `#9673a6` |

Common style strings by diagram type:

```
# Rounded process box (flowchart)
rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;

# Decision diamond
rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;

# Start/End terminal
ellipse;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;

# Database cylinder
shape=mxgraph.flowchart.database;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;

# Swimlane container (tier)
swimlane;startSize=30;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=1;

# UML class box
swimlane;fontStyle=1;align=center;startSize=40;fillColor=#dae8fc;strokeColor=#6c8ebf;

# Interface / stereotype box
swimlane;fontStyle=3;align=center;startSize=40;fillColor=#f5f5f5;strokeColor=#666666;

# ER table container
shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;

# Orthogonal connector
edgeStyle=orthogonalEdgeStyle;html=1;

# ER relationship (crow's foot)
edgeStyle=entityRelationEdgeStyle;html=1;endArrow=ERmany;startArrow=ERone;
```

> See `references/style-reference.md` for the complete style key catalog and `references/shape-libraries.md` for all shape library names.

### Step 6 β€” Save and Validate

1. **Write the file** to the requested path with `.drawio` extension
2. **Run the validator** (optional but recommended):
   ```bash
   python .github/skills/draw-io-diagram-generator/scripts/validate-drawio.py <path-to-file.drawio>
   ```
3. **Tell the user** how to open the file:
   > "Open `<filename>` in VS Code β€” it will render automatically with the draw.io extension. You can use draw.io's web app or desktop app as well if you prefer."
4. **Provide a brief description** of what is in the diagram so the user knows what to expect.

---

## 4. Diagram-Type Recipes

### Flowchart

Key elements: Start (ellipse) => Process (rounded rectangle) => Decision (diamond) => End (ellipse)

```xml
<!-- Start node -->
<mxCell id="start" value="Start"
        style="ellipse;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;"
        vertex="1" parent="1">
  <mxGeometry x="500" y="80" width="120" height="60" as="geometry" />
</mxCell>

<!-- Process -->
<mxCell id="p1" value="Process Step"
        style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;"
        vertex="1" parent="1">
  <mxGeometry x="500" y="200" width="120" height="60" as="geometry" />
</mxCell>

<!-- Decision -->
<mxCell id="d1" value="Condition?"
        style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;"
        vertex="1" parent="1">
  <mxGeometry x="460" y="320" width="200" height="100" as="geometry" />
</mxCell>

<!-- Arrow: start to p1 -->
<mxCell id="e1" value=""
        style="edgeStyle=orthogonalEdgeStyle;html=1;"
        edge="1" source="start" target="p1" parent="1">
  <mxGeometry relative="1" as="geometry" />
</mxCell>
```

### Architecture Diagram (3-tier)

Use **swimlane containers** for each tier. All service boxes are children of their swimlane.

```xml
<!-- Tier swimlane -->
<mxCell id="tier1" value="Client Layer"
        style="swimlane;startSize=30;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=1;"
        vertex="1" parent="1">
  <mxGeometry x="60" y="100" width="1050" height="130" as="geometry" />
</mxCell>

<!-- Service inside tier (parent="tier1", coords are relative to tier) -->
<mxCell id="webapp" value="Web App"
        style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;"
        vertex="1" parent="tier1">
  <mxGeometry x="80" y="40" width="120" height="60" as="geometry" />
</mxCell>
```

> Connectors between tiers use absolute coordinates with `parent="1"`.

### Sequence Diagram

Key elements: Actors (top), Lifelines (dashed vertical lines), Activation boxes, Message arrows.

- Lifelines: `edge="1"` with `endArrow=none` and `dashed=1`, no source/target β€” use `sourcePoint`/`targetPoint` in geometry
- Synchronous message: `endArrow=block;endFill=1`
- Return message: `endArrow=open;endFill=0;dashed=1`
- Self-call: loop the edge via two Array points to the right and back

**Minimal XML snippet:**

```xml
<!-- Actor (stick figure) -->
<mxCell id="actorA" value="Client"
        style="shape=mxgraph.uml.actor;pointerEvents=1;dashed=0;whiteSpace=wrap;html=1;aspect=fixed;"
        vertex="1" parent="1">
  <mxGeometry x="110" y="80" width="60" height="80" as="geometry" />
</mxCell>

<!-- Service box -->
<mxCell id="actorB" value="API Server"
        style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;"
        vertex="1" parent="1">
  <mxGeometry x="480" y="100" width="160" height="60" as="geometry" />
</mxCell>

<!-- Lifeline β€” floating edge: uses sourcePoint/targetPoint, NOT source/target attributes -->
<mxCell id="lifA" value=""
        style="edgeStyle=none;dashed=1;endArrow=none;"
        edge="1" parent="1">
  <mxGeometry relative="1" as="geometry">
    <mxPoint x="140" y="160" as="sourcePoint" />
    <mxPoint x="140" y="700" as="targetPoint" />
  </mxGeometry>
</mxCell>

<!-- Activation box (thin rectangle on lifeline) -->
<mxCell id="actA1" value=""
        style="fillColor=#dae8fc;strokeColor=#6c8ebf;"
        vertex="1" parent="1">
  <mxGeometry x="130" y="220" width="20" height="180" as="geometry" />
</mxCell>

<!-- Synchronous message -->
<mxCell id="msg1" value="POST /orders"
        style="edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;endArrow=block;endFill=1;"
        edge="1" source="actA1" target="actorB" parent="1">
  <mxGeometry relative="1" as="geometry" />
</mxCell>

<!-- Return message (dashed) -->
<mxCell id="msg2" value="201 Created"
        style="edgeStyle=elbowEdgeStyle;elbow=vertical;dashed=1;html=1;endArrow=open;endFill=0;"
        edge="1" source="actorB" target="actA1" parent="1">
  <mxGeometry relative="1" as="geometry" />
</mxCell>
```

> **Note:** Lifelines are floating edges that use `sourcePoint`/`targetPoint` in `<mxGeometry>` instead of `source`/`target` attributes. This is the standard draw.io pattern for sequence diagrams.

### ER Diagram

Use `shape=table` containers with `childLayout=tableLayout`. Rows are `shape=tableRow` cells with `portConstraint=eastwest`. Columns inside each row are `shape=partialRectangle`.

Relationship arrows use `edgeStyle=entityRelationEdgeStyle`:
- One-to-One: `startArrow=ERone;endArrow=ERone`
- One-to-Many: `startArrow=ERone;endArrow=ERmany`
- Many-to-Many: `startArrow=ERmany;endArrow=ERmany`
- Mandatory: `ERmandOne`, Optional: `ERzeroToOne`

### UML Class Diagram

Class boxes are swimlane containers. Attributes and methods are plain text cells. Dividers are zero-height swimlane children.

Arrow styles by relationship type:

| Relationship | Style String |
|---|---|
| Inheritance (extends) | `edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=0;` |
| Realization (implements) | `edgeStyle=orthogonalEdgeStyle;dashed=1;html=1;endArrow=block;endFill=0;` |
| Composition | `edgeStyle=orthogonalEdgeStyle;html=1;startArrow=diamond;startFill=1;endArrow=none;` |
| Aggregation | `edgeStyle=orthogonalEdgeStyle;html=1;startArrow=diamond;startFill=0;endArrow=none;` |
| Dependency | `edgeStyle=orthogonalEdgeStyle;dashed=1;html=1;endArrow=open;endFill=0;` |
| Association | `edgeStyle=orthogonalEdgeStyle;html=1;endArrow=open;endFill=0;` |

---

## 5. Multi-Page Diagrams

Add multiple `<diagram>` elements for complex systems:

```xml
<mxfile host="Electron" version="26.0.0">
  <diagram id="overview" name="Overview">
    <!-- overview mxGraphModel -->
  </diagram>
  <diagram id="detail" name="Detail View">
    <!-- detail mxGraphModel -->
  </diagram>
</mxfile>
```

Each page has its own independent cell id namespace. The same id value can appear in different pages without conflict.

---

## 6. Editing Existing Diagrams

When modifying an existing `.drawio` file:

1. **Read** the file first to understand existing cell ids, positions, and parent hierarchy
2. **Identify the target diagram page** β€” by index or `name` attribute
3. **Assign new unique ids** that do not collide with existing ids
4. **Respect the container hierarchy** β€” children of a swimlane use coordinates relative to their parent
5. **Verify edges** β€” after repositioning nodes, confirm edge source/target ids remain valid

Use `scripts/add-shape.py` to safely add a single shape without editing raw XML:
```bash
python .github/skills/draw-io-diagram-generator/scripts/add-shape.py docs/arch.drawio "New Service" 700 380
```

---

## 7. Best Practices

**Layout**
- Align shapes to the 10px grid (all coordinates divisible by 10)
- Group related shapes inside swimlane containers
- One diagram topic per page; use multi-page files for complex systems
- Aim for 40 or fewer cells per page for readability

**Labels**
- Add a title text cell (`text;strokeColor=none;fillColor=none;fontSize=18;fontStyle=1`) at top of every page
- Always set `whiteSpace=wrap;html=1` on vertex shapes
- Keep labels concise β€” 3 words or fewer per shape where possible

**Style consistency**
- Use the semantic color palette from Section 3 Step 5 consistently across a project
- Prefer `edgeStyle=orthogonalEdgeStyle` for clean right-angle connectors
- Do not inline arbitrary HTML in labels unless necessary

**File naming**
- Use kebab-case: `order-service-flow.drawio`, `database-schema.drawio`
- Place diagrams alongside the code they document: `docs/` or `architecture/`

---

## 8. Troubleshooting

| Problem | Likely Cause | Fix |
|---|---|---|
| File opens blank in VS Code | Missing id=0 or id=1 cell | Add both root cells before any other cells |
| Shape at wrong position | Child inside container β€” coords are relative | Check `parent`; adjust x/y relative to container |
| Edge not visible | source or target id does not match any vertex | Verify both ids exist exactly as written |
| Diagram shows "Compressed" | mxGraphModel is base64-encoded | Open in draw.io web, File > Export > XML (uncompressed) |
| Shape style not rendering | Typo in shape= name | Check `references/shape-libraries.md` for exact style string |
| Label shows escaped HTML | html=0 on a cell with HTML label | Add `html=1;` to the cell style |
| Container children overlap container edge | Container height too small | Increase container height in mxGeometry |

---

## 9. Validation Checklist

Before delivering any generated `.drawio` file, verify:

- [ ] File starts with `<mxfile>` root element
- [ ] Every `<diagram>` has a non-empty `id` attribute
- [ ] `<mxCell id="0" />` is the first cell in every diagram
- [ ] `<mxCell id="1" parent="0" />` is the second cell in every diagram
- [ ] All cell `id` values are unique within each diagram
- [ ] Every vertex cell has `vertex="1"` and a child `<mxGeometry as="geometry">`
- [ ] Every edge cell has `edge="1"` and either: (a) `source`/`target` pointing to existing vertex ids, or (b) `<mxPoint as="sourcePoint">` and `<mxPoint as="targetPoint">` in its `<mxGeometry>` (floating edge β€” used for sequence diagram lifelines)
- [ ] Every cell (except id=0) has a `parent` pointing to an existing id
- [ ] `html=1` is in the style for any label containing HTML tags
- [ ] XML is well-formed (no unclosed tags, no unescaped `&`, `<`, `>` in attribute values)
- [ ] A title label cell exists at the top of each page

Run the automated validator:
```bash
python .github/skills/draw-io-diagram-generator/scripts/validate-drawio.py <file.drawio>
```

---

## 10. Output Format

When delivering a diagram, always provide:

1. **The `.drawio` file** written to the requested path
2. **A one-sentence summary** of what the diagram shows
3. **How to open it**:
   > "Open `<filename>` in VS Code β€” the draw.io extension will render it automatically. Or you can open it in the draw.io web app or desktop app if you prefer."
4. **How to edit it** (if the user is likely to customise):
   > "Click any shape to select it. Double-click to edit the label. Drag to reposition."
5. **Validation status** β€” whether the validator script was run and passed

---

## 11. References

All companion files are in `.github/skills/draw-io-diagram-generator/`:

| File | Contents |
|---|---|
| `references/drawio-xml-schema.md` | Complete mxfile / mxGraphModel / mxCell attribute reference, coordinate system, reserved cells, validation rules |
| `references/style-reference.md` | All style keys with allowed values, vertex and edge style keys, shape catalog, semantic color palette |
| `references/shape-libraries.md` | All shape library categories (General, Flowchart, UML, ER, Network, BPMN, Mockup, K8s) with style strings |
| `assets/templates/flowchart.drawio` | Ready-to-use flowchart template |
| `assets/templates/architecture.drawio` | 4-tier system architecture template |
| `assets/templates/sequence.drawio` | 3-actor sequence diagram template |
| `assets/templates/er-diagram.drawio` | 3-table ER diagram with crow's foot relationships |
| `assets/templates/uml-class.drawio` | Interface + 2 classes + enum with relationship arrows |
| `scripts/validate-drawio.py` | Python script to validate XML structure of any .drawio file |
| `scripts/add-shape.py` | Python CLI to add a new shape to an existing diagram |
| `scripts/README.md` | How to use the scripts with examples |
assets/templates/
architecture.drawio 7.9 KB
<mxfile host="Electron" modified="2026-03-25T00:00:00.000Z" version="26.0.0">
  <diagram id="architecture" name="Architecture">
    <mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1"
                  tooltips="1" connect="1" arrows="1" fold="1"
                  page="1" pageScale="1" pageWidth="1169" pageHeight="827"
                  math="0" shadow="0">
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />

        <!-- TITLE -->
        <mxCell id="2" value="System Architecture"
                style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontStyle=1;"
                vertex="1" parent="1">
          <mxGeometry x="330" y="30" width="500" height="40" as="geometry" />
        </mxCell>

        <!-- ====== TIER 1: CLIENT LAYER ====== -->
        <mxCell id="tier1" value="Client Layer"
                style="swimlane;startSize=30;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=1;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="60" y="100" width="1050" height="130" as="geometry" />
        </mxCell>

        <!-- NOTE: Cells id="3" and id="4" use Cisco shape library.
             Enable it in VS Code draw.io extension: More Shapes > Networking > Cisco.
             If the library is unavailable, replace with:
             style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" -->
        <mxCell id="3" value="Web Browser"
                style="shape=mxgraph.cisco.computers_and_peripherals.pc;html=1;pointerEvents=1;dashed=0;fillColor=#dae8fc;strokeColor=#6c8ebf;sketch=0;aspect=fixed;"
                vertex="1" parent="tier1">
          <mxGeometry x="60" y="40" width="100" height="70" as="geometry" />
        </mxCell>

        <mxCell id="4" value="Mobile App"
                style="shape=mxgraph.cisco.computers_and_peripherals.mobile_phone;html=1;pointerEvents=1;dashed=0;fillColor=#dae8fc;strokeColor=#6c8ebf;sketch=0;aspect=fixed;"
                vertex="1" parent="tier1">
          <mxGeometry x="220" y="40" width="60" height="80" as="geometry" />
        </mxCell>

        <mxCell id="5" value="Third-party Client"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=12;"
                vertex="1" parent="tier1">
          <mxGeometry x="370" y="50" width="160" height="60" as="geometry" />
        </mxCell>

        <!-- ====== TIER 2: API GATEWAY ====== -->
        <mxCell id="tier2" value="API Gateway / Load Balancer"
                style="swimlane;startSize=30;fillColor=#fff2cc;strokeColor=#d6b656;fontStyle=1;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="60" y="270" width="1050" height="130" as="geometry" />
        </mxCell>

        <mxCell id="6" value="API Gateway"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;"
                vertex="1" parent="tier2">
          <mxGeometry x="200" y="30" width="160" height="60" as="geometry" />
        </mxCell>

        <mxCell id="7" value="Auth / JWT"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;"
                vertex="1" parent="tier2">
          <mxGeometry x="440" y="30" width="160" height="60" as="geometry" />
        </mxCell>

        <mxCell id="8" value="Rate Limiter"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;"
                vertex="1" parent="tier2">
          <mxGeometry x="680" y="30" width="160" height="60" as="geometry" />
        </mxCell>

        <!-- ====== TIER 3: SERVICES ====== -->
        <mxCell id="tier3" value="Services Layer"
                style="swimlane;startSize=30;fillColor=#d5e8d4;strokeColor=#82b366;fontStyle=1;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="60" y="440" width="1050" height="130" as="geometry" />
        </mxCell>

        <mxCell id="9" value="Order Service"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;"
                vertex="1" parent="tier3">
          <mxGeometry x="60" y="30" width="160" height="60" as="geometry" />
        </mxCell>

        <mxCell id="10" value="User Service"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;"
                vertex="1" parent="tier3">
          <mxGeometry x="280" y="30" width="160" height="60" as="geometry" />
        </mxCell>

        <mxCell id="11" value="Notification Service"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;"
                vertex="1" parent="tier3">
          <mxGeometry x="500" y="30" width="160" height="60" as="geometry" />
        </mxCell>

        <mxCell id="12" value="Billing Service"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;"
                vertex="1" parent="tier3">
          <mxGeometry x="720" y="30" width="160" height="60" as="geometry" />
        </mxCell>

        <!-- ====== TIER 4: DATA LAYER ====== -->
        <mxCell id="tier4" value="Data Layer"
                style="swimlane;startSize=30;fillColor=#f8cecc;strokeColor=#b85450;fontStyle=1;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="60" y="610" width="1050" height="130" as="geometry" />
        </mxCell>

        <mxCell id="13" value="Primary DB&#xa;(PostgreSQL)"
                style="shape=mxgraph.flowchart.database;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;"
                vertex="1" parent="tier4">
          <mxGeometry x="100" y="20" width="80" height="80" as="geometry" />
        </mxCell>

        <mxCell id="14" value="Cache&#xa;(Redis)"
                style="shape=mxgraph.flowchart.database;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;"
                vertex="1" parent="tier4">
          <mxGeometry x="320" y="20" width="80" height="80" as="geometry" />
        </mxCell>

        <mxCell id="15" value="Message Queue&#xa;(Kafka)"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;"
                vertex="1" parent="tier4">
          <mxGeometry x="540" y="35" width="160" height="60" as="geometry" />
        </mxCell>

        <mxCell id="16" value="Object Store&#xa;(S3)"
                style="shape=mxgraph.flowchart.stored_data;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;"
                vertex="1" parent="tier4">
          <mxGeometry x="760" y="25" width="100" height="80" as="geometry" />
        </mxCell>

        <!-- INTER-TIER CONNECTORS (tier-level, using absolute coords) -->

        <!-- Client β†’ API Gateway -->
        <mxCell id="c1" value="HTTPS"
                style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;"
                edge="1" source="tier1" target="tier2" parent="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

        <!-- API Gateway β†’ Services -->
        <mxCell id="c2" value="REST / gRPC"
                style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;"
                edge="1" source="tier2" target="tier3" parent="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

        <!-- Services β†’ Data -->
        <mxCell id="c3" value="SQL / Cache / Events"
                style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;"
                edge="1" source="tier3" target="tier4" parent="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

      </root>
    </mxGraphModel>
  </diagram>
</mxfile>
er-diagram.drawio 26.0 KB
<mxfile host="Electron" modified="2026-03-25T00:00:00.000Z" version="26.0.0">
  <diagram id="er-diagram" name="ER Diagram">
    <mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1"
                  tooltips="1" connect="1" arrows="1" fold="1"
                  page="1" pageScale="1" pageWidth="1169" pageHeight="827"
                  math="0" shadow="0">
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />

        <!-- TITLE -->
        <mxCell id="2" value="Entity Relationship Diagram"
                style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontStyle=1;"
                vertex="1" parent="1">
          <mxGeometry x="300" y="20" width="570" height="40" as="geometry" />
        </mxCell>

        <!-- ====== TABLE: users ====== -->
        <!-- Table container -->
        <mxCell id="tbl_users" value="users"
                style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="80" y="120" width="260" height="210" as="geometry" />
        </mxCell>

        <!-- Header row (already part of container via startSize, add column headers) -->
        <mxCell id="tbl_users_r0" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=1;"
                vertex="1" parent="tbl_users">
          <mxGeometry y="30" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="tbl_users_r0_c1" value="PK"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;"
                vertex="1" parent="tbl_users_r0">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r0_c2" value="id (UUID)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r0">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r0_c3" value="NOT NULL"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_users_r0">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_users_r1" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_users">
          <mxGeometry y="60" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="tbl_users_r1_c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r1">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r1_c2" value="email (VARCHAR 255)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r1">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r1_c3" value="UNIQUE"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_users_r1">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_users_r2" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_users">
          <mxGeometry y="90" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="tbl_users_r2_c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r2">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r2_c2" value="name (VARCHAR 100)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r2">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r2_c3" value="NOT NULL"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_users_r2">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_users_r3" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_users">
          <mxGeometry y="120" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="tbl_users_r3_c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r3">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r3_c2" value="created_at (TIMESTAMP)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r3">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r3_c3" value="DEFAULT NOW()"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_users_r3">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_users_r4" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_users">
          <mxGeometry y="150" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="tbl_users_r4_c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r4">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r4_c2" value="updated_at (TIMESTAMP)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r4">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="tbl_users_r4_c3" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_users_r4">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <!-- ====== TABLE: orders ====== -->
        <mxCell id="tbl_orders" value="orders"
                style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="460" y="120" width="260" height="240" as="geometry" />
        </mxCell>

        <mxCell id="tbl_orders_r0" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=1;"
                vertex="1" parent="tbl_orders">
          <mxGeometry y="30" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t2r0c1" value="PK"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;"
                vertex="1" parent="tbl_orders_r0">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r0c2" value="id (UUID)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r0">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r0c3" value="NOT NULL"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_orders_r0">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_orders_r1" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_orders">
          <mxGeometry y="60" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t2r1c1" value="FK"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=2;overflow=hidden;"
                vertex="1" parent="tbl_orders_r1">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r1c2" value="user_id (UUID)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r1">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r1c3" value="NOT NULL"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_orders_r1">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_orders_r2" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_orders">
          <mxGeometry y="90" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t2r2c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r2">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r2c2" value="status (VARCHAR 20)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r2">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r2c3" value="DEFAULT 'pending'"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_orders_r2">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_orders_r3" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_orders">
          <mxGeometry y="120" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t2r3c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r3">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r3c2" value="total (DECIMAL 10,2)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r3">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r3c3" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r3">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_orders_r4" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_orders">
          <mxGeometry y="150" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t2r4c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r4">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r4c2" value="created_at (TIMESTAMP)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_orders_r4">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t2r4c3" value="DEFAULT NOW()"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_orders_r4">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <!-- ====== TABLE: order_items ====== -->
        <mxCell id="tbl_items" value="order_items"
                style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="460" y="430" width="260" height="240" as="geometry" />
        </mxCell>

        <mxCell id="tbl_items_r0" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=1;"
                vertex="1" parent="tbl_items">
          <mxGeometry y="30" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t3r0c1" value="PK"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;"
                vertex="1" parent="tbl_items_r0">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r0c2" value="id (UUID)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_items_r0">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r0c3" value="NOT NULL"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_items_r0">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_items_r1" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_items">
          <mxGeometry y="60" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t3r1c1" value="FK"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=2;overflow=hidden;"
                vertex="1" parent="tbl_items_r1">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r1c2" value="order_id (UUID)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_items_r1">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r1c3" value="NOT NULL"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_items_r1">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_items_r2" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_items">
          <mxGeometry y="90" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t3r2c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_items_r2">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r2c2" value="product_name (VARCHAR)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_items_r2">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r2c3" value="NOT NULL"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_items_r2">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_items_r3" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_items">
          <mxGeometry y="120" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t3r3c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_items_r3">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r3c2" value="quantity (INT)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_items_r3">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r3c3" value="CHECK (&gt; 0)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_items_r3">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <mxCell id="tbl_items_r4" value=""
                style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;top=0;left=0;right=0;bottom=0;"
                vertex="1" parent="tbl_items">
          <mxGeometry y="150" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="t3r4c1" value=""
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_items_r4">
          <mxGeometry width="50" height="30" as="geometry"><mxRectangle width="50" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r4c2" value="unit_price (DECIMAL 10,2)"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
                vertex="1" parent="tbl_items_r4">
          <mxGeometry x="50" width="140" height="30" as="geometry"><mxRectangle width="140" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>
        <mxCell id="t3r4c3" value="NOT NULL"
                style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
                vertex="1" parent="tbl_items_r4">
          <mxGeometry x="190" width="70" height="30" as="geometry"><mxRectangle width="70" height="30" as="alternateBounds" /></mxGeometry>
        </mxCell>

        <!-- ====== RELATIONSHIPS ====== -->

        <!-- users 1 β†’ orders (one-to-many): ERone on users side, ERmanyToOne on orders side -->
        <mxCell id="rel1" value=""
                style="edgeStyle=entityRelationEdgeStyle;html=1;endArrow=ERmany;startArrow=ERone;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;"
                edge="1" source="tbl_users_r1" target="tbl_orders_r1" parent="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

        <!-- orders 1 β†’ order_items (one-to-many) -->
        <mxCell id="rel2" value=""
                style="edgeStyle=entityRelationEdgeStyle;html=1;endArrow=ERmany;startArrow=ERone;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;"
                edge="1" source="tbl_orders_r0" target="tbl_items_r1" parent="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

      </root>
    </mxGraphModel>
  </diagram>
</mxfile>
flowchart.drawio 4.5 KB
<mxfile host="Electron" modified="" version="26.0.0">
    <diagram id="flowchart" name="Flowchart">
        <mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
            <root>
                <mxCell id="0"/>
                <mxCell id="1" parent="0"/>
                <mxCell id="2" value="[Diagram Title]" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontStyle=1;" parent="1" vertex="1">
                    <mxGeometry x="380" y="40" width="400" height="40" as="geometry"/>
                </mxCell>
                <mxCell id="3" value="Start" style="ellipse;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;fontStyle=1;" parent="1" vertex="1">
                    <mxGeometry x="500" y="120" width="160" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="4" value="Step 1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=13;" parent="1" vertex="1">
                    <mxGeometry x="500" y="240" width="160" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="5" value="Step 2" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=13;" parent="1" vertex="1">
                    <mxGeometry x="500" y="360" width="160" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="6" value="Decision?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;" parent="1" vertex="1">
                    <mxGeometry x="480" y="480" width="200" height="100" as="geometry"/>
                </mxCell>
                <mxCell id="7" value="Yes Path" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;" parent="1" vertex="1">
                    <mxGeometry x="720" y="500" width="160" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="8" value="No Path" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=13;" parent="1" vertex="1">
                    <mxGeometry x="280" y="500" width="160" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="9" value="End" style="ellipse;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=13;fontStyle=1;" parent="1" vertex="1">
                    <mxGeometry x="500" y="660" width="160" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="10" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;" parent="1" source="3" target="4" edge="1">
                    <mxGeometry relative="1" as="geometry"/>
                </mxCell>
                <mxCell id="11" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;" parent="1" source="4" target="5" edge="1">
                    <mxGeometry relative="1" as="geometry"/>
                </mxCell>
                <mxCell id="12" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;" parent="1" source="5" target="6" edge="1">
                    <mxGeometry relative="1" as="geometry"/>
                </mxCell>
                <mxCell id="13" value="Yes" style="edgeStyle=orthogonalEdgeStyle;html=1;" parent="1" source="6" target="7" edge="1">
                    <mxGeometry relative="1" as="geometry"/>
                </mxCell>
                <mxCell id="14" value="No" style="edgeStyle=orthogonalEdgeStyle;html=1;" parent="1" source="6" target="8" edge="1">
                    <mxGeometry relative="1" as="geometry"/>
                </mxCell>
                <mxCell id="15" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;" parent="1" source="7" target="9" edge="1">
                    <mxGeometry relative="1" as="geometry">
                        <Array as="points">
                            <mxPoint x="800" y="690"/>
                        </Array>
                    </mxGeometry>
                </mxCell>
                <mxCell id="16" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;" parent="1" source="8" target="9" edge="1">
                    <mxGeometry relative="1" as="geometry">
                        <Array as="points">
                            <mxPoint x="360" y="690"/>
                        </Array>
                    </mxGeometry>
                </mxCell>
            </root>
        </mxGraphModel>
    </diagram>
</mxfile>
sequence.drawio 7.1 KB
<mxfile host="Electron" modified="2026-03-25T00:00:00.000Z" version="26.0.0">
  <diagram id="sequence" name="Sequence Diagram">
    <mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1"
                  tooltips="1" connect="1" arrows="1" fold="1"
                  page="1" pageScale="1" pageWidth="1169" pageHeight="827"
                  math="0" shadow="0">
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />

        <!-- TITLE -->
        <mxCell id="2" value="Sequence Diagram"
                style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontStyle=1;"
                vertex="1" parent="1">
          <mxGeometry x="334" y="20" width="500" height="40" as="geometry" />
        </mxCell>

        <!-- ====== ACTOR BOXES (top row) ====== -->
        <!-- Actor A: Client -->
        <mxCell id="actorA" value="Client"
                style="shape=mxgraph.uml.actor;pointerEvents=1;dashed=0;fillColor=#dae8fc;strokeColor=#6c8ebf;sketch=0;aspect=fixed;"
                vertex="1" parent="1">
          <mxGeometry x="110" y="80" width="60" height="80" as="geometry" />
        </mxCell>

        <!-- Actor B: API Server -->
        <mxCell id="actorB" value="&lt;b&gt;API Server&lt;/b&gt;"
                style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="480" y="100" width="160" height="60" as="geometry" />
        </mxCell>

        <!-- Actor C: Database -->
        <mxCell id="actorC" value="&lt;b&gt;Database&lt;/b&gt;"
                style="shape=mxgraph.flowchart.database;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="870" y="90" width="80" height="80" as="geometry" />
        </mxCell>

        <!-- ====== LIFELINES ====== -->
        <!-- Lifeline A -->
        <mxCell id="lifA" value=""
                style="edgeStyle=none;dashed=1;endArrow=none;entryX=0.5;entryY=0;entryDx=0;entryDy=0;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <Array as="points" />
            <mxPoint x="140" y="160" as="sourcePoint" />
            <mxPoint x="140" y="780" as="targetPoint" />
          </mxGeometry>
        </mxCell>

        <!-- Lifeline B -->
        <mxCell id="lifB" value=""
                style="edgeStyle=none;dashed=1;endArrow=none;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="560" y="160" as="sourcePoint" />
            <mxPoint x="560" y="780" as="targetPoint" />
          </mxGeometry>
        </mxCell>

        <!-- Lifeline C -->
        <mxCell id="lifC" value=""
                style="edgeStyle=none;dashed=1;endArrow=none;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="910" y="170" as="sourcePoint" />
            <mxPoint x="910" y="780" as="targetPoint" />
          </mxGeometry>
        </mxCell>

        <!-- ====== ACTIVATION BOXES (thin rectangles on lifelines) ====== -->
        <!-- Activation on A during request flow -->
        <mxCell id="actA1" value=""
                style="fillColor=#dae8fc;strokeColor=#6c8ebf;"
                vertex="1" parent="1">
          <mxGeometry x="130" y="220" width="20" height="200" as="geometry" />
        </mxCell>

        <!-- Activation on B during processing -->
        <mxCell id="actB1" value=""
                style="fillColor=#fff2cc;strokeColor=#d6b656;"
                vertex="1" parent="1">
          <mxGeometry x="550" y="260" width="20" height="120" as="geometry" />
        </mxCell>

        <!-- Activation on C during DB query -->
        <mxCell id="actC1" value=""
                style="fillColor=#d5e8d4;strokeColor=#82b366;"
                vertex="1" parent="1">
          <mxGeometry x="900" y="300" width="20" height="60" as="geometry" />
        </mxCell>

        <!-- ====== MESSAGE ARROWS ====== -->
        <!-- 1. A β†’ B: Request -->
        <mxCell id="msg1" value="1: POST /api/orders"
                style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=1;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="150" y="240" as="sourcePoint" />
            <mxPoint x="550" y="240" as="targetPoint" />
          </mxGeometry>
        </mxCell>

        <!-- 2. B β†’ B: Validate input (self-message) -->
        <mxCell id="msg2" value="2: Validate &amp; Authenticate"
                style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=1;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="570" y="280" as="sourcePoint" />
            <mxPoint x="570" y="300" as="targetPoint" />
            <Array as="points">
              <mxPoint x="620" y="280" />
              <mxPoint x="620" y="300" />
            </Array>
          </mxGeometry>
        </mxCell>

        <!-- 3. B β†’ C: Query -->
        <mxCell id="msg3" value="3: INSERT orders (...)"
                style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=1;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="570" y="320" as="sourcePoint" />
            <mxPoint x="900" y="320" as="targetPoint" />
          </mxGeometry>
        </mxCell>

        <!-- 4. C β†’ B: Return result -->
        <mxCell id="msg4" value="4: {id: 42, status: created}"
                style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=open;endFill=0;dashed=1;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="900" y="360" as="sourcePoint" />
            <mxPoint x="570" y="360" as="targetPoint" />
          </mxGeometry>
        </mxCell>

        <!-- 5. B β†’ A: Response -->
        <mxCell id="msg5" value="5: 201 Created {orderId: 42}"
                style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=open;endFill=0;dashed=1;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="550" y="410" as="sourcePoint" />
            <mxPoint x="150" y="410" as="targetPoint" />
          </mxGeometry>
        </mxCell>

        <!-- ====== NOTES ====== -->
        <mxCell id="note1" value="&lt;i&gt;Requires Authorization header&lt;/i&gt;"
                style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;fontSize=11;fillColor=#fffacd;strokeColor=#aaa;"
                vertex="1" parent="1">
          <mxGeometry x="200" y="220" width="200" height="40" as="geometry" />
        </mxCell>

        <!-- connector from note to arrow -->
        <mxCell id="noteEdge1" value=""
                style="edgeStyle=none;dashed=1;endArrow=none;"
                edge="1" parent="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="290" y="240" as="sourcePoint" />
            <mxPoint x="350" y="240" as="targetPoint" />
          </mxGeometry>
        </mxCell>

      </root>
    </mxGraphModel>
  </diagram>
</mxfile>
uml-class.drawio 12.7 KB
<mxfile host="Electron" modified="2026-03-25T00:00:00.000Z" version="26.0.0">
  <diagram id="uml-class" name="UML Class Diagram">
    <mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1"
                  tooltips="1" connect="1" arrows="1" fold="1"
                  page="1" pageScale="1" pageWidth="1169" pageHeight="827"
                  math="0" shadow="0">
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />

        <!-- TITLE -->
        <mxCell id="2" value="UML Class Diagram"
                style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontStyle=1;"
                vertex="1" parent="1">
          <mxGeometry x="334" y="20" width="500" height="40" as="geometry" />
        </mxCell>

        <!-- ====== INTERFACE: IOrderRepository ====== -->
        <mxCell id="iface1" value="Β«interfaceΒ»&#xa;IOrderRepository"
                style="swimlane;fontStyle=3;align=center;startSize=40;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="400" y="80" width="260" height="170" as="geometry" />
        </mxCell>
        <mxCell id="iface1_div" value=""
                style="swimlane;startSize=0;fillColor=none;strokeColor=#666666;"
                vertex="1" parent="iface1">
          <mxGeometry y="40" width="260" height="1" as="geometry" />
        </mxCell>
        <mxCell id="iface1_m1" value="+ findById(id: UUID): Order"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="iface1">
          <mxGeometry y="50" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="iface1_m2" value="+ findByUserId(userId: UUID): Order[]"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="iface1">
          <mxGeometry y="80" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="iface1_m3" value="+ save(order: Order): Order"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="iface1">
          <mxGeometry y="110" width="260" height="30" as="geometry" />
        </mxCell>
        <mxCell id="iface1_m4" value="+ delete(id: UUID): void"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="iface1">
          <mxGeometry y="140" width="260" height="30" as="geometry" />
        </mxCell>

        <!-- ====== CLASS: Order ====== -->
        <mxCell id="cls_order" value="Order"
                style="swimlane;fontStyle=1;align=center;startSize=40;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="80" y="370" width="280" height="290" as="geometry" />
        </mxCell>
        <!-- Attributes section divider -->
        <mxCell id="cls_order_d1" value=""
                style="swimlane;startSize=0;fillColor=none;strokeColor=#6c8ebf;"
                vertex="1" parent="cls_order">
          <mxGeometry y="40" width="280" height="1" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_a1" value="- id: UUID"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="50" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_a2" value="- userId: UUID"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="75" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_a3" value="- items: OrderItem[]"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="100" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_a4" value="- status: OrderStatus"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="125" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_a5" value="- total: Decimal"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="150" width="280" height="25" as="geometry" />
        </mxCell>
        <!-- Methods divider -->
        <mxCell id="cls_order_d2" value=""
                style="swimlane;startSize=0;fillColor=none;strokeColor=#6c8ebf;"
                vertex="1" parent="cls_order">
          <mxGeometry y="175" width="280" height="1" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_m1" value="+ addItem(item: OrderItem): void"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="185" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_m2" value="+ removeItem(itemId: UUID): void"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="210" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_m3" value="+ calculateTotal(): Decimal"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="235" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_order_m4" value="+ confirm(): void"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_order">
          <mxGeometry y="260" width="280" height="25" as="geometry" />
        </mxCell>

        <!-- ====== CLASS: OrderItem ====== -->
        <mxCell id="cls_item" value="OrderItem"
                style="swimlane;fontStyle=1;align=center;startSize=40;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="480" y="370" width="280" height="230" as="geometry" />
        </mxCell>
        <mxCell id="cls_item_d1" value=""
                style="swimlane;startSize=0;fillColor=none;strokeColor=#82b366;"
                vertex="1" parent="cls_item">
          <mxGeometry y="40" width="280" height="1" as="geometry" />
        </mxCell>
        <mxCell id="cls_item_a1" value="- id: UUID"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_item">
          <mxGeometry y="50" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_item_a2" value="- productName: string"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_item">
          <mxGeometry y="75" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_item_a3" value="- quantity: int"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_item">
          <mxGeometry y="100" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_item_a4" value="- unitPrice: Decimal"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_item">
          <mxGeometry y="125" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_item_d2" value=""
                style="swimlane;startSize=0;fillColor=none;strokeColor=#82b366;"
                vertex="1" parent="cls_item">
          <mxGeometry y="150" width="280" height="1" as="geometry" />
        </mxCell>
        <mxCell id="cls_item_m1" value="+ subtotal(): Decimal"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_item">
          <mxGeometry y="160" width="280" height="25" as="geometry" />
        </mxCell>
        <mxCell id="cls_item_m2" value="+ validate(): boolean"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="cls_item">
          <mxGeometry y="185" width="280" height="25" as="geometry" />
        </mxCell>

        <!-- ====== ENUM: OrderStatus ====== -->
        <mxCell id="enum1" value="Β«enumerationΒ»&#xa;OrderStatus"
                style="swimlane;fontStyle=3;align=center;startSize=40;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=13;"
                vertex="1" parent="1">
          <mxGeometry x="820" y="370" width="200" height="170" as="geometry" />
        </mxCell>
        <mxCell id="enum1_d1" value=""
                style="swimlane;startSize=0;fillColor=none;strokeColor=#d6b656;"
                vertex="1" parent="enum1">
          <mxGeometry y="40" width="200" height="1" as="geometry" />
        </mxCell>
        <mxCell id="enum1_v1" value="PENDING"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="enum1">
          <mxGeometry y="50" width="200" height="25" as="geometry" />
        </mxCell>
        <mxCell id="enum1_v2" value="CONFIRMED"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="enum1">
          <mxGeometry y="75" width="200" height="25" as="geometry" />
        </mxCell>
        <mxCell id="enum1_v3" value="SHIPPED"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="enum1">
          <mxGeometry y="100" width="200" height="25" as="geometry" />
        </mxCell>
        <mxCell id="enum1_v4" value="DELIVERED"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="enum1">
          <mxGeometry y="125" width="200" height="25" as="geometry" />
        </mxCell>
        <mxCell id="enum1_v5" value="CANCELLED"
                style="text;html=1;align=left;verticalAlign=top;spacingLeft=5;whiteSpace=wrap;overflow=hidden;rotatable=0;"
                vertex="1" parent="enum1">
          <mxGeometry y="150" width="200" height="25" as="geometry" />
        </mxCell>

        <!-- ====== RELATIONSHIPS ====== -->

        <!-- Order REALIZES IOrderRepository (dashed + open triangle) -->
        <mxCell id="rel_realize" value=""
                style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=0;dashed=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;"
                edge="1" source="cls_order" target="iface1" parent="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

        <!-- Order COMPOSITION OrderItem (filled diamond on Order side) -->
        <mxCell id="rel_compose" value="1        *"
                style="edgeStyle=orthogonalEdgeStyle;html=1;startArrow=ERmandOne;endArrow=ERmany;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;"
                edge="1" source="cls_order" target="cls_item" parent="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

        <!-- Order USES OrderStatus (dashed dependency) -->
        <mxCell id="rel_dep" value="Β«useΒ»"
                style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=open;startArrow=none;dashed=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;"
                edge="1" source="cls_order" target="enum1" parent="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>

      </root>
    </mxGraphModel>
  </diagram>
</mxfile>
references/
drawio-xml-schema.md 10.5 KB
# draw.io XML Schema Reference

Complete reference for the `.drawio` file format (mxGraph XML). Use this when generating, parsing, or validating diagram files.

---

## Top-Level Structure

Every `.drawio` file is XML with this root structure:

```xml
<!-- Set modified to the current ISO 8601 timestamp when generating a new file -->
<mxfile host="Electron" modified=""
        agent="draw.io" version="26.0.0" type="device">
  <diagram id="<unique-id>" name="<Page Name>">
    <mxGraphModel ...attributes...>
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />
        <!-- All content cells here -->
      </root>
    </mxGraphModel>
  </diagram>
</mxfile>
```

### `<mxfile>` Attributes

| Attribute | Required | Default | Description |
| ----------- | ---------- | --------- | ------------- |
| `host` | No | `"app.diagrams.net"` | Origin editor (`"Electron"` for desktop/VS Code) |
| `modified` | No | β€” | ISO 8601 timestamp |
| `agent` | No | β€” | User agent string |
| `version` | No | β€” | draw.io version |
| `type` | No | `"device"` | Storage type |

### `<diagram>` Attributes

| Attribute | Required | Description |
| ----------- | ---------- | ------------- |
| `id` | Yes | Unique page identifier (any string) |
| `name` | Yes | Tab label shown in editor |

### `<mxGraphModel>` Attributes

| Attribute | Type | Default | Description |
| ----------- | ------ | --------- | ------------- |
| `dx` | int | `1422` | Scroll X offset |
| `dy` | int | `762` | Scroll Y offset |
| `grid` | `0`/`1` | `1` | Show grid |
| `gridSize` | int | `10` | Grid snap size in px |
| `guides` | `0`/`1` | `1` | Show alignment guides |
| `tooltips` | `0`/`1` | `1` | Enable tooltips |
| `connect` | `0`/`1` | `1` | Enable connection arrows on hover |
| `arrows` | `0`/`1` | `1` | Show directional arrows |
| `fold` | `0`/`1` | `1` | Enable group fold/collapse |
| `page` | `0`/`1` | `1` | Show page boundary |
| `pageScale` | float | `1` | Page zoom scale |
| `pageWidth` | int | `1169` | Page width in px (A4 landscape) |
| `pageHeight` | int | `827` | Page height in px (A4 landscape) |
| `math` | `0`/`1` | `0` | Enable LaTeX math rendering |
| `shadow` | `0`/`1` | `0` | Global shadow on shapes |

**Common page sizes (px at 96dpi):**

| Format | Width | Height |
| -------- | ------- | -------- |
| A4 landscape | `1169` | `827` |
| A4 portrait | `827` | `1169` |
| A3 landscape | `1654` | `1169` |
| Letter landscape | `1100` | `850` |
| Letter portrait | `850` | `1100` |
| Screen (16:9) | `1654` | `931` |

---

## Reserved Cells (Always Required)

```xml
<mxCell id="0" />                 <!-- Root cell β€” never omit, never add attributes -->
<mxCell id="1" parent="0" />     <!-- Default layer β€” all cells are children of this -->
```

These two cells MUST be the first entries inside `<root>`. IDs `0` and `1` are reserved and must not be used for any other cell.

---

## Vertex (Shape) Element

```xml
<mxCell
  id="2"
  value="Label Text"
  style="rounded=1;whiteSpace=wrap;html=1;"
  vertex="1"
  parent="1">
  <mxGeometry x="200" y="160" width="120" height="60" as="geometry" />
</mxCell>
```

### `<mxCell>` Vertex Attributes

| Attribute | Required | Type | Description |
| ----------- | ---------- | ------ | ------------- |
| `id` | Yes | string | Unique identifier within this diagram |
| `value` | Yes | string | Label text (HTML allowed if style has `html=1`) |
| `style` | Yes | string | Semicolon-delimited key=value style string |
| `vertex` | Yes | `"1"` | Must be `"1"` to declare this as a shape |
| `parent` | Yes | string | Parent cell ID (`"1"` for default layer) |

### `<mxGeometry>` Vertex Attributes

| Attribute | Required | Type | Description |
| ----------- | ---------- | ------ | ------------- |
| `x` | Yes | float | Left edge of shape (px from canvas origin) |
| `y` | Yes | float | Top edge of shape (px from canvas origin) |
| `width` | Yes | float | Shape width in px |
| `height` | Yes | float | Shape height in px |
| `as` | Yes | `"geometry"` | Always `"geometry"` |

---

## Edge (Connector) Element

```xml
<mxCell
  id="5"
  value="Label"
  style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;"
  edge="1"
  source="2"
  target="3"
  parent="1">
  <mxGeometry relative="1" as="geometry" />
</mxCell>
```

### `<mxCell>` Edge Attributes

| Attribute | Required | Type | Description |
| ----------- | ---------- | ------ | ------------- |
| `id` | Yes | string | Unique identifier |
| `value` | Yes | string | Connector label (empty string for no label) |
| `style` | Yes | string | Style string (see Edge Styles) |
| `edge` | Yes | `"1"` | Must be `"1"` to declare as connector |
| `source` | No | string | ID of source vertex |
| `target` | No | string | ID of target vertex |
| `parent` | Yes | string | Parent cell ID (usually `"1"`) |

### `<mxGeometry>` Edge Attributes

| Attribute | Required | Type | Description |
| ----------- | ---------- | ------ | ------------- |
| `relative` | No | `"1"` | Always `"1"` for edges |
| `as` | Yes | `"geometry"` | Always `"geometry"` |

### Edge with Label Offset

```xml
<mxGeometry x="-0.1" y="10" relative="1" as="geometry">
  <mxPoint as="offset" />
</mxGeometry>
```

The `x` on relative geometry moves the label along the edge (-1 to 1). `y` is perpendicular offset in px.

### Edge with Manual Waypoints (Control Points)

```xml
<mxGeometry relative="1" as="geometry">
  <Array as="points">
    <mxPoint x="340" y="80" />
    <mxPoint x="340" y="200" />
  </Array>
</mxGeometry>
```

---

## Multi-Page Diagrams

```xml
<mxfile>
  <diagram id="page-1" name="Overview">
    <mxGraphModel>...</mxGraphModel>
  </diagram>
  <diagram id="page-2" name="Detail">
    <mxGraphModel>...</mxGraphModel>
  </diagram>
</mxfile>
```

Each `<diagram>` is a separate page/tab. Cell IDs are scoped to their own `<diagram>` β€” the same ID value can appear in different pages without conflict.

---

## Layer Cells

Layers replace the default `id="1"` layer. Cells are assigned to a layer via `parent`:

```xml
<mxCell id="0" />
<mxCell id="1" value="Background" parent="0" />        <!-- layer 1 -->
<mxCell id="layer2" value="Services" parent="0" />     <!-- layer 2 -->
<mxCell id="layer3" value="Connectors" parent="0" />   <!-- layer 3 -->

<!-- Assign layer via parent attribute -->
<mxCell id="10" value="API" ... parent="layer2">
  <mxGeometry ... />
</mxCell>
```

Toggle layer visibility:

```xml
<mxCell id="layer2" value="Services" parent="0" visible="0" />
```

---

## Swimlane Container

```xml
<!-- Swimlane container -->
<mxCell id="swim1" value="Process" style="shape=pool;startSize=30;horizontal=1;" 
        vertex="1" parent="1">
  <mxGeometry x="40" y="40" width="800" height="340" as="geometry" />
</mxCell>

<!-- Lane 1 (child of swimlane container) -->
<mxCell id="lane1" value="Customer" style="swimlane;startSize=30;" 
        vertex="1" parent="swim1">
  <mxGeometry x="0" y="30" width="800" height="150" as="geometry" />
</mxCell>

<!-- Shape inside lane (child of lane) -->
<mxCell id="step1" value="Place Order" style="rounded=1;whiteSpace=wrap;html=1;" 
        vertex="1" parent="lane1">
  <mxGeometry x="80" y="50" width="120" height="60" as="geometry" />
</mxCell>
```

> **Key**: Cells inside a swimlane have `parent` set to the **lane's ID**, not `"1"`.  
> Coordinates inside lanes are **relative to the lane origin**.

---

## Group Cells

```xml
<!-- Invisible group container -->
<mxCell id="group1" value="" style="group;" vertex="1" parent="1">
  <mxGeometry x="100" y="100" width="300" height="200" as="geometry" />
</mxCell>

<!-- Children relative to group origin -->
<mxCell id="child1" value="A" style="rounded=1;" vertex="1" parent="group1">
  <mxGeometry x="20" y="20" width="100" height="60" as="geometry" />
</mxCell>
```

---

## HTML Labels

When `html=1` is in the style, `value` can contain HTML:

```xml
<mxCell value="&lt;b&gt;OrderService&lt;/b&gt;&lt;br&gt;&lt;i&gt;:8080&lt;/i&gt;"
        style="rounded=1;html=1;" vertex="1" parent="1">
  <mxGeometry x="100" y="100" width="160" height="60" as="geometry" />
</mxCell>
```

HTML must be XML-escaped:

- `<` β†’ `&lt;`
- `>` β†’ `&gt;`
- `&` β†’ `&amp;`
- `"` β†’ `&quot;`

Common HTML tags supported: `<b>`, `<i>`, `<u>`, `<br>`, `<font color="#hex">`, `<span style="...">`, `<hr/>`

---

## Tooltip / Metadata

```xml
<mxCell value="Service Name" tooltip="Handles order processing" style="..." vertex="1" parent="1">
  <mxGeometry ... />
</mxCell>
```

---

## ID Generation Rules

| Rule | Detail |
| ------ | -------- |
| IDs `0` and `1` | Reserved β€” always the root and default layer |
| All other IDs | Must be unique within their `<diagram>` |
| Safe pattern | Sequential integers starting at `2`, or UUID strings |
| Cross-page | IDs do not need to be unique across different `<diagram>` pages |

**Safe sequential ID example:**

```text
id="2", id="3", id="4", ...
```

**UUID-style example:**

```text
id="a1b2c3d4-e5f6-7890-abcd-ef1234567890"
```

---

## Coordinate System

- Origin `(0, 0)` is **top-left** of the canvas
- `x` increases **rightward**
- `y` increases **downward**
- All units are **pixels**

---

## Recommended Spacing

| Context | Value |
| --------- | ------- |
| Minimum gap between shapes | `40px` |
| Comfortable gap | `80px` |
| Swimlane inner padding | `20px` |
| Page margin from edge | `40px` |
| Connector routing clearance | `10px` |

---

## Minimal Valid `.drawio` File

```xml
<mxfile host="Electron" modified="2026-03-25T00:00:00.000Z" version="26.0.0">
  <diagram id="main" name="Page-1">
    <mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1"
                  tooltips="1" connect="1" arrows="1" fold="1"
                  page="1" pageScale="1" pageWidth="1169" pageHeight="827"
                  math="0" shadow="0">
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />
      </root>
    </mxGraphModel>
  </diagram>
</mxfile>
```

---

## Validation Rules

### Must Pass

- [ ] `id="0"` and `id="1"` cells always present as first two children of `<root>`
- [ ] No other cell uses `id="0"` or `id="1"`
- [ ] All `id` values are unique within each `<diagram>`
- [ ] Every `<mxCell>` has exactly one `<mxGeometry>` child
- [ ] `<mxGeometry>` has `as="geometry"` attribute
- [ ] Vertex cells have `vertex="1"`, edge cells have `edge="1"`
- [ ] Edge `source`/`target` IDs reference existing vertex IDs in the same diagram
- [ ] Swimlane children have `parent` set to the swimlane/lane ID, not `"1"`
- [ ] HTML in `value` attributes is XML-escaped

### Recommended

- [ ] Shapes do not overlap unless intentional (use β‰₯40px gap)
- [ ] Edge labels are short (≀4 words)
- [ ] Layer cells have descriptive `value` names
- [ ] All shapes fit within `pageWidth` Γ— `pageHeight` bounds
shape-libraries.md 12.8 KB
# draw.io Shape Libraries

Reference guide for all built-in shape libraries. Enable via `View > Shapes` in the draw.io editor (or VS Code extension shape panel).

---

## Library Catalog

### General

**Enable**: Always active by default

Common shapes for any diagram type.

| Shape | Style Key | Use For |
| ------- | ----------- | --------- |
| Rectangle | *(default)* | Boxes, steps, components |
| Rounded Rectangle | `rounded=1;` | Softer process boxes |
| Ellipse | `ellipse;` | States, start/end |
| Triangle | `triangle;` | Arrows, gates |
| Diamond | `rhombus;` | Decisions |
| Hexagon | `shape=hexagon;` | Labels, tech icons |
| Cloud | `shape=cloud;` | Cloud services |
| Cylinder | `shape=cylinder3;` | Databases |
| Note | `shape=note;` | Annotations |
| Document | `shape=document;` | Files |
| Arrow shapes | Various `mxgraph.arrows2.*` | Flow directions |
| Callouts | `shape=callout;` | Speech bubbles |

---

### Flowchart

**Enable**: `View > Shapes > Flowchart`  
**Shape prefix**: `mxgraph.flowchart.`

Standard ANSI/ISO flowchart symbols.

| Symbol | Style String | ANSI Name |
| -------- | ------------- | ----------- |
| Start / End | `ellipse;` | Terminal |
| Process (rectangle) | `rounded=1;` | Process |
| Decision | `rhombus;` | Decision |
| I/O (parallelogram) | `shape=mxgraph.flowchart.io;` | Data |
| Predefined Process | `shape=mxgraph.flowchart.predefined_process;` | Predefined Process |
| Manual Operation | `shape=mxgraph.flowchart.manual_operation;` | Manual Operation |
| Manual Input | `shape=mxgraph.flowchart.manual_input;` | Manual Input |
| Database | `shape=mxgraph.flowchart.database;` | Direct Access Storage |
| Document | `shape=mxgraph.flowchart.document;` | Document |
| Multiple Documents | `shape=mxgraph.flowchart.multi-document;` | Multiple Documents |
| On-page Connector | `ellipse;` (small, 30Γ—30) | Connector |
| Off-page Connector | `shape=mxgraph.flowchart.off_page_connector;` | Off-page Connector |
| Preparation | `shape=mxgraph.flowchart.preparation;` | Preparation |
| Delay | `shape=mxgraph.flowchart.delay;` | Delay |
| Display | `shape=mxgraph.flowchart.display;` | Display |
| Internal Storage | `shape=mxgraph.flowchart.internal_storage;` | Internal Storage |
| Sort | `shape=mxgraph.flowchart.sort;` | Sort |
| Extract | `shape=mxgraph.flowchart.extract;` | Extract |
| Merge | `shape=mxgraph.flowchart.merge;` | Merge |
| Or | `shape=mxgraph.flowchart.or;` | Or |
| Annotation | `shape=mxgraph.flowchart.annotation;` | Annotation |
| Card | `shape=mxgraph.flowchart.card;` | Punched Card |

**Complete flowchart example style strings:**

```text
Process:          rounded=1;whiteSpace=wrap;html=1;
Decision:         rhombus;whiteSpace=wrap;html=1;
Start/End:        ellipse;whiteSpace=wrap;html=1;
Database:         shape=mxgraph.flowchart.database;whiteSpace=wrap;html=1;
Document:         shape=mxgraph.flowchart.document;whiteSpace=wrap;html=1;
I/O (Data):       shape=mxgraph.flowchart.io;whiteSpace=wrap;html=1;
```

---

### UML

**Enable**: `View > Shapes > UML`

#### Use Case Diagrams

| Shape | Style String |
| ------- | ------------- |
| Actor | `shape=mxgraph.uml.actor;whiteSpace=wrap;html=1;` |
| Use Case (ellipse) | `ellipse;whiteSpace=wrap;html=1;` |
| System Boundary | `swimlane;startSize=30;whiteSpace=wrap;html=1;` |

#### Class Diagrams

Use swimlane containers for class boxes:

```xml
<!-- Class container -->
<mxCell value="Β«interfaceΒ»&#xa;IOrderService" 
        style="swimlane;fontStyle=1;align=center;startSize=30;whiteSpace=wrap;html=1;"
        vertex="1" parent="1">
  <mxGeometry x="200" y="100" width="200" height="160" as="geometry" />
</mxCell>

<!-- Attributes (child of class) -->
<mxCell value="+ id: string&#xa;+ status: string" 
        style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;overflow=hidden;html=1;"
        vertex="1" parent="classId">
  <mxGeometry y="30" width="200" height="60" as="geometry" />
</mxCell>

<!-- Method separator line -->
<mxCell value="" style="line;strokeWidth=1;fillColor=none;" vertex="1" parent="classId">
  <mxGeometry y="90" width="200" height="10" as="geometry" />
</mxCell>

<!-- Methods (child of class) -->
<mxCell value="+ create(): Order&#xa;+ cancel(): void"
        style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;overflow=hidden;html=1;"
        vertex="1" parent="classId">
  <mxGeometry y="100" width="200" height="60" as="geometry" />
</mxCell>
```

#### UML Relationship Arrows

| Relationship | Style String |
| ------------- | ------------- |
| Inheritance (extends) | `edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=0;` |
| Implementation (implements) | `edgeStyle=orthogonalEdgeStyle;dashed=1;html=1;endArrow=block;endFill=0;` |
| Association | `edgeStyle=orthogonalEdgeStyle;html=1;endArrow=open;endFill=0;` |
| Dependency | `edgeStyle=orthogonalEdgeStyle;dashed=1;html=1;endArrow=open;endFill=0;` |
| Aggregation | `edgeStyle=orthogonalEdgeStyle;html=1;startArrow=diamond;startFill=0;endArrow=none;` |
| Composition | `edgeStyle=orthogonalEdgeStyle;html=1;startArrow=diamond;startFill=1;endArrow=none;` |

#### Component Diagram

| Shape | Style String |
| ------- | ------------- |
| Component | `shape=component;align=left;spacingLeft=36;whiteSpace=wrap;html=1;` |
| Interface (lollipop) | `ellipse;whiteSpace=wrap;html=1;aspect=fixed;` (small circle) |
| Port | `shape=mxgraph.uml.port;` |
| Node | `shape=mxgraph.uml.node;whiteSpace=wrap;html=1;` |
| Artifact | `shape=mxgraph.uml.artifact;whiteSpace=wrap;html=1;` |

#### Sequence Diagrams

| Shape | Style String |
| ------- | ------------- |
| Actor | `shape=mxgraph.uml.actor;whiteSpace=wrap;html=1;` |
| Lifeline (object) | `shape=umlLifeline;startSize=40;whiteSpace=wrap;html=1;` |
| Activation box | `shape=umlActivation;whiteSpace=wrap;html=1;` |
| Sync message | `edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;endArrow=block;endFill=1;` |
| Async message | `edgeStyle=elbowEdgeStyle;elbow=vertical;html=1;endArrow=open;endFill=0;` |
| Return | `edgeStyle=elbowEdgeStyle;elbow=vertical;dashed=1;html=1;endArrow=open;endFill=0;` |
| Self-call | `edgeStyle=elbowEdgeStyle;elbow=vertical;exitX=1;exitY=0.3;entryX=1;entryY=0.5;html=1;` |

#### State Diagrams

| Shape | Style String |
| ------- | ------------- |
| Initial state (solid circle) | `ellipse;html=1;aspect=fixed;fillColor=#000000;strokeColor=#000000;` |
| State | `rounded=1;whiteSpace=wrap;html=1;arcSize=50;` |
| Final state | `shape=doubleEllipse;fillColor=#000000;strokeColor=#000000;` |
| Transition | `edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=1;` |
| Fork/Join | `shape=mxgraph.uml.fork_or_join;html=1;fillColor=#000000;` |

---

### Entity Relationship (ER Diagrams)

**Enable**: `View > Shapes > Entity Relation`

#### Modern ER Tables (crow's foot notation)

```xml
<!-- Table container -->
<mxCell id="tbl-orders" value="orders"
        style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=1;"
        vertex="1" parent="1">
  <mxGeometry x="80" y="80" width="240" height="210" as="geometry" />
</mxCell>

<!-- Column row -->
<mxCell id="col-id" value=""
        style="shape=tableRow;horizontal=0;startSize=0;swimmilaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;"
        vertex="1" parent="tbl-orders">
  <mxGeometry y="30" width="240" height="30" as="geometry" />
</mxCell>

<!-- PK marker cell -->
<mxCell value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;"
        vertex="1" parent="col-id">
  <mxGeometry width="40" height="30" as="geometry" />
</mxCell>

<!-- Column name cell -->
<mxCell value="id" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;"
        vertex="1" parent="col-id">
  <mxGeometry x="40" width="140" height="30" as="geometry" />
</mxCell>

<!-- Data type cell -->
<mxCell value="UUID" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;overflow=hidden;fontStyle=2;"
        vertex="1" parent="col-id">
  <mxGeometry x="180" width="60" height="30" as="geometry" />
</mxCell>
```

#### ER Relationship Connectors (crow's foot)

| Cardinality | Style String |
| ------------- | ------------- |
| One-to-one | `edgeStyle=entityRelationEdgeStyle;html=1;startArrow=ERmandOne;endArrow=ERmandOne;startFill=1;endFill=1;` |
| One-to-many | `edgeStyle=entityRelationEdgeStyle;html=1;startArrow=ERmandOne;endArrow=ERmany;startFill=1;endFill=1;` |
| Zero-to-many | `edgeStyle=entityRelationEdgeStyle;html=1;startArrow=ERmandOne;endArrow=ERzeroToMany;startFill=1;endFill=0;` |
| Zero-to-one | `edgeStyle=entityRelationEdgeStyle;html=1;startArrow=ERmandOne;endArrow=ERzeroToOne;startFill=1;endFill=0;` |
| Many-to-many | `edgeStyle=entityRelationEdgeStyle;html=1;startArrow=ERmany;endArrow=ERmany;startFill=1;endFill=1;` |

---

### Network / Infrastructure

**Enable**: `View > Shapes > Networking`

| Shape | Style String |
| ------- | ------------- |
| Generic server | `shape=server;html=1;whiteSpace=wrap;` |
| Web server | `shape=mxgraph.network.web_server;` |
| Database server | `shape=mxgraph.network.database;` |
| Laptop | `shape=mxgraph.network.laptop;` |
| Desktop | `shape=mxgraph.network.desktop;` |
| Mobile phone | `shape=mxgraph.network.mobile;` |
| Router | `shape=mxgraph.cisco.routers.router;` |
| Switch | `shape=mxgraph.cisco.switches.workgroup_switch;` |
| Firewall | `shape=mxgraph.cisco.firewalls.firewall;` |
| Cloud (generic) | `shape=cloud;` |
| Internet | `shape=mxgraph.network.internet;` |
| Load balancer | `shape=mxgraph.network.load_balancer;` |

---

### BPMN 2.0

**Enable**: `View > Shapes > BPMN`  
**Shape prefix**: `shape=mxgraph.bpmn.*`

| Shape | Style String |
| ------- | ------------- |
| Start event | `shape=mxgraph.bpmn.shape;perimeter=mxPerimeter.ellipsePerimeter;symbol=general;verticalLabelPosition=bottom;` |
| End event | `shape=mxgraph.bpmn.shape;perimeter=mxPerimeter.ellipsePerimeter;symbol=terminate;verticalLabelPosition=bottom;` |
| Task | `shape=mxgraph.bpmn.shape;perimeter=mxPerimeter.rectanglePerimeter;symbol=task;` |
| Exclusive gateway | `shape=mxgraph.bpmn.shape;perimeter=mxPerimeter.rhombusPerimeter;symbol=exclusiveGw;` |
| Parallel gateway | `shape=mxgraph.bpmn.shape;perimeter=mxPerimeter.rhombusPerimeter;symbol=parallelGw;` |
| Sub-process | `shape=mxgraph.bpmn.shape;perimeter=mxPerimeter.rectanglePerimeter;symbol=subProcess;` |
| Sequence flow | `edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=1;` |
| Message flow | `edgeStyle=orthogonalEdgeStyle;dashed=1;html=1;endArrow=block;endFill=0;` |
| Pool | `shape=pool;startSize=30;horizontal=1;` |
| Lane | `swimlane;startSize=30;` |

---

### Mockup / Wireframe

**Enable**: `View > Shapes > Mockup`

| Shape | Style String |
| ------- | ------------- |
| Button | `shape=mxgraph.mockup.forms.button;` |
| Input field | `shape=mxgraph.mockup.forms.text1;` |
| Checkbox | `shape=mxgraph.mockup.forms.checkbox;` |
| Dropdown | `shape=mxgraph.mockup.forms.comboBox;` |
| Browser window | `shape=mxgraph.mockup.containers.browser;` |
| Mobile screen | `shape=mxgraph.mockup.containers.smartphone;` |
| List | `shape=mxgraph.mockup.containers.list;` |
| Table | `shape=mxgraph.mockup.containers.table;` |

---

### Kubernetes

**Enable**: `View > Shapes > Kubernetes`

| Resource | Style String |
| ---------- | ------------- |
| Pod | `shape=mxgraph.kubernetes.pod;` |
| Deployment | `shape=mxgraph.kubernetes.deploy;` |
| Service | `shape=mxgraph.kubernetes.svc;` |
| Ingress | `shape=mxgraph.kubernetes.ing;` |
| ConfigMap | `shape=mxgraph.kubernetes.cm;` |
| Secret | `shape=mxgraph.kubernetes.secret;` |
| PersistentVolume | `shape=mxgraph.kubernetes.pv;` |
| Namespace | `shape=mxgraph.kubernetes.ns;` |
| Node | `shape=mxgraph.kubernetes.node;` |

---

## Enabling Libraries in VS Code

Libraries are enabled inside the draw.io editor (which VS Code embeds):

1. Open any `.drawio` or `.drawio.svg` file in VS Code
2. Click the `+` icon in the shape panel (left sidebar) β†’`Search Shapes` or `More Shapes`
3. Check the library you want to activate
4. Shapes appear in the panel for drag-and-drop

Libraries are stored per-user in draw.io settings (not per-project).

---

## Custom Shape Library Creation

A custom library is an XML file with `.xml` extension loaded via `File > Open Library`:

```xml
<mxlibrary>
  [
    {
      "xml": "&lt;mxCell value=\"Component\" style=\"rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;\" vertex=\"1\"&gt;&lt;mxGeometry width=\"120\" height=\"60\" as=\"geometry\" /&gt;&lt;/mxCell&gt;",
      "w": 120,
      "h": 60,
      "aspect": "fixed",
      "title": "My Component"
    }
  ]
</mxlibrary>
```

Each shape entry contains:
- `xml`: XML-escaped cell definition
- `w` / `h`: Default width/height
- `aspect`: `"fixed"` to lock ratio
- `title`: Name shown in panel

    }
  ]
</mxlibrary>
```
style-reference.md 15.1 KB
# draw.io Style Reference

Complete reference for the `style` attribute on `<mxCell>` elements. Styles are semicolon-delimited `key=value` pairs.

---

## Style Format

```text
style="key1=value1;key2=value2;key3=value3;"
```

- Keys and values are case-sensitive
- Trailing semicolon is optional but recommended
- Unknown keys are silently ignored
- Missing keys use draw.io defaults

---

## Universal Style Keys

Apply to all shapes and edges.

| Key | Values | Default | Description |
| ----- | -------- | --------- | ------------- |
| `fillColor` | `#hex` / `none` | `#FFFFFF` | Shape fill color (draw.io default; use semantic palette for project diagrams) |
| `strokeColor` | `#hex` / `none` | `#000000` | Border/line color (draw.io default; use semantic palette for project diagrams) |
| `fontColor` | `#hex` | `#000000` | Text color |
| `fontSize` | integer | `11` | Font size in pt |
| `fontStyle` | bitmask (see below) | `0` | Bold/italic/underline |
| `fontFamily` | string | `Helvetica` | Font family name |
| `align` | `left`/`center`/`right` | `center` | Horizontal text alignment |
| `verticalAlign` | `top`/`middle`/`bottom` | `middle` | Vertical text alignment |
| `opacity` | 0–100 | `100` | Shape opacity (%) |
| `shadow` | `0`/`1` | `0` | Drop shadow |
| `dashed` | `0`/`1` | `0` | Dashed border |
| `dashPattern` | e.g. `8 8` | β€” | Custom dash/gap pattern (px) |
| `strokeWidth` | float | `2` | Border/line width in px |
| `spacing` | integer | `2` | Padding around text (px) |
| `spacingTop` | integer | `0` | Top text padding |
| `spacingBottom` | integer | `0` | Bottom text padding |
| `spacingLeft` | integer | `4` | Left text padding |
| `spacingRight` | integer | `4` | Right text padding |
| `html` | `0`/`1` | `0` | Allow HTML in label |
| `whiteSpace` | `wrap`/`nowrap` | `nowrap` | Text wrapping |
| `overflow` | `visible`/`hidden`/`fill` | `visible` | Text overflow behaviour |
| `rotatable` | `0`/`1` | `1` | Allow rotation in editor |
| `movable` | `0`/`1` | `1` | Allow move in editor |
| `resizable` | `0`/`1` | `1` | Allow resize in editor |
| `deletable` | `0`/`1` | `1` | Allow delete in editor |
| `editable` | `0`/`1` | `1` | Allow label edit in editor |
| `locked` | `0`/`1` | `0` | Lock all editing |
| `nolabel` | `0`/`1` | `0` | Hide label entirely |
| `noLabel` | `0`/`1` | `0` | Alias of `nolabel` |
| `labelPosition` | `left`/`center`/`right` | `center` | Label anchor horizontal |
| `verticalLabelPosition` | `top`/`middle`/`bottom` | `middle` | Label anchor vertical |
| `imageAlign` | `left`/`center`/`right` | `center` | Image alignment |

### `fontStyle` Bitmask Values

| Value | Effect |
| ------- | -------- |
| `0` | Normal |
| `1` | Bold |
| `2` | Italic |
| `4` | Underline |
| `8` | Strikethrough |

Combine by addition: `3` = bold + italic, `5` = bold + underline, `7` = bold + italic + underline.

---

## Shape Keys (Vertex Only)

| Key | Values | Description |
| ----- | -------- | ------------- |
| `shape` | see Shape Catalog | Override default rectangle shape |
| `rounded` | `0`/`1` | Rounded corners on rectangle |
| `arcSize` | 0–50 | Corner radius % (when `rounded=1`) |
| `perimeter` | function name | Connection perimeter type |
| `aspect` | `fixed` | Lock aspect ratio on resize |
| `rotation` | float | Rotation in degrees |
| `fixedSize` | `0`/`1` | Prevent auto-size when editing label |
| `container` | `0`/`1` | Treat shape as container for children |
| `collapsible` | `0`/`1` | Allow collapse/expand toggle |
| `startSize` | integer | Header size in swimlane/container (px) |
| `swimlaneHead` | `0`/`1` | Show swimlane header |
| `swimlaneBody` | `0`/`1` | Show swimlane body |
| `fillOpacity` | 0–100 | Fill-only opacity (independent of `opacity`) |
| `strokeOpacity` | 0–100 | Stroke-only opacity |
| `gradientColor` | `#hex` / `none` | Gradient end color |
| `gradientDirection` | `north`/`south`/`east`/`west` | Gradient direction |
| `sketch` | `0`/`1` | Rough hand-drawn style |
| `comic` | `0`/`1` | Comic/cartoon line style |
| `glass` | `0`/`1` | Glass reflection effect |

---

## Shape Catalog

### Basic Shapes

| Shape | Style String | Visual |
| ------- | ------------- | -------- |
| Rectangle (default) | *(no shape key needed)* | β–‘ |
| Rounded rectangle | `rounded=1;` | β–’ |
| Ellipse / Circle | `ellipse;` | β—‹ |
| Diamond | `rhombus;` | β—‡ |
| Triangle | `triangle;` | β–³ |
| Hexagon | `shape=hexagon;` | ⬑ |
| Pentagon | `shape=mxgraph.basic.pentagon;` | β¬  |
| Star | `shape=mxgraph.basic.star;` | β˜… |
| Cross | `shape=mxgraph.basic.x;` | βœ• |
| Cloud | `shape=cloud;` | ☁ |
| Note / Callout | `shape=note;folded=1;` | πŸ“ |
| Document | `shape=document;` | πŸ“„ |
| Cylinder (database) | `shape=cylinder3;` | πŸ—„ |
| Tape | `shape=tape;` | β€” |
| Parallelogram | `shape=parallelogram;perimeter=parallelogramPerimeter;` | β–± |

### Flowchart Shapes (`mxgraph.flowchart.*`)

| Shape | Style String | Used For |
| ------- | ------------- | ---------- |
| Process | `shape=mxgraph.flowchart.process;` | Standard process |
| Start/End (terminal) | `ellipse;` or `shape=mxgraph.flowchart.terminate;` | Flow start/end |
| Decision | `rhombus;` | Yes/No branch |
| Data (I/O) | `shape=mxgraph.flowchart.io;` | Input/Output |
| Predefined Process | `shape=mxgraph.flowchart.predefined_process;` | Subroutine |
| Manual Input | `shape=mxgraph.flowchart.manual_input;` | Manual entry |
| Manual Operation | `shape=mxgraph.flowchart.manual_operation;` | Manual step |
| Database | `shape=mxgraph.flowchart.database;` | Data store |
| Internal Storage | `shape=mxgraph.flowchart.internal_storage;` | Internal data |
| Direct Data | `shape=mxgraph.flowchart.direct_data;` | Drum storage |
| Document | `shape=mxgraph.flowchart.document;` | Document |
| Multi-document | `shape=mxgraph.flowchart.multi-document;` | Multiple docs |
| On-page Connector | `ellipse;` (small) | Page connector |
| Off-page Connector | `shape=mxgraph.flowchart.off_page_connector;` | Off-page ref |
| Preparation | `shape=mxgraph.flowchart.preparation;` | Initialization |
| Delay | `shape=mxgraph.flowchart.delay;` | Wait state |
| Display | `shape=mxgraph.flowchart.display;` | Output display |
| Sort | `shape=mxgraph.flowchart.sort;` | Sort operation |
| Extract | `shape=mxgraph.flowchart.extract;` | Extract operation |
| Merge | `shape=mxgraph.flowchart.merge;` | Merge paths |
| Or | `shape=mxgraph.flowchart.or;` | OR gate |
| And | `shape=mxgraph.flowchart.and;` | AND gate |
| Annotation | `shape=mxgraph.flowchart.annotation;` | Comment/note |

### UML Shapes (`mxgraph.uml.*`)

| Shape | Style String | Used For |
| ------- | ------------- | ---------- |
| Actor | `shape=mxgraph.uml.actor;` | Use-case actor |
| Boundary | `shape=mxgraph.uml.boundary;` | System boundary |
| Control | `shape=mxgraph.uml.control;` | Controller object |
| Entity | `shape=mxgraph.uml.entity;` | Entity object |
| Component | `shape=component;` | Component box |
| Package | `shape=mxgraph.uml.package;` | Package |
| Note | `shape=note;` | UML note |
| Lifeline | `shape=umlLifeline;startSize=40;` | Sequence lifeline |
| Activation | `shape=umlActivation;` | Activation box |
| Destroy | `shape=mxgraph.uml.destroy;` | Destroy marker |
| State | `ellipse;` | State node |
| Initial State | `ellipse;fillColor=#000000;` | UML initial state |
| Final State | `shape=doubleEllipse;fillColor=#000000;` | UML final state |
| Fork/Join | `shape=mxgraph.uml.fork_or_join;` | Fork/join bar |

### Network Shapes (`mxgraph.network.*`)

| Shape | Style String |
| ------- | ------------- |
| Server | `shape=server;` |
| Database server | `shape=mxgraph.network.database;` |
| Firewall | `shape=mxgraph.cisco.firewalls.firewall;` |
| Router | `shape=mxgraph.cisco.routers.router;` |
| Switch | `shape=mxgraph.cisco.switches.workgroup_switch;` |
| Cloud | `shape=cloud;` |
| Internet | `shape=mxgraph.network.internet;` |
| Laptop | `shape=mxgraph.network.laptop;` |
| Desktop | `shape=mxgraph.network.desktop;` |
| Mobile | `shape=mxgraph.network.mobile;` |

### AWS Shapes (`mxgraph.aws4.*`)

Use the AWS4 library. Common shapes:

| Shape | Style String |
| ------- | ------------- |
| EC2 | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;` |
| Lambda | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.lambda;` |
| S3 | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.s3;` |
| RDS | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.rds;` |
| API Gateway | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.api_gateway;` |
| CloudFront | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.cloudfront;` |
| Load Balancer | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.elb;` |
| SQS | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.sqs;` |
| SNS | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.sns;` |
| DynamoDB | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.dynamodb;` |
| ECS | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ecs;` |
| EKS | `shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.eks;` |
| VPC | `shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_vpc;` |
| Region | `shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_region;` |

### Azure Shapes (`mxgraph.azure.*`)

| Shape | Style String |
| ------- | ------------- |
| App Service | `shape=mxgraph.azure.app_service;` |
| Function App | `shape=mxgraph.azure.function_apps;` |
| SQL Database | `shape=mxgraph.azure.sql_database;` |
| Blob Storage | `shape=mxgraph.azure.blob_storage;` |
| API Management | `shape=mxgraph.azure.api_management;` |
| Service Bus | `shape=mxgraph.azure.service_bus;` |
| AKS | `shape=mxgraph.azure.aks;` |
| Container Registry | `shape=mxgraph.azure.container_registry_registries;` |

### GCP Shapes (`mxgraph.gcp2.*`)

| Shape | Style String |
| ------- | ------------- |
| Cloud Run | `shape=mxgraph.gcp2.cloud_run;` |
| Cloud Functions | `shape=mxgraph.gcp2.cloud_functions;` |
| Cloud SQL | `shape=mxgraph.gcp2.cloud_sql;` |
| Cloud Storage | `shape=mxgraph.gcp2.cloud_storage;` |
| GKE | `shape=mxgraph.gcp2.container_engine;` |
| Pub/Sub | `shape=mxgraph.gcp2.cloud_pubsub;` |
| BigQuery | `shape=mxgraph.gcp2.bigquery;` |

---

## Edge Style Keys

| Key | Values | Description |
| ----- | -------- | ------------- |
| `edgeStyle` | see below | Connection routing algorithm |
| `rounded` | `0`/`1` | Rounded corners on orthogonal edges |
| `curved` | `0`/`1` | Curved line segments |
| `orthogonal` | `0`/`1` | Force orthogonal routing |
| `jettySize` | `auto`/integer | Source/target jet size |
| `exitX` | 0.0–1.0 | Source exit point X (0=left, 0.5=center, 1=right) |
| `exitY` | 0.0–1.0 | Source exit point Y (0=top, 0.5=center, 1=bottom) |
| `exitDx` | float | Source exit X offset (px) |
| `exitDy` | float | Source exit Y offset (px) |
| `entryX` | 0.0–1.0 | Target entry point X |
| `entryY` | 0.0–1.0 | Target entry point Y |
| `entryDx` | float | Target entry X offset (px) |
| `entryDy` | float | Target entry Y offset (px) |
| `endArrow` | see Arrow Types | Arrow head at target |
| `startArrow` | see Arrow Types | Arrow tail at source |
| `endFill` | `0`/`1` | Filled end arrow head |
| `startFill` | `0`/`1` | Filled start arrow head |
| `endSize` | integer | End arrow head size (px) |
| `startSize` | integer | Start arrow head size (px) |
| `labelBackgroundColor` | `#hex`/`none` | Label background fill |
| `labelBorderColor` | `#hex`/`none` | Label border color |

### `edgeStyle` Values

| Value | Routing | Use When |
| ------- | --------- | ---------- |
| `none` | Straight line | Simple direct connections |
| `orthogonalEdgeStyle` | Right-angle turns | Flowcharts, architecture |
| `elbowEdgeStyle` | Single elbow | Clean directional diagrams |
| `entityRelationEdgeStyle` | ER-style routing | ER diagrams |
| `segmentEdgeStyle` | Segmented with handles | Fine-tuned routing |
| `isometricEdgeStyle` | Isometric grid | Isometric diagrams |

### Arrow Types (`endArrow` / `startArrow`)

| Value | Shape | Use For |
| ------- | ------- | --------- |
| `block` | Filled triangle | Standard directed arrow |
| `open` | Open chevron β†’ | Open/light arrow |
| `classic` | Classic arrow | Default draw.io arrow |
| `classicThin` | Thin classic | Compact diagrams |
| `none` | No arrowhead | Undirected lines |
| `oval` | Circle dot | Aggregation start |
| `diamond` | Hollow diamond | Aggregation |
| `diamondThin` | Thin diamond | Slim diagrams |
| `ERone` | `\|` bar | ER cardinality "one" |
| `ERmany` | Crow's foot | ER cardinality "many" |
| `ERmandOne` | `\|\|` | ER mandatory one |
| `ERzeroToOne` | `o\|` | ER zero-or-one |
| `ERzeroToMany` | `o<` | ER zero-or-many |
| `ERoneToMany` | `\|<` | ER one-or-many |

---

## Color Palette

### Semantic Colors (Recommended for Consistent Diagrams)

| Meaning | Fill | Stroke | Usage |
| --------- | ------ | -------- | ------- |
| User / Client | `#dae8fc` | `#6c8ebf` | Browser, client apps |
| Service / Process | `#d5e8d4` | `#82b366` | Backend services |
| Database / Storage | `#f5f5f5` | `#666666` | Databases, files |
| Decision / Warning | `#fff2cc` | `#d6b656` | Decision nodes, alerts |
| Error / Critical | `#f8cecc` | `#b85450` | Error paths, critical |
| External / Partner | `#e1d5e7` | `#9673a6` | 3rd party, external |
| Queue / Async | `#ffe6cc` | `#d79b00` | Message queues |
| Gateway / Proxy | `#dae8fc` | `#0050ef` | API gateways, proxies |

### Dark Background Shapes

For dark-themed diagrams, swap to:

- Fill: `#1e4d78` (dark blue), `#1a4731` (dark green)
- Stroke: `#4aa3df`, `#67ab9f`
- Font: `#ffffff`

---

## Complete Style Examples

### Rounded Blue Box

```text
rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;
```

### Green Process Step

```text
rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;
```

### Yellow Decision Diamond

```text
rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;
```

### Red Error Box

```text
rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;
```

### Database Cylinder

```text
shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;fillColor=#f5f5f5;strokeColor=#666666;
```

### Swimlane Container

```text
shape=pool;startSize=30;horizontal=1;fillColor=#f5f5f5;strokeColor=#999999;
```

### Swimlane Lane

```text
swimlane;startSize=30;fillColor=#ffffff;strokeColor=#999999;
```

### Orthogonal Connector

```text
edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;
```

### Directed Arrow (bold)

```text
edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;endArrow=block;endFill=1;strokeWidth=2;
```

### Dashed Dependency Line

```text
edgeStyle=orthogonalEdgeStyle;dashed=1;endArrow=open;endFill=0;strokeColor=#666666;
```

### ER Relationship Line (one-to-many)

```text
edgeStyle=entityRelationEdgeStyle;html=1;endArrow=ERmany;startArrow=ERmandOne;endFill=1;startFill=1;
```

### UML Inheritance Arrow (hollow triangle)

```text
edgeStyle=orthogonalEdgeStyle;html=1;endArrow=block;endFill=0;
```

### UML Composition (filled diamond)

```text
edgeStyle=orthogonalEdgeStyle;html=1;startArrow=diamond;startFill=1;endArrow=none;
```

### UML Aggregation (open diamond)

```text
edgeStyle=orthogonalEdgeStyle;html=1;startArrow=diamond;startFill=0;endArrow=none;
```

### UML Dependency (dashed arrow)

```text
edgeStyle=orthogonalEdgeStyle;dashed=1;html=1;endArrow=open;endFill=0;
```

### Invisible connector (for alignment)

```text
edgeStyle=none;strokeColor=none;endArrow=none;
```
scripts/
README.md 3.3 KB
# draw-io Scripts

Utility scripts for working with `.drawio` diagram files in the cxp-bu-order-ms project.

## Requirements

- Python 3.8+
- No external dependencies (uses standard library only: `xml.etree.ElementTree`, `argparse`, `json`, `sys`, `pathlib`)

## Scripts

### `validate-drawio.py`

Validates the XML structure of a `.drawio` file against required constraints.

**Usage**

```bash
python scripts/validate-drawio.py <path-to-diagram.drawio>
```

**Examples**

```bash
# Validate a single file
python scripts/validate-drawio.py docs/architecture.drawio

# Validate all drawio files in a directory
for f in docs/**/*.drawio; do python scripts/validate-drawio.py "$f"; done
```

**Checks performed**

| Check | Description |
|-------|-------------|
| Root cells | Verifies id="0" and id="1" cells are present in every diagram page |
| Unique IDs | All `mxCell` id values are unique within a diagram |
| Edge connectivity | Every edge has valid `source` and `target` attributes pointing to existing cells |
| Geometry | Every vertex cell has an `mxGeometry` child element |
| Parent chain | Every cell's `parent` attribute references an existing cell id |
| XML well-formedness | File is valid XML |

**Exit codes**

- `0` β€” Validation passed
- `1` β€” One or more validation errors found (errors printed to stdout)

---

### `add-shape.py`

Adds a new shape (vertex cell) to an existing `.drawio` diagram file.

**Usage**

```bash
python scripts/add-shape.py <diagram.drawio> <label> <x> <y> [options]
```

**Arguments**

| Argument | Required | Description |
|----------|----------|-------------|
| `diagram` | Yes | Path to the `.drawio` file |
| `label` | Yes | Text label for the new shape |
| `x` | Yes | X coordinate (pixels from top-left) |
| `y` | Yes | Y coordinate (pixels from top-left) |

**Options**

| Option | Default | Description |
|--------|---------|-------------|
| `--width` | `120` | Shape width in pixels |
| `--height` | `60` | Shape height in pixels |
| `--style` | `"rounded=1;whiteSpace=wrap;html=1;"` | draw.io style string |
| `--diagram-index` | `0` | Index of the diagram page (0-based) |
| `--dry-run` | false | Print the new cell XML without modifying the file |

**Examples**

```bash
# Add a basic rounded box
python scripts/add-shape.py docs/flowchart.drawio "New Step" 400 300

# Add a custom styled shape
python scripts/add-shape.py docs/flowchart.drawio "Decision" 400 400 \
  --width 160 --height 80 \
  --style "rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;"

# Preview without writing
python scripts/add-shape.py docs/architecture.drawio "Service X" 600 200 --dry-run
```

**Output**

Prints the new cell id on success:
```
Added shape id="auto_abc123" to page 0 of docs/flowchart.drawio
```

---

## Common Workflows

### Validate before committing

```bash
# Validate all diagrams
find . -name "*.drawio" -not -path "*/node_modules/*" | \
  xargs -I{} python scripts/validate-drawio.py {}
```

### Quickly add a placeholder node

```bash
python scripts/add-shape.py docs/architecture.drawio "TODO: Service" 800 400 \
  --style "rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;"
```

### Check a template is valid

```bash
python scripts/validate-drawio.py .github/skills/draw-io-diagram-generator/templates/flowchart.drawio
```
add-shape.py 6.5 KB
#!/usr/bin/env python3
"""
add-shape.py β€” Add a new vertex shape to an existing .drawio diagram file.

Usage:
    python scripts/add-shape.py <diagram.drawio> <label> <x> <y> [options]

Examples:
    python scripts/add-shape.py docs/flowchart.drawio "New Step" 400 300
    python scripts/add-shape.py docs/arch.drawio "Decision" 400 400 \\
        --width 160 --height 80 \\
        --style "rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;"
    python scripts/add-shape.py docs/arch.drawio "Preview Node" 200 200 --dry-run
"""
from __future__ import annotations

import argparse
import hashlib
import sys
import time
import xml.etree.ElementTree as ET
from pathlib import Path


DEFAULT_STYLE = "rounded=1;whiteSpace=wrap;html=1;"


def _indent_xml(elem: ET.Element, level: int = 0) -> None:
    """Indent XML tree in-place. Replaces ET.indent() for Python 3.8 compatibility."""
    indent = "\n" + "  " * level
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = indent + "  "
        if not elem.tail or not elem.tail.strip():
            elem.tail = indent
        for child in elem:
            _indent_xml(child, level + 1)
        # last child tail
        if not child.tail or not child.tail.strip():
            child.tail = indent
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = indent
    if not level:
        elem.tail = "\n"


def _generate_id(label: str, x: int, y: int) -> str:
    """Generate a short deterministic-ish id based on label + position + time."""
    seed = f"{label}:{x}:{y}:{time.time_ns()}"
    return "auto_" + hashlib.sha1(seed.encode()).hexdigest()[:8]


def add_shape(
    path: Path,
    label: str,
    x: int,
    y: int,
    width: int = 120,
    height: int = 60,
    style: str = DEFAULT_STYLE,
    diagram_index: int = 0,
    dry_run: bool = False,
) -> int:
    """
    Parse the .drawio file, insert a new vertex cell into the specified diagram page,
    and write the file back (unless dry_run is True).

    Returns:
        0 on success, 1 on failure.
    """
    # Preserve the original XML declaration / indentation by writing raw bytes.
    ET.register_namespace("", "")

    try:
        tree = ET.parse(path)
    except ET.ParseError as exc:
        print(f"ERROR: XML parse error in '{path}': {exc}")
        return 1

    mxfile = tree.getroot()
    if mxfile.tag != "mxfile":
        print(f"ERROR: Root element must be <mxfile>, got <{mxfile.tag}>")
        return 1

    diagrams = mxfile.findall("diagram")
    if diagram_index >= len(diagrams):
        print(
            f"ERROR: diagram-index {diagram_index} is out of range "
            f"(file has {len(diagrams)} diagram(s))"
        )
        return 1

    diagram = diagrams[diagram_index]
    graph_model = diagram.find("mxGraphModel")
    if graph_model is None:
        print(
            "ERROR: <mxGraphModel> not found as direct child. "
            "Compressed diagrams are not supported."
        )
        return 1

    root_elem = graph_model.find("root")
    if root_elem is None:
        print("ERROR: <root> element not found inside <mxGraphModel>")
        return 1

    # Determine parent id β€” default to "1" (the default layer)
    parent_id = "1"
    existing_ids = {c.get("id") for c in root_elem.findall("mxCell") if c.get("id")}
    if parent_id not in existing_ids:
        # Fallback to the first cell id that isn't "0"
        for c in root_elem.findall("mxCell"):
            cid = c.get("id")
            if cid and cid != "0":
                parent_id = cid
                break

    # Generate a unique id
    new_id = _generate_id(label, x, y)
    while new_id in existing_ids:
        new_id = _generate_id(label + "_", x, y)

    # Build the new mxCell element
    new_cell = ET.Element("mxCell")
    new_cell.set("id", new_id)
    new_cell.set("value", label)
    new_cell.set("style", style)
    new_cell.set("vertex", "1")
    new_cell.set("parent", parent_id)

    geom = ET.SubElement(new_cell, "mxGeometry")
    geom.set("x", str(x))
    geom.set("y", str(y))
    geom.set("width", str(width))
    geom.set("height", str(height))
    geom.set("as", "geometry")

    if dry_run:
        print("DRY RUN β€” new cell XML (not written):")
        print(ET.tostring(new_cell, encoding="unicode"))
        print(f"\nWould add to diagram '{diagram.get('name', diagram_index)}' in '{path}'")
        return 0

    root_elem.append(new_cell)

    # Write back preserving XML declaration (uses _indent_xml for Python 3.8 compat)
    _indent_xml(tree.getroot())
    tree.write(str(path), encoding="utf-8", xml_declaration=True)

    print(
        f"Added shape id=\"{new_id}\" to page {diagram_index} "
        f"('{diagram.get('name', '')}') of {path}"
    )
    return 0


def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Add a shape to an existing .drawio diagram file.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument("diagram", help="Path to the .drawio file")
    parser.add_argument("label", help="Text label for the new shape")
    parser.add_argument("x", type=int, help="X coordinate (pixels)")
    parser.add_argument("y", type=int, help="Y coordinate (pixels)")
    parser.add_argument("--width", type=int, default=120, help="Shape width (default: 120)")
    parser.add_argument("--height", type=int, default=60, help="Shape height (default: 60)")
    parser.add_argument(
        "--style",
        default=DEFAULT_STYLE,
        help=f'draw.io style string (default: "{DEFAULT_STYLE}")',
    )
    parser.add_argument(
        "--diagram-index",
        type=int,
        default=0,
        help="0-based index of the diagram page to add to (default: 0)",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Print the new cell XML without writing to file",
    )
    return parser.parse_args(argv)


def main(argv: list[str] | None = None) -> int:
    args = _parse_args(argv)
    path = Path(args.diagram)

    if not path.exists():
        print(f"ERROR: File not found: {path}")
        return 1
    if not path.is_file():
        print(f"ERROR: Not a file: {path}")
        return 1

    return add_shape(
        path=path,
        label=args.label,
        x=args.x,
        y=args.y,
        width=args.width,
        height=args.height,
        style=args.style,
        diagram_index=args.diagram_index,
        dry_run=args.dry_run,
    )


if __name__ == "__main__":
    sys.exit(main())
validate-drawio.py 7.5 KB
#!/usr/bin/env python3
"""
validate-drawio.py β€” Validate the XML structure of a .drawio diagram file.

Usage:
    python scripts/validate-drawio.py <path-to-file.drawio>

Exit codes:
    0  All checks passed
    1  One or more validation errors found
"""
from __future__ import annotations

import sys
import xml.etree.ElementTree as ET
from pathlib import Path


def _error(msg: str, errors: list) -> None:
    errors.append(msg)
    print(f"  ERROR: {msg}")


def validate_file(path: Path) -> list[str]:
    """Parse and validate a single .drawio file. Returns list of error strings."""
    errors: list[str] = []

    # --- XML well-formedness ---
    try:
        tree = ET.parse(path)
    except ET.ParseError as exc:
        return [f"XML parse error: {exc}"]

    root = tree.getroot()
    if root.tag != "mxfile":
        _error(f"Root element must be <mxfile>, got <{root.tag}>", errors)
        return errors

    diagrams = root.findall("diagram")
    if not diagrams:
        _error("No <diagram> elements found inside <mxfile>", errors)
        return errors

    for d_idx, diagram in enumerate(diagrams):
        d_name = diagram.get("name", f"page-{d_idx}")
        prefix = f"[diagram '{d_name}']"

        # Find mxGraphModel (may be direct child or base64-encoded; we handle direct only)
        graph_model = diagram.find("mxGraphModel")
        if graph_model is None:
            print(f"  SKIP {prefix}: mxGraphModel not found as direct child (may be compressed)")
            continue

        root_elem = graph_model.find("root")
        if root_elem is None:
            _error(f"{prefix} Missing <root> element inside <mxGraphModel>", errors)
            continue

        cells = root_elem.findall("mxCell")
        cell_ids: dict[str, ET.Element] = {}
        has_id0 = False
        has_id1 = False

        # --- Collect all IDs, check for root cells ---
        for cell in cells:
            cid = cell.get("id")
            if cid is None:
                _error(f"{prefix} Found <mxCell> without an 'id' attribute", errors)
                continue
            if cid in cell_ids:
                _error(f"{prefix} Duplicate cell id='{cid}'", errors)
            cell_ids[cid] = cell
            if cid == "0":
                has_id0 = True
            if cid == "1":
                has_id1 = True

        if not has_id0:
            _error(f"{prefix} Missing required root cell id='0'", errors)
        if not has_id1:
            _error(f"{prefix} Missing required default-layer cell id='1'", errors)

        # L2: id="0" must be the first cell, id="1" must be the second cell
        if len(cells) >= 1 and cells[0].get("id") != "0":
            _error(
                f"{prefix} First <mxCell> must have id='0', "
                f"got id='{cells[0].get('id')}'",
                errors,
            )
        if len(cells) >= 2 and cells[1].get("id") != "1":
            _error(
                f"{prefix} Second <mxCell> must have id='1', "
                f"got id='{cells[1].get('id')}'",
                errors,
            )
        # L3: id="1" must have parent="0"
        for cell in cells:
            if cell.get("id") == "1" and cell.get("parent") != "0":
                _error(
                    f"{prefix} Cell id='1' must have parent='0', "
                    f"got parent='{cell.get('parent')}'",
                    errors,
                )
        # H2: Every diagram page must contain a title cell
        # (a vertex with style containing 'text;' and 'fontSize=18')
        def _is_title_style(style: str) -> bool:
            """Return True if the style string identifies a draw.io title text cell."""
            return (
                (style.startswith("text;") or ";text;" in style)
                and "fontSize=18" in style
            )

        has_title_cell = any(
            c.get("vertex") == "1" and _is_title_style(c.get("style") or "")
            for c in cells
        )
        if not has_title_cell:
            _error(
                f"{prefix} No title cell found β€” add a vertex with style "
                "containing 'text;' and 'fontSize=18' at the top of the page",
                errors,
            )

        # --- Check each cell for structural validity ---
        for cell in cells:
            cid = cell.get("id", "<unknown>")
            is_vertex = cell.get("vertex") == "1"
            is_edge = cell.get("edge") == "1"

            # Parent must exist (skip the root cell id=0 which has no parent)
            parent = cell.get("parent")
            if cid != "0":
                if parent is None:
                    _error(f"{prefix} Cell id='{cid}' is missing a 'parent' attribute", errors)
                elif parent not in cell_ids:
                    _error(
                        f"{prefix} Cell id='{cid}' references unknown parent='{parent}'",
                        errors,
                    )

            # Vertex cells must have mxGeometry
            if is_vertex:
                geom = cell.find("mxGeometry")
                if geom is None:
                    _error(
                        f"{prefix} Vertex cell id='{cid}' is missing <mxGeometry>",
                        errors,
                    )

            # Edge cells must have source and target, both must exist.
            # Exception: floating edges (e.g. sequence diagram lifelines) use
            # sourcePoint/targetPoint in mxGeometry instead of source/target attributes.
            if is_edge:
                source = cell.get("source")
                target = cell.get("target")
                geom = cell.find("mxGeometry")
                has_source_point = geom is not None and any(
                    p.get("as") == "sourcePoint" for p in geom.findall("mxPoint")
                )
                has_target_point = geom is not None and any(
                    p.get("as") == "targetPoint" for p in geom.findall("mxPoint")
                )
                if source is None and not has_source_point:
                    _error(
                        f"{prefix} Edge cell id='{cid}' is missing 'source' attribute "
                        f"(and no sourcePoint in mxGeometry)",
                        errors,
                    )
                elif source is not None and source not in cell_ids:
                    _error(
                        f"{prefix} Edge id='{cid}' references unknown source='{source}'",
                        errors,
                    )
                if target is None and not has_target_point:
                    _error(
                        f"{prefix} Edge cell id='{cid}' is missing 'target' attribute "
                        f"(and no targetPoint in mxGeometry)",
                        errors,
                    )
                elif target is not None and target not in cell_ids:
                    _error(
                        f"{prefix} Edge id='{cid}' references unknown target='{target}'",
                        errors,
                    )

    return errors


def main() -> int:
    if len(sys.argv) < 2:
        print("Usage: python validate-drawio.py <diagram.drawio>")
        return 1

    path = Path(sys.argv[1])
    if not path.exists():
        print(f"File not found: {path}")
        return 1
    if not path.is_file():
        print(f"Not a file: {path}")
        return 1

    print(f"Validating: {path}")
    errors = validate_file(path)

    if errors:
        print(f"\nFAIL β€” {len(errors)} error(s) found.")
        return 1

    print("PASS β€” No errors found.")
    return 0


if __name__ == "__main__":
    sys.exit(main())

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.