# CLAUDE.md - AI Assistant Guide for draw.io Desktop

## Project Overview

Draw.io Desktop is an Electron-based desktop application that wraps the core draw.io diagramming editor (included as a git submodule). It enables creating flowcharts, UML diagrams, and more, with a security-first design that isolates diagram data from the internet.

**Repository:** https://github.com/jgraph/drawio-desktop
**License:** Apache 2.0
**Version:** see `drawio/VERSION` (stamped into `package.json` by `npm run sync`)

## Quick Reference

```bash
# Clone (MUST be recursive for submodule)
git clone --recursive https://github.com/jgraph/drawio-desktop.git

# Install dependencies
npm install

# Run application
npm start

# Run with DevTools enabled
DRAWIO_ENV=dev npm start

# Sync version before building (required)
npm run sync

# Build for specific platforms
npm run release-win       # Windows x64
npm run release-linux     # Linux (AppImage, deb, rpm)
npm run release-appx      # Windows Store
```

## Project Structure

```
drawio-desktop/
├── src/main/
│   ├── electron.js           # Main Electron process (3,700+ lines)
│   ├── electron-preload.js   # IPC bridge with contextBridge
│   ├── args.js               # CLI argument definitions and parser
│   ├── progress-bar.js       # Progress bar for long-running operations
│   └── disableUpdate.js      # Generated by sync script
├── src/test/
│   ├── cli-args.test.js      # CLI argument parsing tests (npm test)
│   └── msi-project-created.test.js # MSI shortcut icon hook tests (npm test)
├── drawio/                   # Git submodule - core draw.io editor
│   └── src/main/webapp/      # Web application loaded in Electron
├── build/                    # Build resources
│   ├── notarize.mjs          # macOS Quick Look setup, signing + notarization
│   ├── sign-trusted.mjs      # Windows signing hook (Azure Trusted Signing)
│   ├── fuses.mjs             # Electron security fuses
│   ├── msi-project-created.mjs # msiProjectCreated hook: MSI shortcuts use exe icon, not the C:\Windows\Installer icon cache
│   ├── dmg-hidden-files.mjs  # beforePack hook: parks hidden DMG support files outside the installer window
│   ├── quicklook-preview.html # Quick Look preview page (viewer-static.min.js)
│   ├── quicklook-entitlements.plist # Sandbox entitlements for .appex
│   └── entitlements.mac.plist
├── doc/
│   ├── RELEASE_PROCESS.md    # Release workflow documentation
│   └── BUILDING_FOR_PERSONAL_USE.md # Guide for unsigned fork/personal builds
├── electron-builder-*.json   # Platform-specific build configs
├── sync.cjs                  # Version sync script
└── package.json
```

## Tech Stack

- **Runtime:** Node.js 22.12+ (`engines` in `package.json`; CI builds on Node 24)
- **Framework:** Electron (version pinned in `package.json`)
- **Language:** JavaScript (ES6 modules)
- **Build Tool:** electron-builder
- **Package Manager:** npm

## Key Files

| File | Purpose |
|------|---------|
| `src/main/electron.js` | Main process: window management, IPC handlers, menus, auto-update |
| `src/main/electron-preload.js` | Secure IPC bridge between renderer and main process |
| `src/main/args.js` | CLI option definitions and argument parser (used by CLI export) |
| `sync.cjs` | Pre-build script that syncs version from `drawio/VERSION` |
| `electron-builder-*.json` | Platform-specific build configurations |
| `build/sign-trusted.mjs` | electron-builder Windows signing hook (Azure Trusted Signing) |

## Code Style

- **ES6 modules** with `import`/`export`
- **Tab indentation**
- **Allman brace style** (opening brace on new line)
- **camelCase** for variables, **PascalCase** for classes
- No ESLint/Prettier - manual style consistency
- Sparse comments; code clarity preferred

## Git Conventions

### Branches
- `dev` - Main development branch (PR target)
- `release` - Production releases
- `releases/v*.*.*` - Version-specific release branches

### Commit Messages
- Lowercase sentence style without period
- Issue references: `[jgraph/drawio-desktop#XXXX]`
- Examples:
  - `Fixes paste error`
  - `Adds buffer as dependency [jgraph/drawio-desktop#2301]`
  - `Prepare release v29.3.0`

### Version Tags
Format: `v{MAJOR}.{MINOR}.{PATCH}` (e.g., `v29.3.0`)
Tags trigger CI/CD build workflows.

## Build Process

1. **Sync version:** `npm run sync` reads `drawio/VERSION` and updates `package.json`
2. **Install:** `npm ci` for clean install
3. **Build:** `electron-builder` with platform-specific config
4. **Post-build:** Security fuses applied, Quick Look extension assembled (macOS), notarization (macOS)

### Code Signing
- **Windows:** Azure Trusted Signing via the `signtoolOptions.sign` hook `build/sign-trusted.mjs` (configured in `electron-builder-win*.json`, not CSC_LINK certificates). CI (`electron-builder-win.yml`) downloads the signing dlib, locates `signtool.exe`, and authenticates with `AZURE_TENANT_ID`/`AZURE_CLIENT_ID`/`AZURE_CLIENT_SECRET` secrets
- **macOS:** Apple Developer certificate + notarization in `build/notarize.mjs`
- **Unsigned builds:** `DRAWIO_UNSIGNED=true` skips signing (Windows) and notarization (macOS) for personal/fork builds

