New languages
- Added German, Spanish, Italian, and Portuguese translations across all 12 namespaces (thanks @cedriclocqueneux!)
Timeline layout improvements
- Replaced greedy lane assignment with a force-directed simulation for better milestone placement; falls back to greedy for dense zoom levels
- Normalised repulsion force by neighbour count to fix crowding at dense zoom levels
- Fixed force-directed lane repulsion tie-breaking
UI polish
- Normalised add-milestone and add-chapter button heights and sizing
- Removed browser button chrome and fixed uneven text centering on add buttons
- Compacted bottom bar on landscape mobile to free vertical space
- Hidden minimap on ultraCompact screens (landscape phones) and below 380px height / 400px width
Media placeholder fixes
- Fixed raw HTML entity strings (
🎬/🎧) rendering literally instead of as emoji in the media-unavailable block - Removed redundant icon span from the audio/video unavailable block (emoji now lives in the i18n string)
- Updated placeholder text in all 6 languages to clarify media is stored on the originating device, not just "synced from another device"
Internals (forward-compat)
- Added
media_id,photo_id, andthumbnail_idfields to the milestone schema; stable reference slots for the upcoming content-addressed blob store; all null today - Wired
media_idandphoto_idon milestone create and edit paths, mirroring the existing IndexedDB blob-key convention - One-time startup backfill stamps existing milestones that have media but no reference IDs yet