# PeerSpeak Feature Inventory A capability list of what PeerSpeak **already does**, so we don't keep re-deriving it. This is the user-facing companion to `ARCHITECTURE.md` (which covers internals). When you ship a feature, add it here. **Field-test status legend:** - βœ… **Verified** β€” confirmed working on a real call (usually desktop ↔ dopedart). - πŸ§ͺ **Tests-green** β€” unit-tested and/or smoke-tested locally, but **not** yet exercised on a real 2-machine call. Treat as unproven. - βš™οΈ **Plumbing** β€” wiring/persistence that has no separate field test of its own. --- ## Rooms & sessions | Feature | Status | Notes | |---|---|---| | Create a room β†’ shareable join ticket | βœ… | Ticket carries host addr + topic id. | | Join a room via ticket | βœ… | Paste ticket on the launch screen. | | Leave a room | βœ… | | | Multi-peer rooms (mesh) | βœ… | Presence over the iroh gossip plane. | | Copy ticket to clipboard | βœ… | Button on the room screen. | | Remembered nickname | βœ… | `config.username` pre-fills the launch field, saved on join/create. | | In-call duration timer | βœ… | `call_started` drives the header clock. | ## Audio I/O & codec | Feature | Status | Notes | |---|---|---| | PipeWire capture + playback | βœ… | `src/audio/pipewire_impl.rs`. | | Input device selection | βœ… | | | Output device selection | βœ… | | | App-internal input (mic) gain slider | βœ… | Unity default; applied pre-gate/encode. | | App-internal output gain slider | βœ… | Unity default; applied on the mixed output. | | Opus codec | βœ… | 48 kHz mono, `Application::Voip`, 20 ms / 960-sample frames. | | Adaptive jitter buffer + Opus PLC | πŸ§ͺ | `src/core/jitter.rs`; reorders by seq, adaptive playout delay, conceals drops. Not field-tested. | ## Voice controls | Feature | Status | Notes | |---|---|---| | Self mute | βœ… | | | Deafen | βœ… | | | Push-to-talk + rebindable hotkey | βœ… | `TogglePtt` / `StartSettingHotkey`; gate ahead of the encoder. | | Noise gate (hysteresis + attack/release/hangover) | βœ… | `src/audio/gate.rs`. | | Draggable gate threshold on the mic meter | βœ… | Discord/OBS-style `GateMeter` canvas widget. | | Mic level meter β€” in-call | βœ… | | | Mic level meter β€” off-call "Test mic" | βœ… | `SetMicMonitor` / standalone capture stream. | | Mix-bus soft limiter | πŸ§ͺ | `src/audio/limiter.rs`; A/B never confirmed on a real call. | | Echo cancellation (opt-in) | πŸ§ͺ | `src/audio/echo_cancel.rs`; PipeWire `module-echo-cancel` (WebRTC AEC+NS+AGC) via RAII pactl guard. **Needs a 2-machine call through speakers, not headphones.** | ## Per-peer controls | Feature | Status | Notes | |---|---|---| | Per-peer volume | βœ… | `SetPeerVolume`. | | Per-peer local mute | βœ… | Still decoded (levels show) but not mixed. | | Per-peer speaking indicator | βœ… | `audio_levels` per `EndpointId`. | ## Text chat | Feature | Status | Notes | |---|---|---| | In-room text chat | πŸ§ͺ | Over the gossip plane; full-width bottom-dock UI. 2-machine delivery not confirmed. | | Outgoing + incoming sanitization | βœ… | `sanitize_chat`: strips control chars, collapses whitespace, caps 2000. | | 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`. Field-verified desktop↔dopedart 2026-06-06. Capture portal appears on the host when the first viewer connects. | | Watch a peer's share | βœ… | πŸ‘ Watch spawns a pixelpass viewer β†’ opens the stream in mpv (vlc fallback). Field-verified: laptop watched the desktop's screen. | | 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. Single-sharer path verified; concurrent not yet. | | Late-joiner sees in-progress share | πŸ§ͺ | Sharing rides presence, so the existing gossip re-announce delivers the offer to late joiners. Not yet explicitly tested. | | 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 | |---|---|---| | Local call recording (mic + incoming mix β†’ WAV) | πŸ§ͺ | `src/audio/recorder.rs`; writes to `~/peerspeak-recordings/`. Solo + 2-machine recording not confirmed. Header REC timer present. | ## Notifications & sounds | Feature | Status | Notes | |---|---|---| | Desktop notifications toggle | βœ… | `notifications_enabled`. | | Event chimes | βœ… | 8 events: self-join, peer-join, peer-leave, reconnect-attempt, reconnected, self-leave, mic-toggle, reconnect-failed. | | Per-event custom sound override | βœ… | `custom_sound_*` paths in config. | ## Networking & privacy | Feature | Status | Notes | |---|---|---| | iroh QUIC transport | βœ… | | | Network mode picker | βœ… | `RelayNoDiscovery` (default), `N0Full`, `DirectOnly`. Takes effect next join. | | Retained-address reconnect | βœ… | Dials last-known full addr before falling back to bare id. | | Reconnect + eviction model | βœ… | Incl. two-outage reconnect-eviction fix + regression test. | | Self-hosted relay | ❌ | Decided against β€” rely on n0 relays, `RelayNoDiscovery` default. | ## UI & layout | Feature | Status | Notes | |---|---|---| | Selectable room layouts | βœ… | 3-Column, Bottom Dock (default), Drawer. | | Layout picker popup w/ canvas thumbnails | βœ… | `LayoutThumb`, no image dependency. | | Draggable + persisted panel dividers | βœ… | Per-layout, clamped to window; saved on drag-release. | | Chat drawer toggle (drawer layout) | πŸ§ͺ | Toggle + restart-persistence not explicitly confirmed. | | Responsive full-window Settings screen | βœ… | | | Canvas-drawn icon set | βœ… | 18 icons (`Icon`/`IconKind` in `src/app/mod.rs`), no image/font dep, theme-recolored. Replaces all emoji; SVG design source in `~/Documents/peerspeak-mockups/icons/`. | | Selectable UI themes | βœ… | 10 palettes (`src/theme.rs`): Catppuccin Mocha (default)/Macchiato/FrappΓ©/Latte, Dracula, Nord, Tokyo Night, Gruvbox Dark, Solarized Light, Gruvbox Light. Picked via canvas palette swatches in Settings (`ThemeSwatch`), applied live + persisted (`config.theme`). All palettes unit-tested for WCAG-AA text legibility. | ## Persistence | Feature | Status | Notes | |---|---|---| | Config file | βœ… | `~/.config/peerspeak/config.json`. | | Backward-compatible loading | βœ… | serde `default`s fill missing fields; unknown fields tolerated. | | Window size restored | βœ… | `window_width`/`window_height`, saved on close. | | Window position restored | βœ… | `window_x`/`window_y`, saved on close. **X11 only** β€” see Platform support. | ## Platform support (Linux desktop) | Concern | Status | Notes | |---|---|---| | Wayland | βœ… | Default on this dev box; winit's Wayland backend. App/taskbar icon comes from the `.desktop` file matched by `application_id = "peerspeak"`. | | X11 (incl. XWayland) | βœ… | winit's X11 backend (both backends compile in by default; winit auto-selects β€” Wayland if `WAYLAND_DISPLAY` is set, else X11 via `DISPLAY`). Launch verified rendering on X11. The embedded RGBA window icon (`from_rgba`) is honored on X11 even without the `.desktop` installed. | | Window position restore | X11 only | Wayland's xdg-shell gives clients no way to place their own window, so we center there. On X11 the saved `window_x`/`window_y` is restored via `Position::Specific` (outer position; no drift across save/restore β€” iced's `Moved` and `Position::Specific` both use the outer position). Verified live on X11 (saved an off-center position; the window reopened there, not centered). Gated by `is_wayland()` (`src/app/mod.rs`); decision logic unit-tested (`initial_window_position`). | --- ## Known field-test debt (the πŸ§ͺ rows above, collected) Re-run on a real desktop ↔ dopedart call before calling these done: 1. **Echo cancellation** β€” 2-machine call *through speakers* (the only path that actually exercises AEC). 2. **Mix-bus soft limiter** β€” A/B a hot signal. 3. **Local call recording** β€” solo + 2-machine, verify the WAV. 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** β€” single-sharer 2-machine path VERIFIED 2026-06-06 (desktop hosted, laptop watched via mpv). Remaining: multiple concurrent sharers, late-joiner sees an in-progress share, and Stop clears the peer's badge. 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). - Invite links beyond the raw ticket / room persistence.