Commit Graph

4 Commits

Author SHA1 Message Date
d3dd785921 core: file/image transfer over the RTC data channel (chunked, P2P)
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>
2026-06-21 11:54:29 -05:00
c3b9b7940f core: account identity — same-account devices auto-pair on the LAN
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>
2026-06-21 01:41:00 -05:00
6e24d19a58 core: nearby-device discovery (AirDrop-style) — friendly names + Engine.nearby()
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>
2026-06-21 01:09:56 -05:00
fdd65eea7c core: in-core RTC transport — direct P2P clipboard over WebRTC data channel
Implement RtcTransport (webrtc-rs) behind the Transport trait, so every native
platform that links tethercore gets direct P2P alongside SSE from one impl.

Signaling rides the existing server bus and matches the web client byte-for-
byte (presence chirp, lower-id-offers tie-break, {kind, sdp|candidate} signal
envelopes, "tether" data channel carrying raw clipboard text) — so Rust peers
interoperate with the browser's native RTCPeerConnection.

Dedup now keys on (from, text) with a 3s TTL so the SSE and data-channel copies
of the same clipboard collapse to one; the faster RTC copy wins.

Proven by examples/rtc_p2p.rs: two engines bring up a data channel and a
clipboard is delivered over it (received source == peer from-id, confirming the
RTC path beat the SSE relay).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 20:38:12 -05:00