Add Engine.send_file(name, mime, data) and MessageHandler.on_file. Files stream
over each open RTC data channel as binary frames — M<json meta> · C<16-byte
id><16KB chunk>… · E<id> — and reassemble on the receiver in arrival order
(the channel is reliable+ordered). The data channel now distinguishes string
(clipboard) from binary (file) messages. Files are direct-only — never relayed,
matching prefer-direct.
Transport trait gains start(...files) + a default-noop send_file (only RTC
implements it). The agent saves received files to ~/Downloads; the apps stub
on_file (receive UI is a follow-up). All MessageHandler impls updated.
Proven by examples/file_demo.rs: A sends a 50KB blob (4 chunks), B reassembles
it to identical bytes over RTC.
Follow-ups: file transfer over LAN (same-network without RTC); attach/preview UI;
progress + size cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an `account` dimension (Apple/Google `sub`, or any shared id) advertised
over mDNS alongside the device name. The LAN transport now connects to a peer
when they share a room OR a non-empty account — so your own devices find each
other and sync on the network regardless of room, no pairing step. Peer gains
`account` so the UI can mark "your devices".
Engine::new gains `account` (6th arg, ""=signed out); all callers updated.
Apps pass "" until sign-in lands; agent takes --account.
Proven by examples/account_pair.rs: two devices in DIFFERENT rooms with the
same account sync over LAN with no server.
Next: derive the account from Sign in with Apple / Google so this is automatic
(the cross-network version = account-derived shared server room).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each device now advertises a friendly name over mDNS ("Patrick's MacBook"),
and the LAN transport records every tether device on the network — regardless
of room — into a discovered table. New Engine.nearby() -> [Peer] exposes that
list for an AirDrop-style picker; entries prune after 30s unseen. Peer carries
{id, name, source, room} so the UI can offer "pair" (join that device's room).
Engine::new gains a `name` param (5th); all callers updated (apps pass
UIDevice.name / Host.localizedName / Build.MODEL, agent passes hostname or
--name). Auto-connect stays room-scoped; only the *visibility* is broadened.
Proven by examples/nearby.rs: two engines in different rooms discover each
other by name over mDNS — and it picks up real devices on the LAN too.
iOS caveat unchanged: mDNS needs the multicast entitlement, so nearby() is
empty there until that lands. Desktop/Android work today.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A controllable LAN peer (dead server, doesn't touch the OS clipboard) used to
prove the desktop agent syncs over LAN alone. Confirmed both directions on macOS
with the server unreachable: pbcopy → agent → peer, and peer → agent → pasteboard.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>