Replace the stale-flag-based voice re-registration in the WS ready
handler with a server-authoritative check. If the server's voiceStates
doesn't include us (server restarted, lost in-memory voiceRooms),
cleanly tear down the LiveKit session via leaveVoice() + disconnect().
If the server still knows about us (WS blip), do nothing.
- Import BITRATE_MATRIX_KBPS from shared constants
- Parse and return bitrateMatrixOverrides in rowToLimits
- Widen updateData type to accept null values
- Raise maxBitrateKbps and minBitrateKbps caps from 50 000 to 1 000 000 kbps
- Add full validation for bitrateMatrixOverrides (null to clear, key/value checks)
After creating or copying a role, the name input is now auto-focused
with text selected so the user immediately starts typing. A subtle
"Role created" banner appears until the user makes any change.
RoleEditView used useState(role.name) for draft state, but React
reuses the component instance when editingRoleId changes, ignoring
the new initial value. Adding key={editingRoleId} forces a remount,
re-initializing all draft state (name, color, permissions) correctly.
Replaces hardcoded 3-resolution/3-framerate arrays with ALL_RESOLUTIONS
and STANDARD_FRAMERATES from shared constants (540p–Native, 30–120 fps).
Adds amber warning banner when high-end options (≥1440p or ≥75 fps) are
enabled, and fixes toggleResolution to handle 'native' with correct sort.
Replace hardcoded VALID_RESOLUTIONS/VALID_FRAMERATES with shared STANDARD_RESOLUTIONS/STANDARD_FRAMERATES constants. Update rowToLimits to handle the 'native' resolution value in DB parsing, and update all PATCH validation blocks to accept the expanded resolution/framerate sets and the 'native' option.
Optimistic message used content: '' while the server normalized it to
null, causing the content-based dedup to fail and leaving both the
empty temp message and the real attachment message in the list.
- Add GET/PUT/DELETE /api/categories/:id/overrides endpoints with
privilege escalation guard matching existing channel override pattern
- Add isCategoryPrivate() and broadcastCategoryOverrideChange() helpers
that cascade visibility changes to all channels in a category
- Fix PATCH /api/channels/:id to recompute permissions via
broadcastOverrideChange when categoryId changes (different category
overrides may apply)
- Include isPrivate flag on categories in GET /api/spaces/:id payload
using batch-fetched @everyone overrides
- Include isPrivate on categories in broadcastChannelLayout and
PATCH /api/categories/:id broadcasts
Refactor loadDictionary to accept a file path and support both legacy
bare-array (version 0) and versioned object formats. Add background
sync that fetches the latest games.json from GitHub, uses ETag-based
conditional requests (304 support), atomic cache writes, and hot-swaps
processMap/gameEntries without resetting current detection state.
On startup, the best local source (cache vs bundled seed) is loaded
immediately for instant detection, then sync runs fire-and-forget.
Rewrite games.json from bare array (28 entries) to versioned format
{ version: 1, games: [...] } with 249 entries covering top Steam games,
competitive titles, AAA, indie, survival/craft, simulation, horror,
fighting, racing, MMOs, and media apps (Spotify, VLC, OBS, mpv).
Every process name is specific enough to avoid false-positive detection.