A clean, minimal desktop application for film photographers to easily fix camera metadata in scanned film images. Built with Electron and React, inspired by ImageOptim's simple drag-and-drop interface.
This repository is organized as a monorepo containing:
- 🖥️ Desktop App - Electron application for EXIF editing
- 🌐 Marketing Website - Next.js landing page
- 📦 Shared - Common types and utilities
- Simple Drag & Drop Interface: Drop images anywhere in the window to process them
- Camera Profile Management: Create and save camera/lens combinations for reuse
- Batch Processing: Process multiple images simultaneously
- Real-time Feedback: See processing results for each file instantly
- Dark Mode Support: Light, dark, and system theme options
- Persistent Storage: Profiles are saved between app sessions
- Cross-platform: Works on macOS, Windows, and Linux
- JPEG (.jpg, .jpeg)
- TIFF (.tiff, .tif)
✅ No external dependencies required! ExifTool is bundled with the application.
-
Clone the repository:
git clone https://github.com/yourusername/film-exif-editor.git cd film-exif-editor -
Install dependencies for all packages:
npm install
-
Start development servers:
# Start both desktop app and website concurrently npm run dev # Or start individually: npm run dev:desktop # Electron app on localhost:5173 npm run dev:website # Next.js site on localhost:3001
npm run buildnpm run build:desktop # Build Electron app
npm run build:website # Build Next.js websitenpm run dist # Package for current platformBuilt applications will be available in the release/ directory at the repository root.
The built website will be in packages/website/.next/ directory.
The project includes automated CI/CD with GitHub Actions:
-
Create a release:
./scripts/create-release.sh
-
GitHub Actions automatically:
- Builds for macOS, Windows, and Linux
- Creates a GitHub Release
- Uploads installers for all platforms
-
Download URLs available at:
https://github.com/YOUR_USERNAME/film-exif-editor/releases/latest
See DEPLOYMENT.md for complete deployment guide including:
- Setting up GitHub Actions
- Code signing (optional)
- Linking downloads from website
- Versioning and release management
- Click the "+" button in the footer
- Fill in the camera details:
- Profile Name: A descriptive name (e.g., "Nikon FM")
- Camera Make: Manufacturer (e.g., "Nikon")
- Camera Model: Model number (e.g., "FM")
- Lens: Optional lens information (e.g., "50mm f/1.8")
- Click "Save Profile"
- Select a camera profile from the dropdown (⋯ button)
- Drag and drop image files onto the window, or click to browse
- View processing results in real-time
- Click "Clear Results" to reset for the next batch
Click the theme button in the footer to switch between:
- ☀️ Light mode
- 🌙 Dark mode
- 🖥️ System (follows your OS preference)
- Frontend: React 18 + TypeScript + Vite
- Backend: Electron (main process)
- EXIF Processing: ExifTool (bundled via exiftool-vendored)
- Styling: Tailwind CSS v4 (website) / custom CSS (desktop)
- Icons: Iconify React
- Storage: electron-store
- Build: electron-builder
film-exif-editor/
├── packages/
│ ├── desktop/ # Electron desktop application
│ │ ├── electron/ # Electron main process
│ │ ├── src/ # React UI components
│ │ ├── package.json
│ │ └── vite.config.ts
│ ├── website/ # Next.js marketing website
│ │ ├── app/ # Next.js app directory
│ │ ├── public/ # Static assets
│ │ └── package.json
│ └── shared/ # Shared code and types
│ ├── src/
│ │ ├── types.ts # Common TypeScript interfaces
│ │ ├── utils.ts # Utility functions
│ │ └── index.ts # Package exports
│ └── package.json
├── package.json # Root workspace configuration
└── README.md
Root workspace commands:
npm run dev- Start both desktop app and website concurrentlynpm run dev:desktop- Start only the desktop appnpm run dev:website- Start only the marketing websitenpm run build- Build all packagesnpm run build:desktop- Build desktop appnpm run build:website- Build websitenpm run dist- Package desktop app for distributionnpm run clean- Clean all build outputsnpm run typecheck- Run TypeScript checks on all packages
The app uses Electron's IPC for secure communication. window.electronAPI (exposed via contextBridge in packages/desktop/electron/preload.ts) includes: profile CRUD, EXIF read/write with backups, geocoding, thumbnail extraction/caching, processing log, and window controls. See preload.ts for the full ElectronAPI interface.
Note that the interface is wider than the app. Handlers for custom values, saved locations, GPX import and the Mapbox token are registered and never called, because nothing in the UI reaches them — see "Built but not wired up" in CLAUDE.md.
- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Make your changes and test thoroughly
- Commit with descriptive messages:
git commit -m "Add feature description" - Push to your fork:
git push origin feature-name - Submit a pull request
The app needs read/write access to image files. On macOS, you may need to grant permissions in System Preferences > Security & Privacy.
For processing many images, be patient as ExifTool processes each file individually for accuracy.
MIT License - see LICENSE file for details.
- Inspired by ImageOptim's clean interface design
- Uses ExifTool by Phil Harvey for EXIF manipulation (bundled via exiftool-vendored)
- Icons provided by Iconify
Built for film photographers, by film photographers 📸