You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SongBinder is a local-first progressive web application for musicians who need an offline-capable song library, setlist builder, and live-performance lyric viewer. The application is delivered as a static front-end with no bundled server runtime: all primary business data is stored in the browser first, while optional cloud transfer, AI, and OCR features call external services or libraries from the client. The codebase includes three coordinated application surfaces:
Main library application for song, setlist, import, export, and launch workflows.
Full-screen performance application for live playback, autoscroll, and stage navigation.
Dedicated editor application for structured lyric authoring, chords, metadata, and AI-assisted editing.
Local Auth Setup
Supabase Auth is optional. The app remains local-first: normal editing, song creation, setlist changes, backup, import, and export all continue to use IndexedDB on the current device first. When signed in, the app now exposes explicit manual cloud transfer actions:
Pull from Cloud inside the Import modal merges remote songs and setlists into local IndexedDB.
Push to Cloud inside the Export modal uploads local songs and setlists to Supabase without deleting remote data.
Normal editing does not automatically sync in the background.
Cloud transfer currently preserves setlist membership and order through normalized setlists and setlist_songs tables.
Copy .env.example to .env or .env.local.
Set SUPABASE_URL and SUPABASE_ANON_KEY.
Run npm run env:write to generate env.js.
Serve the project and use the landing screen to either sign in with Google or continue offline.
env.js is gitignored and must be generated locally for auth-enabled runs.
1. System Architecture
1.1 Architectural Summary
Layer
Implementation
Responsibilities
Frontend shell
Static HTML + vanilla JavaScript + CSS
Renders the library UI, performance mode, and editor without a build step.
Persistence
IndexedDB via idb, plus localStorage / sessionStorage
Stores songs, setlists, migration flags, user preferences, performance state, and reminders.
Search and matching
Fuse.js
Fuzzy search across titles and lyrics; fuzzy setlist matching during text/OCR import.
Document parsing
Mammoth.js
Extracts plain text from imported .docx files.
OCR
Tesseract.js + local language files / CDN fallback
Reads setlist images and converts them into song-title candidates.
Reordering
SortableJS
Enables drag-and-drop ordering inside setlists.
Optional AI integration
OpenRouter HTTP API
Powers editor-side lyric generation, rewriting, chord suggestions, and formatting.
Optional cloud transfer
Supabase Auth + PostgREST
Google sign-in plus explicit manual push/pull of songs and setlists.
Offline delivery
Service worker + Web App Manifest
Caches application shells and assets for PWA-like offline use.
1.2 High-Level Topology
flowchart TD
User[User] --> Main[index.html / script.js]
User --> Editor[editor/editor.html / editor.js]
User --> Perf[performance/performance.html / performance.js]
Main --> IDB[(IndexedDB: songs, setlists, meta)]
Editor --> IDB
Perf --> IDB
Main --> LS[localStorage / sessionStorage]
Editor --> LS
Perf --> LS
Main --> SW[sw.js Service Worker]
Editor --> SW
Perf --> SW
Main --> OCR[Tesseract.js]
Main --> DOCX[Mammoth.js]
Main --> Search[Fuse.js]
Main --> Supabase[Supabase Auth / PostgREST]
Editor --> OpenRouter[OpenRouter API]
Main --> CDN[Tessdata CDN fallback]
Enables window.CONFIG.devMode in local environments.
sessionStorage state
Key
Purpose
songsLoadedToastShown
Prevents duplicate initial-load toasts.
backupReminderShown
Prevents duplicate reminder toasts per session.
dbResetToastShown
Prevents repeated database-reset notices.
lastSongId
Editor continuity when returning to a song.
3.3 Main Data Flows
Song Import Flow
flowchart LR
File[Uploaded file] --> Type{Extension?}
Type -->|txt| TXT[Read as text]
Type -->|docx| DOCX[Mammoth extractRawText]
Type -->|csv| CSV[Parse rows]
Type -->|json| JSON[Parse known backup/library formats]
TXT --> Normalize[Normalize title and lyrics]
DOCX --> Normalize
CSV --> Normalize
JSON --> Normalize
Normalize --> Dedupe[Skip duplicate or create copy]
Dedupe --> IDB[Persist to IndexedDB songs]
IDB --> UI[Re-render library]
Loading
Setlist Import Flow
flowchart LR
Source[TXT / DOCX / OCR text] --> Clean[Normalize OCR artifacts and line formatting]
Clean --> Filter[Keep probable titles only]
Filter --> Fuse[Fuzzy match against song titles]
Fuse --> Match{Match found?}
Match -->|yes| Add[Append song ID to setlist order]
Match -->|no| Missing[Track as not found]
Add --> Create[Create setlist record]
Missing --> Toast[Notify partial misses]
Create --> UI[Select and render imported setlist]
Loading
Performance Launch Flow
User chooses a setlist or “All Songs”.
User optionally filters the launcher list.
User starts from the first song or from a specific card.
Main app builds query parameters:
songId = explicit starting song,
setlistId = current or inferred setlist,
ids = comma-separated ordered setlist IDs for file:// and offline fallback.
Performance page loads songs from IndexedDB and reconstructs the ordered play queue.
If the same setlist was previously in progress, the app may prompt to resume.
Manual Cloud Pull Flow
User signs in with Google or stays in local-only mode.
User opens the Import modal and selects Pull from Cloud.
The app validates that a Supabase user is available.
The client optionally upserts a minimal profile row.
Songs are fetched from songs and mapped back to local IDs via legacy_id.
Setlists are fetched from setlists, and membership/order are fetched from setlist_songs.
Remote records are merged into local IndexedDB without deleting local-only records.
The app stores a local last pulled timestamp and re-renders the current UI.
Manual Cloud Push Flow
User opens the Export modal and selects Push to Cloud.
The app validates that a Supabase user is available.
The client optionally upserts a minimal profile row.
Local songs are upserted to songs using (user_id, legacy_id) as the sync key.
Local setlists are upserted to setlists using (user_id, legacy_id) as the sync key.
The client refreshes remote IDs, then rewrites setlist_songs rows for each pushed setlist.
Positions are written one-based in Supabase so membership order is preserved.
The app stores a local last pushed timestamp and leaves local IndexedDB as the source of truth for normal editing.
3.4 API and Integration Specifications
Internal HTTP API
There is no first-party backend API in this repository. All application logic executes client-side.
External API: Supabase
Property
Value
Auth
Supabase OAuth with Google provider
Purpose
Optional sign-in plus explicit manual cloud transfer
This repository does not use a server-side .env contract. Optional AI credentials are entered in the editor UI and stored in browser localStorage.
If you want environment-driven configuration for hosted deployments, you must add your own build or server injection layer; it is not present in the current codebase.
4.6 Production Deployment
Because the project is static, production hosting can be handled by any web server or static host.
Requirements
Serve the repository root as static files.
Preserve the directory structure for editor/, performance/, lib/, and assets/.
Serve with HTTPS for best browser API support.
Ensure eng.traineddata.gz is not sent with Content-Encoding: gzip; it should be served as a binary file, ideally with application/octet-stream.
The service worker precaches the main shell, performance shell, editor shell, icons, fonts, and local JS/CSS assets.
HTML navigations are resolved to cached shell files during offline use.
file:// launches may still work, but service workers do not run in that mode; the application compensates by passing ordered ids into performance mode.
If the application shell changes, users may need to accept the “Update available” banner or hard-refresh.
5. Contribution Guidelines
5.1 Development Standards
Preserve the no-build-step architecture unless a deliberate platform change is approved.
Keep primary functionality available without a backend dependency.
Favor progressive enhancement for browser APIs such as speech recognition, wake lock, and clipboard access.
Avoid introducing inline scripts that would weaken the existing CSP model.
Maintain local-first data ownership and never silently move user data off-device.
5.2 Recommended Workflow
git checkout -b docs/<short-description>
npm install
npm run lint
npm run format
5.3 Validation Checklist for Pull Requests
Verify the main app opens and navigates between Songs, Setlists, and Lyrics tabs.
Verify editor launch and save round-trips to IndexedDB.
Verify performance mode launches and returns cleanly.
Verify at least one import and one export workflow when modifying those areas.
Verify service worker registration still succeeds on a static server.
5.4 Pull Request Expectations
Include the following in each PR:
concise problem statement,
summary of functional changes,
user impact and risk areas,
manual verification steps,
screenshots for visual changes,
migration or storage compatibility notes if persistence changes.
6. Troubleshooting for Developers and Operators
Symptom
Likely cause
Resolution
OCR import fails immediately
Tesseract assets missing or headers incorrect
Confirm lib/tesseract/* is being served and that eng.traineddata.gz is not content-encoded.
Service worker does not update
Cached older shell still controlling clients
Use the in-app update banner, unregister in DevTools, or hard refresh.
PDF export opens nothing
Browser blocked window.open()
Allow pop-ups for the app origin.
Voice search/dictation buttons do nothing
Browser lacks Web Speech API or microphone permission
Test in a compatible browser and grant microphone access.
AI tools return errors
Missing or invalid OpenRouter key/model
Re-enter key in editor settings and validate outbound network access.
Performance page loads without songs
IndexedDB unavailable in current context or file:// constraints
Launch from the main app so songId, setlistId, and ids are passed together.
User data disappears
Browser storage cleared or quota eviction
Encourage periodic full JSON export and persistent-storage approval.
7. Repository Map
Path
Role
index.html
Main application shell
script.js
Main application controller
style.css
Shared application styling
config.js
Runtime defaults and editor/performance feature flags
sw.js
Service worker and offline cache strategy
editor/editor.html
Full editor shell
editor/editor.js
Editor controller and AI integration
editor/db.js
Editor IndexedDB bridge
editor/songs.js
Shared editor song utilities and export helpers
performance/performance.html
Performance shell
performance/performance.js
Performance runtime controller
performance/performance.css
Performance-specific styling
manifest.json
PWA metadata
lib/
Vendored client libraries
assets/
Icons, logos, images, and fonts
8. License and Data Ownership
This repository does not currently declare a dedicated project license beyond the package metadata. Operationally, the application is designed so that end-user song data remains in the user’s browser unless that user explicitly exports it or configures an optional third-party AI integration.