A mobile-friendly, offline-first time tracking app for presentations and recording sessions. Track your slides, capture photos, take notes, and export your session data.
- Session Management - Create, start, pause, resume, and end timing sessions
- Slide Tracking - Mark new slides with Ctrl+Space or the UI button
- Per-Slide Pause Tracking - Track pause events with timestamps and durations
- Camera Support - Capture photos for each slide with front/back camera switching
- Notes - Add notes to each slide during or after the session
- Offline-First - All data stored locally in localStorage, works without internet
- Export Options - Export sessions as ZIP files containing:
- JSON - Full session data for re-import
- CSV - Spreadsheet format for analysis
- Markdown - Formatted report with embedded image references
- Import - Import previously exported ZIP files with photos
- Bun (recommended) or Node.js
bun installbun run devOpens the app at http://localhost:3000
bun run buildStatic files are output to the dist/ directory.
bun run test- Create a Session - Click "New Session" to start
- Start Recording - Press the Start button to begin timing
- Mark Slides - Use Ctrl+Space or click "Next Slide" to mark a new slide
- Capture Photos - Use the camera button to take photos of your slides
- Add Notes - Type notes in the notes field for each slide
- Pause/Resume - Pause the timer when needed, pauses are tracked per-slide
- End Session - Complete the session to view the summary
- Export - Download your session as a ZIP with all photos included
- Import - Re-import exported sessions to restore them
- Vite - Build tool and dev server
- React 19 - UI framework
- TypeScript - Type safety
- Tailwind CSS 4 - Styling
- JSZip - ZIP file creation for exports
- Lucide React - Icons
The app is configured for static hosting. Deploy the dist/ folder to any static host:
- Netlify - Configured via
netlify.toml - Vercel - Works out of the box
- GitHub Pages - Copy
dist/contents
interface Session {
id: string
name: string
createdAt: number
startTime: number | null
endTime: number | null
totalDuration: number
slides: Slide[]
status: 'idle' | 'running' | 'paused' | 'completed'
}
interface Slide {
id: string
name: string
startTime: number
endTime: number | null
duration: number
photos: Photo[]
notes: string
pauseEvents: PauseEvent[]
totalPauseTime: number
}MIT