Renders space invite system messages in DMs as embed-style cards on the chat
surface. Three render states: snapshot-only on mount (Join enabled, loading
dot), live-confirmed (memberCount refreshed from preview), revoked (gray-out
+ glass-pill indicator). Join targets the space's home origin via
joinByCode(code, spaceInstanceOrigin || undefined) — the three-way
federation correctness rule. Re-exports getApiForOrigin from api/client to
expose the cross-store resolver under a natural import surface.
Other namespaces use 'update' (users.update, spaces.update, channels.update,
roles.update); the new 'invites.patch' breaks the pattern. Renamed before
Tasks 17-19 import it. Plan updated to match.
Storage panel's max-upload-size input now accepts any positive integer
(bounded only by JS safe-integer ceiling) and offers an MB/GB unit
toggle. Multipart limit relaxed to MAX_SAFE_INTEGER — actual cap is
enforced per-request from the DB setting, not at the framework layer.
Wire broadcastStreamWatch (LiveKit data-channel ping) to all three
explicit user-action sites in StreamTile: "Stop Watching" context-menu
item, "Watch Stream" context-menu item, and the handleWatch button
callback. Broadcast is intentionally confined to click sites only —
not wired in voiceStore actions — to prevent noisy stream_user_left
sounds on automatic teardown paths in useLiveKit.ts.
vitest+jsdom puts test code in a different realm than Node's TextEncoder,
so `toBeInstanceOf(Uint8Array)` rejects the encoder's return value even
though it is structurally a Uint8Array. `Object.prototype.toString.call`
checks the @@toStringTag tag, which is realm-safe.
Strip the desktop package description to "Backspace" so Windows Task
Manager shows the bare product name instead of the long tagline.
Unify the web meta description and PWA manifest description on a single
positioning line that names Discord and TeamSpeak as the comparison
targets — improves link-preview copy and SEO surface.
Voice & Video tab no longer fires getUserMedia on mount. Uses
navigator.permissions.query({name:'camera'}) for state detection,
which is passive (no LED activation). Preview only opens when the
user explicitly clicks the dormant tile or the prompt-state CTA.
In-call mode unchanged (attaches existing LK track, no extra LED).
Spec and plan updated to reflect the corrected design — the
"auto-start preview, no Test Camera toggle" rationale was wrong;
macOS holds the camera LED on for ~2s after release, so any
incidental getUserMedia call (probe, transient mount) flashes the
LED in the user's face even when they aren't on the Voice & Video
panel. Privacy-correct UX: never light the LED without an explicit
user action.
Two-mode preview: in-call attaches to the LiveKit local camera track
(no double-capture); pre-call uses getUserMedia. Modes transition
reactively on isCameraOn changes.
Empty labels fall back to 'Camera N'; duplicate labels are
disambiguated with ' (1)', ' (2)' suffixes by enumeration order.
'No cameras detected' subline appears when enumeration is empty.