Commit Graph
265 Commits
Author SHA1 Message Date
jevb 2a2e6daed6 chore: stage QuickSwitchOverlay files missed from earlier commit 2026-03-27 13:10:21 +01:00
jevb 9078bce145 fix: compact member items in sidebar, increase max resize height to 65% 2026-03-27 13:04:35 +01:00
jevb af2d1c9516 feat: add member list header/resize, DM section in sidebar, remove member toggle from chat header
- Add "MEMBERS" category header and drag-to-resize handle on the member
  list section with height persisted to localStorage (min 80px, max 40vh)
- Add collapsible "DIRECT MESSAGES" section between channels and members
  showing up to 5 online users as DM targets
- Remove members toggle button from ChatHeader (members are always in sidebar)
- Clean up onToggleMembers from ChatHeaderOptions and ChatArea
- Update unit and E2E tests to reflect the removed toggle
2026-03-27 12:56:40 +01:00
jevb f2d35c8eb9 feat: persist collapsible section state per-server in localStorage
Add loadCollapsedCategories/saveCollapsedCategories persistence layer
keyed by server name. toggleCategory now auto-saves to localStorage.
On sidebar mount, collapsed state is loaded for the current server so
each server remembers its own collapsed/expanded sections across sessions.
2026-03-27 12:39:46 +01:00
jevb 3686581bf5 feat: wire DM conversations to member data in sidebar
Build DM conversation list from online members instead of empty array.
Each online member (excluding current user) appears as a DM target with
status indicator and "No messages yet" placeholder. Selection sets the
active DM user and keeps sidebar in DM mode. Sidebar re-renders on
member presence changes and active DM user switches.

Also loads per-server collapsed category state on sidebar mount.
2026-03-27 12:39:34 +01:00
jevb fdb5e0ed85 feat: wire quick-switch overlay to disconnect and navigate to ConnectPage
The onSwitch callback now stores the target host in sessionStorage and
calls clearAuth(), which triggers the existing logout flow (voice leave,
WS disconnect, navigate to ConnectPage). On the ConnectPage side, a new
selectServer() method auto-fills the host and loads saved credentials.
The onAddServer callback similarly disconnects and lands on ConnectPage
so the user can add a new server profile.
2026-03-27 12:34:28 +01:00
jevb f4df18d5be feat: relocate MemberList into unified sidebar as collapsible section
Mount the MemberList component below the channel list in channel mode,
with admin callbacks (kick, ban, role change) and visibility toggled
via the existing ChatHeader member list button / uiStore subscription.
Tracked as channelModeExtras for proper cleanup on mode switch.
2026-03-27 12:29:44 +01:00
jevb 0aeaa50856 fix: address code review — test regression, theme consolidation, validation, dead CSS
- Fix test regression: update theme option count 3→4 and midnight index [1]→[2] for neon-glow insertion
- Fix stale Voice & Audio tests: update select count 3→4 (video quality + device added), replace sensitivity slider test with meter bar render check
- Consolidate theme systems: applyTheme() in helpers.ts now delegates body class + persistence to applyThemeByName() so both paths write to owncord:theme:active
- Add input validation to loadCustomTheme(): reject non-object payloads and entries missing name/colors fields
- Remove dead CSS tokens --strip-width and --members-width from tokens.css
- Extend 800px responsive breakpoint to cover .unified-sidebar alongside .channel-sidebar
2026-03-27 12:22:22 +01:00
jevb 6fd4779624 feat: restore saved theme on app startup
- Import restoreTheme from @lib/themes in main.ts
- Call restoreTheme() after applyStoredAppearance() so the body
  class (theme-<name>) is applied before first render
2026-03-27 12:09:56 +01:00
jevb 9a9464a8de feat: add neon-glow to theme selector and apply body class on theme switch
- Add "neon-glow" entry to THEMES in settings/helpers.ts
- Update applyTheme() to also toggle theme-<name> class on document.body
- Update UiState and setTheme() in ui.store.ts to include "neon-glow"
2026-03-27 12:09:47 +01:00
jevb 4d63368a51 refactor: remove ServerStrip component and CSS (replaced by unified sidebar)
- Delete ServerStrip.ts and its unit test
- Remove all .server-strip / .server-icon / .server-separator CSS rules
- Remove @media (max-width: 600px) responsive rule for .server-strip
2026-03-27 12:09:38 +01:00
jevb ba014242e7 feat: update MainPage layout — remove server strip and standalone member list
Remove serverStripSlot and memberListSlot from the app layout in
MainPage.ts. Remove standalone MemberList creation, mounting, and
visibility subscription from ChatArea.ts. The member list is now
part of the unified sidebar (Task 8).
2026-03-27 12:04:48 +01:00
jevb 99e999e698 feat: rewrite SidebarArea as unified sidebar (removes ServerStrip)
- Remove ServerStrip import and server strip slot from sidebar layout
- Add unified sidebar header with OC icon, server name, and online count
- Subscribe to uiStore.sidebarMode to swap between ChannelSidebar and
  DmSidebar content views within the same DOM slot
