Project Description
Figure is a powerful VSCode extension that integrates Excalidraw, allowing users to create beautiful diagrams and sketches directly within the VSCode editor.
The extension provides a seamless drawing experience with features like saving/loading diagrams and theme synchronization with VSCode.
Technology Stack
TypeScript
VSCode Extension API
React
Excalidraw
HTML/CSS
Node.js
High-Level Architecture
graph TD
User[User] --- VSCode[VSCode Editor]
VSCode --- Extension[Figure Extension]
Extension --- Webview[Webview Panel]
Webview --- Excalidraw[Excalidraw Library]
subgraph "Extension Components"
ExtensionAPI[VSCode Extension API] --- Commands[Commands]
Commands --- WebviewAPI[Webview API]
WebviewAPI --- MessagePassing[Message Passing]
end
subgraph "Webview Components"
WebviewUI[Webview UI] --- ReactApp[React App]
ReactApp --- ExcalidrawComponent[Excalidraw Component]
ExcalidrawComponent --- DrawingTools[Drawing Tools]
end
Extension --- Extension_Components[Extension Components]
Webview --- Webview_Components[Webview Components]
classDef vscode fill:#d1e7dd,stroke:#20c997;
classDef extension fill:#cfe2ff,stroke:#0d6efd;
classDef webview fill:#e2e3e5,stroke:#6c757d;
classDef excalidraw fill:#f8d7da,stroke:#dc3545;
class VSCode vscode;
class Extension extension;
class Webview webview;
class Excalidraw excalidraw;
Detailed Component Architecture
graph TD
subgraph "Extension"
ActivateFunc["activate() function"] --> RegisterCommand["Register figure.new command"]
RegisterCommand --> CreateWebview["Create Webview Panel"]
CreateWebview --> SetupMessageHandlers["Setup Message Handlers"]
SetupMessageHandlers --> HandleSave["Handle Save Messages"]
SetupMessageHandlers --> HandleExport["Handle Export Messages"]
end
subgraph "Webview"
WebviewHTML["HTML Content"] --> LoadScripts["Load React & Excalidraw Scripts"]
LoadScripts --> InitializeApp["Initialize React App"]
InitializeApp --> RenderExcalidraw["Render Excalidraw Component"]
RenderExcalidraw --> SetupProps["Setup Excalidraw Props"]
SetupProps --> OnChangeHandler["onChange Handler"]
SetupProps --> OnSaveHandler["onSave Handler"]
SetupProps --> ThemeProps["Theme Props"]
OnSaveHandler --> PostMessage["Post Message to Extension"]
end
ActivateFunc -.-> WebviewHTML["Provides HTML content"]
HandleSaveRequest -.-> HandleSave["POST Message"]
classDef extension fill:#f0f7ff,stroke:#3b82f6;
classDef webview fill:#f0fdf4,stroke:#22c55e;
class Extension extension;
class Webview webview;
Project Structure
figure/
├── .vscode/ # VSCode configuration
├── media/ # Images and assets
│ └── icon.png # Extension icon
├── node_modules/ # Dependencies
├── out/ # Compiled JavaScript files
│ └── extension.js # Compiled extension code
├── src/ # Source code
│ └── extension.ts # Main extension code
├── .gitignore # Git ignore file
├── .vscodeignore # VSCode packaging ignore file
├── LICENSE.md # License file
├── README.md # Documentation
├── package.json # Extension metadata and dependencies
├── package-lock.json # Dependency lock file
└── tsconfig.json # TypeScript configuration
Data Flow Diagram
sequenceDiagram
participant User
participant VSCode
participant Extension
participant Webview
participant Excalidraw
User->>VSCode: Open Command Palette
User->>VSCode: Select "New Figure Sketch"
VSCode->>Extension: Activate Extension
Extension->>Webview: Create Webview Panel
Extension->>Webview: Load HTML Content
Webview->>Excalidraw: Load Excalidraw Library
Webview->>Excalidraw: Initialize Drawing Interface
Excalidraw->>Webview: Render Drawing Tools
Webview->>User: Display Drawing Interface
User->>Excalidraw: Create Drawing
User->>Excalidraw: Press Ctrl/Cmd+S
Excalidraw->>Webview: Trigger onSave Event
Webview->>Extension: Send Save Message
Extension->>VSCode: Call showSaveDialog
User->>VSCode: Select Save Location
VSCode->>Extension: Return File URI
Extension->>VSCode: Write .figure File
VSCode->>User: Show Success Message
Detailed API Endpoints
graph LR
subgraph "VSCode Extension API"
Commands["vscode.commands"] --- RegisterCommand["registerCommand"]
Window["vscode.window"] --- CreateWebview["createWebviewPanel"]
Window --- ShowSaveDialog["showSaveDialog"]
Window --- ShowMessage["showInformationMessage"]
Workspace["vscode.workspace"] --- WriteFile["fs.writeFile"]
ExtensionContext["ExtensionContext"] --- Subscriptions["subscriptions"]
end
subgraph "Webview API"
WebviewPanel["WebviewPanel"] --- HTML["html property"]
WebviewPanel --- OnDidReceiveMessage["onDidReceiveMessage"]
WebviewPanel --- PostMessage["postMessage"]
end
RegisterCommand --- CreateWebview
OnDidReceiveMessage --- HandleSave["Handle 'save' command"]
OnDidReceiveMessage --- HandleExport["Handle 'export' command"]
HandleSave --- ShowSaveDialog
ShowSaveDialog --- WriteFile
WriteFile --- ShowMessage
classDef vscode fill:#dbeafe,stroke:#3b82f6;
classDef webview fill:#dcfce7,stroke:#22c55e;
class VSCode_Extension_API vscode;
class Webview_API webview;
Extension Activation Flow
graph LR
A["VSCode Starts"] --> B["Load Extension"]
B --> C["Call activate Function"]
C --> D["Register figure.new Command"]
D --> E["Add to Context Subscriptions"]
E --> F["Extension Ready"]
F --> G["User Invokes Command"]
G --> H["Create WebviewPanel"]
H --> I["Set Panel Options"]
I --> J["Load HTML Content"]
J --> K["Setup Message Handler"]
K --> L["Extension UI Ready"]
style A fill:#d4f4e2,stroke:#34d399,stroke-width:3px,color:#065f46,font-weight:bold
style B fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style C fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style D fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style E fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style F fill:#d4f4e2,stroke:#34d399,stroke-width:3px,color:#065f46,font-weight:bold
style G fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style H fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style I fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style J fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style K fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style L fill:#d4f4e2,stroke:#34d399,stroke-width:3px,color:#065f46,font-weight:bold
User Flow
graph LR
A["Start"] --> B["Open VSCode"]
B --> C["Open Command Palette"]
C --> D["Type New Figure Sketch"]
D --> E["Select Command"]
E --> F["Open Figure Panel"]
F --> G["Create Diagram"]
G --> H["Press Ctrl/Cmd+S"]
H --> I["Select Save Location"]
I --> J["Save .figure File"]
J --> K["View Success Message"]
K --> L["Continue Editing"]
L --> H
style A fill:#d4f4e2,stroke:#34d399,stroke-width:3px,color:#065f46,font-weight:bold
style B fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style C fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style D fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style E fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style F fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style G fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style H fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style I fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style J fill:#fecaca,stroke:#ef4444,stroke-width:3px,color:#7f1d1d,font-weight:bold
style K fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style L fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
Webview Implementation
graph TD
subgraph "Webview Implementation"
HTML[HTML Structure] --> Head[Head Section]
HTML --> Body[Body Section]
Head --> Meta[Meta Tags]
Head --> CSP[Content Security Policy]
Head --> Styles[CSS Styles]
Body --> AppDiv[App Div Container]
Body --> Scripts[JavaScript Scripts]
Scripts --> LoadReact[Load React]
Scripts --> LoadReactDOM[Load ReactDOM]
Scripts --> LoadExcalidraw[Load Excalidraw]
Scripts --> InitApp[Initialize App]
InitApp --> CreateApp[Create React App]
CreateApp --> SetupExcalidrawRef[Setup Excalidraw Ref]
CreateApp --> SetupEffects[Setup React Effects]
CreateApp --> RenderExcalidraw[Render Excalidraw Component]
RenderExcalidraw --> SetupProps[Setup Excalidraw Props]
SetupProps --> OnChangeHandler[onChange Handler]
SetupProps --> OnSaveHandler[onSave Handler]
SetupProps --> ThemeProps[Theme Props]
OnSaveHandler --> PostMessage[Post Message to Extension]
end
classDef webview fill:#f0f7ff,stroke:#3b82f6;
class Webview_Implementation webview;
Save Functionality Implementation
sequenceDiagram
participant User
participant Excalidraw as Excalidraw Component
participant Webview as Webview
participant Extension as Extension Host
participant VSCode as VSCode API
participant FileSystem as File System
User->>Excalidraw: Press Ctrl/Cmd+S
Excalidraw->>Excalidraw: Trigger onSave callback
Excalidraw->>Webview: Call onSave with drawing data
Webview->>Extension: Post 'save' message with data
Extension->>VSCode: Call showSaveDialog
VSCode->>User: Display save dialog
User->>VSCode: Select location and confirm
VSCode->>Extension: Return selected URI
Extension->>FileSystem: Write JSON data to file
FileSystem->>Extension: File write complete
Extension->>VSCode: Show success message
VSCode->>User: Display "Figure saved successfully!"
Drawing Tools Implementation
graph TD
subgraph "Excalidraw Features"
Tools[Drawing Tools] --> ShapeTools[Shape Tools]
Tools --> FreehandTools[Freehand Tools]
Tools --> TextTools[Text Tools]
Tools --> SelectionTools[Selection Tools]
ShapeTools --> Rectangle[Rectangle]
ShapeTools --> Ellipse[Ellipse]
ShapeTools --> Diamond[Diamond]
ShapeTools --> Arrow[Arrow]
ShapeTools --> Line[Line]
FreehandTools --> Pencil[Pencil]
FreehandTools --> Brush[Brush]
TextTools --> TextBox[Text Box]
TextTools --> Label[Label]
SelectionTools --> Select[Select]
SelectionTools --> Move[Move]
SelectionTools --> Resize[Resize]
Settings[Tool Settings] --> Color[Color]
Settings --> StrokeWidth[Stroke Width]
Settings --> Fill[Fill]
Settings --> Opacity[Opacity]
Settings --> FontSize[Font Size]
end
classDef tools fill:#e2e3e5,stroke:#6c757d;
class Excalidraw_Features tools;
Theme Synchronization
sequenceDiagram
participant VSCode as VSCode
participant Extension as Extension
participant Webview as Webview
participant Excalidraw as Excalidraw
Note over VSCode,Excalidraw: Theme Synchronization
VSCode->>Extension: Theme Change Event
Extension->>Webview: Post Theme Message
Webview->>Webview: Process Message
Webview->>Excalidraw: Update Theme (light/dark)
Excalidraw->>Excalidraw: Re-render with New Theme
State Management Flow
graph TD
subgraph "Extension State"
ExtensionContext[Extension Context] --> Subscriptions[Subscriptions]
ExtensionContext --> Disposables[Disposables]
ExtensionContext --> WebviewPanels[Webview Panels]
end
subgraph "Webview State"
ReactState[React State] --> ExcalidrawRef[Excalidraw Ref]
ReactState --> Elements[Drawing Elements]
ReactState --> AppState[App State]
Elements --> Shapes[Shapes]
Elements --> Lines[Lines]
Elements --> TextElements[Text Elements]
AppState --> ViewMode[View Mode]
AppState --> ZoomLevel[Zoom Level]
AppState --> GridMode[Grid Mode]
AppState --> Theme[Theme]
end
ExtensionContext -. Creates .-> WebviewPanels
WebviewPanels -. Renders .-> ReactState
classDef extension fill:#f0f7ff,stroke:#3b82f6;
classDef webview fill:#f0fdf4,stroke:#22c55e;
class Extension_State extension;
class Webview_State webview;
File Format
graph TD
subgraph ".figure File Format"
FigureFile[.figure File] --> JSON[JSON Format]
JSON --> Elements[Elements Array]
JSON --> AppState[App State Object]
Elements --> Element1[Element 1]
Elements --> Element2[Element 2]
Elements --> ElementN[Element N]
Element1 --> ID[id]
Element1 --> Type[type]
Element1 --> X[x]
Element1 --> Y[y]
Element1 --> Width[width]
Element1 --> Height[height]
Element1 --> Properties[properties]
AppState --> ViewMode[viewMode]
AppState --> Zoom[zoom]
AppState --> ScrollX[scrollX]
AppState --> ScrollY[scrollY]
end
classDef file fill:#f0f7ff,stroke:#3b82f6;
class figure_File_Format file;
Development Workflow
sequenceDiagram
participant Developer
participant Git as Git Repository
participant NPM as NPM
participant VSCode as VSCode
participant Extension as Extension Host
Developer->>Git: Clone Repository
Developer->>NPM: Run npm install
NPM->>Developer: Dependencies Installed
Developer->>VSCode: Press F5 to Debug
VSCode->>Extension: Compile TypeScript
Extension->>VSCode: Launch Extension Host
VSCode->>Developer: Extension Development Host Window
Developer->>VSCode: Test Extension Features
Developer->>VSCode: Make Code Changes
VSCode->>Extension: Hot Reload Changes
Developer->>Git: Commit Changes
Future Enhancements
graph TD
subgraph "Planned Features"
Export[Export Functionality] --> ExportPNG[Export to PNG]
Export --> ExportSVG[Export to SVG]
Templates[Templates] --> FlowchartTemplates[Flowchart Templates]
Templates --> DiagramTemplates[Diagram Templates]
Templates --> UMLTemplates[UML Templates]
Collaboration[Collaboration] --> SharedEditing[Shared Editing]
Collaboration --> Comments[Comments]
Collaboration --> VersionHistory[Version History]
Integration[VSCode Integration] --> GitIntegration[Git Integration]
Integration --> WorkspaceIntegration[Workspace Integration]
Integration --> ThemeIntegration[Enhanced Theme Integration]
end
classDef future fill:#f0f7ff,stroke:#3b82f6;
class Planned_Features future;
Getting Started
To run the extension locally:
- Clone the repository
- Install dependencies:
- Run
npm install
to install all required packages
- Start debugging:
- Press F5 in VSCode to launch the extension in debug mode
- This will open a new VSCode window with the extension loaded
- Use the extension:
- Open the command palette (Ctrl/Cmd + Shift + P)
- Type "New Figure Sketch" and select it
- Start creating your diagram
- Save your work with Ctrl/Cmd + S
Conclusion
Figure is a powerful VSCode extension that brings the full capabilities of Excalidraw directly into your editor.
With its seamless integration and intuitive interface, Figure makes it easy to create beautiful diagrams and sketches without leaving your development environment.
The extension's architecture leverages VSCode's Webview API and React to provide a smooth drawing experience,
while its modular design makes it easy to extend with new features in the future.