feat: screen sharing via pixelpass (Discord-style, presence-borne ticket)

Surface pixelpass screen-sharing from inside a peerspeak room. peerspeak owns
voice, pixelpass owns pixels — they're never Cargo deps of each other; the
contract is pixelpass's CLI flags + its `--output json` stdout stream.

Modelled on Discord: multiple simultaneous sharers, a 🔴 Live badge + 👁 Watch
on each sharing peer's card, and in-progress shares visible to late joiners.

- New `src/screenshare` module: pure `parse_pixelpass_event` seam + `pixelpass_path`
  discovery (13 unit tests), async `spawn_host` (→ ticket) and `spawn_viewer`
  (→ parse connected{url} → open mpv, vlc fallback). No new deps.
- Sharing rides presence: `PeerState.sharing: Option<ticket>` (serde-defaulted),
  so the existing gossip re-announce delivers the offer to late joiners for free
  and a PeerUpdated fires on start/stop — no separate gossip message needed.
- core: Start/Stop/ViewShare commands; host + viewer children tracked in the
  session, killed on stop/leave (kill_on_drop backstop). Viewer limit left to
  pixelpass's bandwidth-measured cap.
- UI: Share/Stop button (graceful "needs pixelpass" disabled state), Live badge
  + Watch on peer cards, Sharing badge on the self card. Verified by screenshot.
- config: optional `pixelpass_path` override (hand-editable).

Tests-green; the 2-machine gossip/remote path is not yet field-verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 15:46:34 -04:00
co-authored by Claude Opus 4.8
parent 5dad86db57
commit d9e544607a
11 changed files with 703 additions and 4 deletions
+17 -1
View File
@@ -68,6 +68,19 @@ covers internals). When you ship a feature, add it here.
| History cap | ⚙️ | `CHAT_HISTORY_MAX = 300`. |
| Local echo of own messages | ✅ | |
## Screen share (via pixelpass)
| Feature | Status | Notes |
|---|---|---|
| Share your screen | 🧪 | Spawns `pixelpass --host --output json`, announces the ticket on presence. `src/screenshare/mod.rs`. Subprocess contract not yet 2-machine-verified. |
| Watch a peer's share | 🧪 | 👁 Watch spawns a pixelpass viewer → opens the stream in mpv (vlc fallback). |
| Live badge on sharing peers | ✅ | 🔴 Live on the peer card; 🔴 Sharing badge on the self card. |
| Multiple simultaneous sharers | 🧪 | Discord-style: each sharer is an independent pixelpass host; the room shows one offer per sharer. |
| Late-joiner sees in-progress share | 🧪 | Sharing rides presence, so the existing gossip re-announce delivers the offer to late joiners. |
| Graceful "needs pixelpass" state | ✅ | Share button disabled + labelled when the binary isn't on `$PATH`. Config override: `pixelpass_path`. |
| Viewer limit | ⚙️ | Not overridden — pixelpass bandwidth-measures its own safe cap (protects the sharer's uplink); refusals surface as an error. |
| Audio of the share | ⚙️ | Video only on the room path (so it can't echo/double with the voice mix). |
## Recording
| Feature | Status | Notes |
@@ -122,11 +135,14 @@ Re-run on a real desktop ↔ dopedart call before calling these done:
4. **In-room text chat** — 2-machine delivery.
5. **Adaptive jitter buffer** — behaviour under real packet loss/jitter.
6. **Chat drawer** — toggle + persistence across restart.
7. **Screen share** — 2-machine: sharer's ticket reaches the room over gossip,
a peer's Watch opens the stream, late joiner sees an in-progress share, stop
clears the badge. (Subprocess contract is solo-verifiable; the gossip/remote
half needs dopedart.) Requires `pixelpass` + `mpv` on both ends.
## Not built (candidate features)
Things that came up but **do not** exist yet:
- Soundboard (play short clips into the call mix).
- Video / screen share.
- Invite links beyond the raw ticket / room persistence.