mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-22 19:35:13 +00:00
8.2 KiB
8.2 KiB
Excalidraw JSON Schema Reference
This document describes the structure of Excalidraw .excalidraw files for diagram generation.
Top-Level Structure
interface ExcalidrawFile {
type: "excalidraw";
version: number; // Always 2
source: string; // "https://excalidraw.com"
elements: ExcalidrawElement[];
appState: AppState;
files: Record<string, any>; // Usually empty {}
}
AppState
interface AppState {
viewBackgroundColor: string; // Hex color, e.g., "#ffffff"
gridSize: number; // Typically 20
}
ExcalidrawElement Base Properties
All elements share these common properties:
interface BaseElement {
id: string; // Unique identifier
type: ElementType; // See Element Types below
x: number; // X coordinate (pixels from top-left)
y: number; // Y coordinate (pixels from top-left)
width: number; // Width in pixels
height: number; // Height in pixels
angle: number; // Rotation angle in radians (usually 0)
strokeColor: string; // Hex color, e.g., "#1e1e1e"
backgroundColor: string; // Hex color or "transparent"
fillStyle: "solid" | "hachure" | "cross-hatch";
strokeWidth: number; // 1-4 typically
strokeStyle: "solid" | "dashed" | "dotted";
roughness: number; // 0-2, controls hand-drawn effect (1 = default)
opacity: number; // 0-100
groupIds: string[]; // IDs of groups this element belongs to
frameId: null; // Usually null
index: string; // Stacking order identifier
roundness: Roundness | null;
seed: number; // Random seed for deterministic rendering
version: number; // Element version (increment on edit)
versionNonce: number; // Random number changed on edit
isDeleted: boolean; // Should be false
boundElements: any; // Usually null
updated: number; // Timestamp in milliseconds
link: null; // External link (usually null)
locked: boolean; // Whether element is locked
}
Element Types
Rectangle
interface RectangleElement extends BaseElement {
type: "rectangle";
roundness: { type: 3 }; // 3 = rounded corners
text?: string; // Optional text inside
fontSize?: number; // Font size (16-32 typical)
fontFamily?: number; // 1 = Virgil, 2 = Helvetica, 3 = Cascadia
textAlign?: "left" | "center" | "right";
verticalAlign?: "top" | "middle" | "bottom";
}
Example:
{
"id": "rect1",
"type": "rectangle",
"x": 100,
"y": 100,
"width": 200,
"height": 100,
"strokeColor": "#1e1e1e",
"backgroundColor": "#a5d8ff",
"text": "My Box",
"fontSize": 20,
"textAlign": "center",
"verticalAlign": "middle",
"roundness": { "type": 3 }
}
Ellipse
interface EllipseElement extends BaseElement {
type: "ellipse";
text?: string;
fontSize?: number;
fontFamily?: number;
textAlign?: "left" | "center" | "right";
verticalAlign?: "top" | "middle" | "bottom";
}
Diamond
interface DiamondElement extends BaseElement {
type: "diamond";
text?: string;
fontSize?: number;
fontFamily?: number;
textAlign?: "left" | "center" | "right";
verticalAlign?: "top" | "middle" | "bottom";
}
Arrow
interface ArrowElement extends BaseElement {
type: "arrow";
points: [number, number][]; // Array of [x, y] coordinates relative to element
startBinding: Binding | null;
endBinding: Binding | null;
roundness: { type: 2 }; // 2 = curved arrow
}
Example:
{
"id": "arrow1",
"type": "arrow",
"x": 100,
"y": 100,
"width": 200,
"height": 0,
"points": [
[0, 0],
[200, 0]
],
"roundness": { "type": 2 },
"startBinding": null,
"endBinding": null
}
Points explanation:
- First point
[0, 0]is relative to(x, y) - Subsequent points are relative to the first point
- For straight horizontal arrow:
[[0, 0], [width, 0]] - For straight vertical arrow:
[[0, 0], [0, height]]
Line
interface LineElement extends BaseElement {
type: "line";
points: [number, number][];
startBinding: Binding | null;
endBinding: Binding | null;
roundness: { type: 2 } | null;
}
Text
interface TextElement extends BaseElement {
type: "text";
text: string;
fontSize: number;
fontFamily: number; // 1-3
textAlign: "left" | "center" | "right";
verticalAlign: "top" | "middle" | "bottom";
roundness: null; // Text has no roundness
}
Example:
{
"id": "text1",
"type": "text",
"x": 100,
"y": 100,
"width": 150,
"height": 25,
"text": "Hello World",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"roundness": null
}
Width/Height calculation:
- Width ≈
text.length * fontSize * 0.6 - Height ≈
fontSize * 1.2 * numberOfLines
Bindings
Bindings connect arrows to shapes:
interface Binding {
elementId: string; // ID of bound element
focus: number; // -1 to 1, position along edge
gap: number; // Distance from element edge
}
Common Colors
| Color Name | Hex Code | Use Case |
|---|---|---|
| Black | #1e1e1e |
Default stroke |
| Light Blue | #a5d8ff |
Primary entities |
| Light Green | #b2f2bb |
Process steps |
| Yellow | #ffd43b |
Important/Central |
| Light Red | #ffc9c9 |
Warnings/Errors |
| Cyan | #96f2d7 |
Secondary items |
| Transparent | transparent |
No fill |
| White | #ffffff |
Background |
ID Generation
IDs should be unique strings. Common patterns:
// Timestamp-based
const id = Date.now().toString(36) + Math.random().toString(36).substr(2);
// Sequential
const id = "element-" + counter++;
// Descriptive
const id = "step-1", "entity-user", "arrow-1-to-2";
Seed Generation
Seeds are used for deterministic randomness in hand-drawn effect:
const seed = Math.floor(Math.random() * 2147483647);
Version and VersionNonce
const version = 1; // Increment when element is edited
const versionNonce = Math.floor(Math.random() * 2147483647);
Coordinate System
- Origin
(0, 0)is top-left corner - X increases to the right
- Y increases downward
- All units are in pixels
Recommended Spacing
| Context | Spacing |
|---|---|
| Horizontal gap between elements | 200-300px |
| Vertical gap between rows | 100-150px |
| Minimum margin from edge | 50px |
| Arrow-to-box clearance | 20-30px |
Font Families
| ID | Name | Description |
|---|---|---|
| 1 | Virgil | Hand-drawn style (default) |
| 2 | Helvetica | Clean sans-serif |
| 3 | Cascadia | Monospace |
Validation Rules
✅ Required:
- All IDs must be unique
typemust match actual element typeversionmust be an integer ≥ 1opacitymust be 0-100
⚠️ Recommended:
- Keep
roughnessat 1 for consistency - Use
strokeWidthof 2 for clarity - Set
isDeletedtofalse - Set
lockedtofalse - Keep
frameId,boundElements,linkasnull
Complete Minimal Example
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"id": "box1",
"type": "rectangle",
"x": 100,
"y": 100,
"width": 200,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "#a5d8ff",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a0",
"roundness": { "type": 3 },
"seed": 1234567890,
"version": 1,
"versionNonce": 987654321,
"isDeleted": false,
"boundElements": null,
"updated": 1706659200000,
"link": null,
"locked": false,
"text": "Hello",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle"
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
},
"files": {}
}