### Personal / Fork Builds
- `doc/BUILDING_FOR_PERSONAL_USE.md` documents building unsigned from a fork (the project is closed to contributions but Apache 2.0 licensed)
- Set `DRAWIO_UNSIGNED=true` and run `electron-builder` directly with `--publish never`; use `npm run sync -- disableUpdate` so auto-update doesn't replace the custom build
- `.github/workflows/personal-build.yml` is a manual (`workflow_dispatch`) workflow that builds unsigned installers on a fork with no secrets and attaches them as run artifacts

> **CI override:** The release build workflows check out the private `jgraph/drawio-dev` repo at its `release` branch, copy the built `*.min.js` **and** `VERSION` into the public `drawio/` submodule tree, then run `npm run sync` as normal. This lets CI ship from an internal release that is ahead of the public `drawio` tag without any change to `sync.cjs`. Out-of-tree builders (who have no access to `drawio-dev`) fall through to the public submodule's `VERSION` as before.

### Platform Build Commands
| Command | Target |
|---------|--------|
| `npm run release-win` | Windows x64 (NSIS + MSI) |
| `npm run release-win32` | Windows 32-bit |
| `npm run release-win-arm64` | Windows ARM64 |
| `npm run release-linux` | Linux (AppImage, deb, rpm) |
| `npm run release-appx` | Windows Store |
| `npm run release-snap` | Snap package |

## Architecture Notes

### Security Model
- **Content Security Policy** prevents remote script execution
- **contextBridge** exposes only specific APIs to renderer
- **validateSender()** ensures IPC calls originate from local draw.io
- No external transmission of diagram data
- **Built-in plugins only** - external/third-party plugins were removed (07/2026). The `isPluginsEnabled` IPC action is retained, hardcoded `false`, so an older bundled webapp degrades to the "plugins disabled" dialog rather than failing

### IPC Pattern
The preload script uses a request/response pattern with unique IDs:
```javascript
// Renderer sends request
electron.request({action: 'save', data: ...}, callbackId);

// Main process handles and responds via IPC
ipcMain.on('request', (e, data) => { ... });
```

### macOS Quick Look Preview
- Pressing Space in Finder shows a rendered preview of `.drawio` files
- Uses `quicklookjs` to embed a Quick Look App Extension (`.appex`) in the app bundle
- The `.appex` loads `viewer-static.min.js` (with embedded shapes) in a WKWebView
- **Build flow:** `afterPack` (fuses.mjs) applies security fuses, then `afterSign` (notarize.mjs) assembles the `.appex`, signs it with sandbox entitlements, re-signs the outer `.app`, and notarizes
- The `.appex` is inserted in `afterSign` (not `afterPack`) so it is never present unsigned during electron-builder's signing verification
- Quick Look extensions require `app-sandbox`, but Electron helpers must not be sandboxed — so the `.appex` gets different entitlements than `entitlementsInherit`
- The UTI `com.jgraph.drawio` is declared via `extendInfo` in `electron-builder-linux-mac.json`
- `viewer-static.min.js` is saved to `build/` during CI before the cleanup step removes it from the drawio submodule; for local dev, it's read from the submodule directly

### Auto-Update
- Checks GitHub releases on startup
- Disable via `DRAWIO_DISABLE_UPDATE=true` or `--disable-update` flag
- Flatpak detection disables updates automatically

### Data Storage
- **macOS:** `~/Library/Application Support/draw.io`
- **Windows:** `%APPDATA%\draw.io\`
- Uses `electron-store` for persistent settings

## Testing

`npm test` runs the unit tests in `src/test/` (CLI argument parsing, MSI shortcut icon hook; Node's built-in test runner). Everything else is manual testing, documented in `doc/RELEASE_PROCESS.md`:
- Launch, create diagram, add shapes, save, open
- Export (PNG, PDF, SVG)
- Undo/redo functionality
- About dialog verification

## CI/CD Workflows

| Workflow | Trigger | Purpose |
|----------|---------|---------|
| `electron-builder.yml` | Version tag | macOS/Linux builds |
| `electron-builder-win.yml` | Version tag | Windows builds (Azure Trusted Signing) |
| `prepare-release.yml` | Manual | Automated release prep |
| `hash-gen.yml` | Manual | Generate checksums |
| `personal-build.yml` | Manual | Unsigned fork builds, artifacts only (no secrets, no publish) |
| `stale.yml` | Schedule | Mark stale issues/PRs |

## Important Constraints

1. **Recursive clone required** - drawio submodule must be initialized
2. **Run `npm run sync` before building** - Updates version from submodule
3. **Version source of truth** - `drawio/VERSION` for public builds; `drawio-dev/VERSION` is copied over `drawio/VERSION` at CI time so the internal release number wins for packaged builds
4. **Closed to contributions** - PRs not accepted; maintained by JGraph (forks for personal use are fine, see `doc/BUILDING_FOR_PERSONAL_USE.md`)
5. **Node 22.12+ required** - see `engines` in `package.json`

## Development Tips

- Set `DRAWIO_ENV=dev` to auto-open DevTools
- Use `npm start --enable-logging` for verbose output
- If using symlink instead of submodule, also symlink `node_modules`
- Main process logs to console; check terminal for errors

## Key Dependencies

| Package | Purpose |
|---------|---------|
| `electron` | Desktop app framework |
| `electron-builder` | Build/package tool |
| `electron-updater` | Auto-update mechanism |
| `electron-store` | Persistent storage |
| `electron-log` | Logging |
| `@cantoo/pdf-lib` | PDF export |
| `quicklookjs` | macOS Quick Look preview extension (dev) |

CLI argument parsing is hand-rolled in `src/main/args.js` (no `commander`).