- Wire UserBar onDisconnect to open QuickSwitchOverlay with saved profiles
- Add openQuickSwitch to SidebarAreaResult for external disconnect flow
- Add unified sidebar CSS classes to app.css
- Voice widget and user bar remain visible in both sidebar modes
2026-03-27 12:01:25 +01:00
jevb cf77deed5d feat: add DM mode support to ChatHeader with recipient info
Adds dmMode option (hides member-list toggle), exposes hashEl in refs,
and adds updateChatHeaderForDm helper that swaps # for @, sets username
and status when a DM recipient is active.
2026-03-27 11:46:49 +01:00
jevb a90018f2c7 feat: add back-to-server header to DmSidebar
Adds optional onBack/serverName fields to DmSidebarOptions. When onBack
is provided, a clickable back header is rendered above the search bar
with arrow, title and subtitle. Supporting CSS added to app.css.
2026-03-27 11:46:40 +01:00
jevb a44b89f1fb feat: add disconnect/switch-server button to UserBar
- Change UserBarOptions from Record<string,never> to interface with optional onDisconnect callback
- Render a "Switch server" button in the controls area when onDisconnect is provided
- Add log-out icon (Lucide path data) to icons.ts for the button
2026-03-27 11:39:48 +01:00
jevb 11d5de09e0 feat: add theme manager with built-in + custom theme support
Implements lib/themes.ts with listThemeNames, applyThemeByName,
getActiveThemeName, saveCustomTheme, loadCustomTheme, deleteCustomTheme,
exportTheme, and restoreTheme. Built-in themes applied via body CSS
classes; custom themes applied via inline CSS variable overrides.
Active theme persisted to localStorage. All 7 unit tests pass (TDD).
2026-03-27 11:37:57 +01:00
jevb b13487dc4f fix: add sidebarMode/activeDmUserId to test UiState mocks 2026-03-27 11:34:45 +01:00
jevb 8ff4077a8b feat: add OC Neon Glow theme CSS with theme contract variables
- Add theme contract variables (--accent-primary, --accent-secondary,
  --accent-gradient, --border-glow) to tokens.css :root block
