# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Commands ```bash npm start # Run the Electron app in development npm run build # Build macOS app bundle (no DMG, arm64 + x64) npm run dist # Build and package as DMG for distribution ./setup.sh # First-time setup: npm install + npm start ``` There are no tests or linting configured. ## Architecture This is a macOS Electron desktop app for batch-renaming photo files. It follows strict Electron security practices: `contextIsolation: true`, `nodeIntegration: false`. **Process boundary:** All filesystem access lives exclusively in `src/main/` (main process). The renderer never touches `fs` directly. ### File structure ``` src/ ├── main/ │ ├── index.js # Main process entry point; creates BrowserWindow, calls registerAll() │ └── ipc/ │ ├── index.js # registerAll(mainWindow) — delegates to folder and files modules │ ├── folder.js # select-folder, reveal-folder │ └── files.js # list-images, read-image, rename-files ├── preload/ │ └── index.js # Bridges IPC to window.api via contextBridge (isolated context) └── renderer/ ├── index.html # App shell (dark theme, CSS variables in :root) ├── style.css # All CSS ├── renderer.js # Main renderer logic; imports state and handlers ├── state.js # Shared mutable state: files[], currentFolder, selectedIdxs, sortMode └── handlers/ ├── drag-drop.js # Card drag-and-drop reorder (single and multi-card) ├── selection.js # Rubber-band selection, Shift-click, Cmd+click ├── pinch-zoom.js # Trackpad pinch to resize thumbnails (CSS --thumb-size, 150–400px) └── image-preview.js # Hover preview overlay ``` ### IPC surface Defined in `src/main/ipc/`, bridged via `src/preload/index.js` as `window.api`: | Channel | Direction | Description | |---|---|---| | `select-folder` | invoke | Opens native folder picker, returns path or null | | `list-images` | invoke | Reads directory, returns `{name, path, mtime, size}[]` filtered to image extensions, sorted by filename | | `read-image` | invoke | Reads file, returns base64 data URL for display | | `rename-files({folderPath, files, prefix, digits})` | invoke | Two-phase rename: temp names first, then final names to avoid collisions | | `reveal-folder` | invoke | Opens folder in Finder via `shell.openPath` | | `fullscreen-change` | push (main→renderer) | Sent on `enter-full-screen` / `leave-full-screen` window events | ### Renderer (`src/renderer/`) Uses ES module imports (`type="module"`). No framework. **State** (`src/renderer/state.js`): - `files[]` — ordered array of file objects; order determines rename sequence - `selectedIdxs` — `Set` of selected card indices - `currentFolder` — active directory path - `sortMode` — `'name'` | `'date'` | `null` **Thumbnail loading:** Uses `IntersectionObserver` with `rootMargin: '400px 0px'` — images load lazily as cards enter the viewport. DOM card elements are created immediately without `src`; the observer fills in `img.src` on entry. Drag-and-drop reorder remaps existing card elements via `card.dataset.filepath` to avoid re-fetching images. **Styling:** All CSS lives in `src/renderer/index.html`. Dark theme with CSS variables defined in `:root`. The macOS traffic-light buttons sit in `#titlebar` with `-webkit-app-region: drag`. **Build output:** `electron-builder` targets DMG for arm64 and x64. The app icon must be at `assets/icon.icns`.