Root cause of the CI "Cannot find module @anydrop/shared" error:
server/tsconfig.tsbuildinfo and shared/tsconfig.tsbuildinfo were checked in.
On the Gitea runner, tsc read those files, concluded the prior (local-machine)
build was still valid, and skipped emitting shared/dist — so there was nothing
to resolve by the time server's tsc ran.
- untrack the two .tsbuildinfo files
- gitignore *.tsbuildinfo
- dockerignore **/dist, **/*.tsbuildinfo (belt-and-suspenders)
- in both Dockerfiles, delete any stray tsbuildinfo + dist dirs before tsc
pnpm's workspace symlink (server/node_modules/@anydrop/shared → ../../../shared)
works locally but breaks on the Gitea Actions runner. TSC resolves the symlink
but cannot read through it, yielding TS2307 on "@anydrop/shared".
Fix: after building shared, copy its package.json + dist into
{server,web}/node_modules/@anydrop/shared as a plain directory before running
the dependent build. Module resolution becomes filesystem-local, independent
of BuildKit layer semantics or storage driver symlink handling.
Pairing:
- Always request fresh code when opening "Mon code" tab
- Clear stale pairCode on modal close/tab switch
- Show errors inline for invalid codes (don't close modal)
- Delete pair code after first use (server)
- Validate code length server-side
Notifications removed:
- Remove all showLocalNotification calls (peer-joined, transfer, text)
- Push setup now runs only once per session (no memory leak)
Other fixes:
- Clear pendingFilesRef on disconnect
- Set transfer status to "transferring" immediately on send start
- Select offline peer when tapping to wake
- Fix file receiver blob restoration race condition (await arrayBuffer)
- Clear selectedPeerId after share-target send
- Add returnValue to beforeunload handler
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
iOS PWA (home screen) has separate localStorage from Safari, so
QR-based pairing breaks. Now pairing uses a 6-character code:
- Device A taps "Appairer" → shows a code like "A7K9XB"
- Device B taps "Appairer" → "Rejoindre" tab → enters the code
- Server resolves the code to the groupId, devices are linked
Codes expire after 5 minutes. Pairing is permanent.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a groupId-based pairing system so devices can always see
each other regardless of network. Scan a QR code once from the
other device, and they're permanently linked via a shared group
stored in localStorage. No account, no email — just one-time QR
scan like Bluetooth pairing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
iCloud Private Relay hides iPhones' real public IP, breaking
IP-based LAN grouping. Devices now detect their local IP
(192.168.x.x) via WebRTC ICE candidates and send it to the
server. The server groups devices by local subnet as a fallback
when public IPs differ.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Offline push-subscribed devices appear dimmed in peer list
- Tap offline peer to send wake push notification
- Skip self-notification (own deviceId excluded)
- iOS/Android share sheet via Web Share Target API
- Online/offline indicator dot on peer avatars
- Web Push API for offline device notifications
- Custom service worker with push event handling
- Local notifications for background tab transfers
- VAPID keys in K8s config
- Persistent deviceId per device
- Users set their own device name and optional profile photo
- Profile persisted in localStorage, no account needed
- Auto-detect device type from user agent
- Server validates client-provided profile defensively
- PeerAvatar shows photo or device icon
- ProfileSetup modal on first visit, editable from header
- Users set their own device name and optional profile picture
- Profile persisted in localStorage (no account needed)
- Auto-detect device type from user agent (iPhone, Mac, Android...)
- Server uses client-provided profile instead of generating names
- PeerAvatar shows photo or device icon instead of animal emojis
- ProfileSetup modal on first visit + editable from header