- Create theme-neon-glow.css with body.theme-neon-glow overrides:
  deeper blacks, OC cyan (#00c8ff) to purple (#7b2fff) brand palette,
  subtle border glow, and semantic color aliases
- Import theme-neon-glow.css in main.ts alongside other style imports
2026-03-27 11:32:36 +01:00
jevb 1eaf9ad72c feat: add sidebarMode and activeDmUserId to UI store
Extends UiState with sidebarMode ("channels" | "dms") and
activeDmUserId (number | null). Adds setSidebarMode (which auto-clears
activeDmUserId when switching back to channels) and setActiveDmUser
actions. All three new tests pass (TDD: RED → GREEN).
2026-03-27 11:29:23 +01:00
jevb efb3f1364a chore: add .superpowers/ to gitignore 2026-03-27 10:47:06 +01:00
jevb a6bb4e77ec fix: regenerate ICO with proper multi-size PNG entries (7 sizes) 2026-03-27 10:25:40 +01:00
jevb 73ff065026 fix: move status dot out of icon div to fix letter centering 2026-03-27 10:13:31 +01:00
jevb 192d779775 feat: replace app icon with OC neon glow logo 2026-03-27 10:09:53 +01:00
jevb 823f9dedea fix: replace form logo with OC neon glow SVG to match branding 2026-03-27 10:05:18 +01:00
jevb 54ea355501 fix: server icon shows single initial for compact 28px size 2026-03-27 10:04:08 +01:00
jevb 6bc6b66ce4 feat: login screen redesign with OC branding and animations
Split layout redesign with deep indigo gradient left panel,
OC neon glow logo (orange→pink→purple→cyan), accent stripe
server cards, and full motion animations.

- Left panel: gradient background with ambient glow spots
- OC logo: SVG lettermark with Gaussian blur glow pulse
- Server cards: compact rows with purple accent stripe
- Inputs: 12px border-radius with focus glow ring
- Button: gradient with box-shadow and shimmer on hover
- Animations: panel slide-in, logo pulse, card stagger,
  button shimmer, all respecting prefers-reduced-motion
2026-03-27 10:00:03 +01:00
jevb d9c6da2bd1 fix: remember password now actually stores the password
The Rust save_credential command was accepting but ignoring the
password parameter (_password). Now stores it in the Windows
Credential Manager JSON blob alongside username and token.

DPAPI encrypts the blob at rest, tied to the Windows user —
plaintext is never written to disk.

Also wire the checkbox state to the profile's rememberPassword
field so it persists across sessions. Previously it was
hardcoded to false on every login.
2026-03-27 08:48:54 +01:00
jevb bb2194849a fix: connection stats RTT detection and rate formatting
- Collect stats from both publisher AND subscriber PeerConnections
  (RTT is typically on the subscriber PC in LiveKit's SFU model)
- Accept candidate-pair with valid RTT regardless of state
  (not just "succeeded" — subscriber may report "in-progress")
- Round raw byte values in formatBytes to avoid 15-digit floats
- Use max instead of sum for totalUp/totalDown across candidate
  pairs to avoid double-counting across PCs
2026-03-27 08:41:34 +01:00
jevb f1517566ab feat: voice connection quality indicator with transport stats
Add a signal-bars icon + ping text to the voice widget header
that shows real-time connection quality. Clicking it expands a
transport statistics pane with outgoing/incoming rates, packet
counts, RTT, and session totals.

- New lib/connectionStats.ts: polls WebRTC RTCPeerConnection
  stats every 2s, computes quality level from RTT thresholds
- Signal icon with 4-tier coloring: green (<100ms), yellow
  (100-200ms), red (>200ms), with 1-4 bars lit
- Expandable stats pane between header and controls
- Auto-starts/stops poller on voice connect/disconnect
- New createSignalIcon() in icons.ts for per-bar coloring
- getRoomForStats() export from livekitSession.ts
2026-03-27 08:35:37 +01:00
jevb 0983c34d59 fix: client code review — 27 fixes across 17 files
Dual-model review (Claude + Codex) found 1 CRITICAL, 9 HIGH,
10 MEDIUM, 7 LOW issues. All addressed:

Security:
- SSRF protection: block link previews to private/internal IPs
- Context-menu dismiss listener leak on item click

Type safety:
- Replace `as any` casts with LocalVideoTrack/LocalTrack types
- loadPref runtime typeof validation against corrupted localStorage
- handleLocalTrackPublished typed with LocalTrackPublication

Memory leaks / listener cleanup:
- MessageInput.destroy() closes open pickers before aborting
- ChannelSidebar/VoiceChannel dismiss setTimeout race guard
- Context-menu item click aborts dismiss AbortController

Async correctness:
- attemptAutoReconnect takes AbortSignal; leaveVoice() aborts it
- OG dedup returns shared in-flight Promise (not empty placeholder)
- Clipboard copy handles rejection with user feedback

State management:
- setMembers() clears typing timers on reconnect
- prependMessages() sets hasMore=true when trimming to cap
- setVoiceStates() preserves currentChannelId when user absent

Code quality:
- VideoGrid: createIcon() replaces innerHTML SVG strings
- VideoGrid: single setUserVolume on unmute (no volume spike)
- api.ts: shared doFetch() eliminates request/adminRequest duplication
- livekitSession: __owncord.lkDebug namespace replaces __lkDebug
- console.error replaced with structured logger
- YouTube oembed uses tauriFetch for consistency
2026-03-27 08:05:32 +01:00
jevb 8cf90c916d t 2026-03-26 22:13:47 +01:00
jevb b9343caf8e feat: add RNNoise worklet TypeScript source 2026-03-26 22:10:49 +01:00
jevb 0edab9b711 chore: remaining unstaged changes — rnnoise worklet, tests, renderers
- RNNoise worklet: extract constants, output ring buffer refactor
- ChannelSidebar: video mode controller integration
- Renderers: message list rendering updates
- MainPage/SidebarArea/VideoModeController: video mode wiring
- Tests: expanded coverage for channel-sidebar, renderers, video-grid,
  video-mode-controller
- TODOS.md: updated task tracking
2026-03-26 22:09:56 +01:00
jevb c628a62565 feat: stream quality presets, nuclear mute, sidebar width fix
- Add stream quality selector (Low/Medium/High/Source) in Voice & Audio
  settings with per-preset bitrate and resolution for camera + screenshare
- Use createLocalVideoTrack/createLocalScreenTracks + publishTrack for
  explicit encoding control (bypasses LiveKit conservative defaults)
- Source quality: 8Mbps camera, 10Mbps screenshare, no adaptive/dynacast
- Nuclear mute: fully unpublish mic track when muting, re-publish on
  unmute — guarantees SFU has no audio to forward
- Listen for LocalTrackPublished to re-enforce mute on renegotiation
- Store manually published tracks for explicit unpublish on disable
- VAD updatePipelineGain respects mute/deafen state
- Widen channel sidebar from 240px to 260px, add overflow handling
  so voice user icons don't clip and cause horizontal scrollbar
- Remove invalid LiveKit server-side room config fields
2026-03-26 22:07:52 +01:00
jevb 15b779f86b fix: voice mute pipeline, security hardening, and video tile controls
- Deafen now also mutes the local microphone (privacy fix)
- Mute/deafen zero the audio pipeline GainNode to guarantee silence
  when the replaced sender track bypasses LiveKit's track disable
- Send voice_leave on failed auto-reconnect to prevent ghost states
- Gate voice_join on LiveKit availability (reject if h.livekit == nil)
- IP-restrict /api/v1/livekit/webhook to admin CIDRs
- Warn at startup when LiveKit API keys are auto-generated (ephemeral)
- Fix connecting guard: move pending-join dispatch outside finally block
- Add volume slider + mute button overlay on remote video tiles
- Document token refresh limitation for 4h+ sessions
- Fix TS2306 in rnnoise-worklet test (ts-expect-error for worklet import)
2026-03-26 20:53:09 +01:00
jevb c13d5a67a5 docs: add specs for screenshare audio and video focus mode
Two specs for the video/screenshare experience:
1. SCREENSHARE-AUDIO.md — system audio capture + per-tile mute
2. VIDEO-FOCUS-MODE.md — Discord-style opt-in viewing with focus layout
2026-03-26 18:39:50 +01:00
jevb 6ca18762cb feat: full screenshare support — button state, video grid, auto-reconnect
- Add enableScreenshare/disableScreenshare to LiveKitSession with
  proper LiveKit track publishing, error handling, and WS notification
- VoiceWidget screenshare button now shows active state (red highlight,
  icon swap, aria-pressed) matching mute/deafen/camera pattern
- VideoModeController activates video grid for screenshare (not just
  camera), with local self-view tile using ID offset to avoid collision
- MainPage voice store subscription now watches screenshare state
  changes to trigger checkVideoMode automatically
- Reset localScreenshare on leaveVoice to prevent stale button state
- Add auto-reconnect on unexpected LiveKit disconnect (2 attempts with
  3s delay, fresh token request on success)
- Fix pre-existing missing reapplyAudioProcessing mock in settings test
- 8 new tests covering screenshare button, video grid activation,
  tile lifecycle, and state cleanup
2026-03-26 18:07:20 +01:00
jevbandclaude-flow 43a11c7b90 fix: invert sensitivity slider direction to match Discord UX
Drag LEFT = easier for mic to pass (high sensitivity),
drag RIGHT = harder (low sensitivity). Inverts both the
threshold indicator position and pointer-to-value mapping.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-25 19:15:50 +01:00
jevbandclaude-flow a0731cf030 fix: voice audio pipeline — autoplay unlock and GainNode-based VAD
- Add --autoplay-policy=no-user-gesture-required to WebView2 config
  so remote participants' audio plays immediately on join (desktop app
  doesn't need browser autoplay restrictions)
- Add AudioPlaybackStatusChanged handler with click-to-unlock fallback
  for browsers that still block autoplay
- Replace broken VAD implementation that used setMicrophoneEnabled/
  mediaStreamTrack.enabled (both fought LiveKit's track lifecycle) with
  a unified GainNode audio pipeline:
    rawMic → AnalyserNode (VAD) → GainNode (volume × gate) → sender
- VAD now gates by setting gain=0 instead of touching the track —
  analyser always sees real audio, no stale track references
- Merge input volume and VAD into single pipeline (always active)
- Add voice settings UI: draggable sensitivity threshold on mic meter,
  input/output volume sliders (0-200%), audio processing toggles

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-25 19:07:52 +01:00
jevb 2be2c5c23b chore: gitignore .claude-flow/ and .mcp.json 2026-03-25 17:29:34 +01:00
jevb 0a194c042c fix: remove invalid active_loopback_prevention from LiveKit config
Field doesn't exist in current LiveKit SFU AudioConfig struct,
causing YAML unmarshal error on startup.
2026-03-25 17:28:14 +01:00
jevb de666d515a docs: sync documentation with codebase — version alignment, config fixes, test scripts
- Fix version misalignment: CLAUDE.md had 1.3.0, README had 1.0.0, actual is 1.2.0
- Fix README config table: upload.max_size_mb 10→100, tls.mode selfsigned→self_signed
- Add missing test scripts to CLAUDE.md (e2e:native, e2e:prod, e2e:ui, watch)
- Add documentation audit report
2026-03-24 21:42:16 +01:00
jevb 7404347a1d fix: address code review — 8 issues across server and client
Server fixes:
- voice_leave: broadcast voice_leave even on DB error so peers don't see ghost users (H1)
- migrate: record migration inside transaction for atomicity (H2)
- password: use init() with panic for dummyHash to catch bcrypt init failures (M1)
- password: replace //nolint:errcheck with explicit _ = discard (M2)
- handlers: clarify edit permission comment, fix error message wording (M3)
- auth_handler: distinguish duplicate username (400) from DB error (500) (M4)

Client fixes:
- media: revert YouTube oEmbed to browser fetch — no need to disable cert verification (C1)
- messages.store: fix prependMessages cap to keep newest messages, not oldest (H5)
- ChannelSidebar: ref-count globalDragAc to prevent multi-instance teardown race (H6)
- attachments: replace console.error with project logger (M6)
- ws: clarify lastSeq reset comment to match actual behavior (M5)
2026-03-24 21:35:40 +01:00
jevb e0437d4d8d feat: LiveKit migration — permissions, auth hardening, voice improvements
Pre-review snapshot of LiveKit migration changes including:
- Permission computation fix (allow-wins semantics)
- Timing-safe password comparison with dummy hash
- Rate limiter window fix
- Dev credential clearing for LiveKit
- Voice leave/join broadcast improvements
- Migration transaction wrapping
- Chat edit/delete permission guards
- TOTP verification endpoint
- Embed regex injection fix
2026-03-24 21:30:23 +01:00
jevb 3f58345e6c fix: address code review — security hardening, leak fixes, credential safety
CRITICAL:
- Add AuthMiddleware + rate limiting to /livekit/* proxy route (was unauthenticated)
- Remove well-known default LiveKit credentials from source; auto-generate unique
  random keys on first run so voice works out of the box securely
- Reject the old "devkey"/"owncord-dev-secret-key-min-32chars" in NewLiveKitClient

HIGH:
- Add 5s timeouts to RemoveParticipant/ListParticipants SDK calls (goroutine leak)
- Fix config.yaml default file permissions from 0644 to 0600
- Fix voice store desync on unexpected LiveKit disconnect (phantom UI state)
- Add in-flight guard to handleVoiceToken (race on rapid channel switch)
- Fix lightbox listener leak on rapid reopen (orphaned mousemove/mouseup/keydown)
- Fix allTracked WeakRef set unbounded growth in media-visibility
- Replace debug console.log with createLogger in embeds.ts
- Stop persisting password in Windows credential blob (only token needed)
2026-03-24 20:23:40 +01:00
jevb e03456527b fix: resolve LiveKit voice issues — duplicate audio, tunnel effect with 5+ users
- Detach existing audio elements before attaching to prevent double playback on reconnects
- Remove webAudioMix to eliminate Web Audio overhead compounding with multiple participants
- Use participant.setVolume() for full 0-200% per-user volume range
- Add pli_throttle and active_loopback_prevention to LiveKit server config
- Bump version to 1.3.0
2026-03-22 21:10:02 +01:00
jevb eacee4f780 fix: resolve TS2556 spread argument error in livekit-session test 2026-03-22 20:14:27 +01:00
jevb a87824f2a5 fix: resolve CI failures — lint errors and coverage threshold
Server: suppress errcheck on deferred Close() calls, discard
resp.Body.Close error, remove unused voiceSpeakersPayload type
and buildVoiceSpeakers func.

Client: add unit tests for os-motion, livekitSession, and
safe-render to bring coverage from 74.16% to 76.06% (threshold 75%).
2026-03-22 20:06:59 +01:00
jevb eb08c792aa docs: add voice metrics and DESIGN.md TODOs from review
- Add voice session metrics TODO (P3)
- Add DESIGN.md creation TODO (post-merge)
2026-03-22 19:30:11 +01:00