Figure - Project Overview

Create beautiful diagrams and sketches directly in VS Code

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:

  1. Clone the repository
  2. Install dependencies:
  3. Start debugging:
  4. Use the extension:

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.