docs: update TODOS.md and DESIGN.md for open-source-ready pass

TODOS.md: Add 3 deferred items — soundboard feature, DM sidebar
incremental reconciliation, api+ws coverage push to 80%.

DESIGN.md: Add popover component pattern spec and 2 decision log entries
for popover pattern and DM profile sidebar.
This commit is contained in:
jevb
2026-04-01 11:39:55 +02:00
parent 4485c4f021
commit b718447e64
2 changed files with 231 additions and 0 deletions
+143
View File
@@ -0,0 +1,143 @@
# Design System — OwnCord
## Product Context
- **What this is:** Self-hosted Windows chat platform with voice, video, DMs, and text channels
- **Who it's for:** Gamers and small groups who want Discord-like features on their own hardware
- **Space/industry:** Self-hosted communication (peers: Discord, TeamSpeak, Revolt, Element)
- **Project type:** Desktop app (Tauri v2 — Rust backend, TypeScript frontend)
## Aesthetic Direction
- **Direction:** Industrial/Utilitarian with Neon accents
- **Decoration level:** Intentional — subtle border glow (`--border-glow`), gradient accents, no heavy textures or patterns. The glow IS the decoration.
- **Mood:** A dark room with neon light strips. Function-first, data-dense, but with personality. Not sterile, not flashy — purposeful.
- **Reference sites:** Discord (layout conventions), Revolt (theming depth), TeamSpeak (audio-first UX)
## Typography
- **Display/Hero:** Segoe UI Variable Display — Windows-native, sharpest rendering on the target platform
- **Body:** Segoe UI Variable Text — consistent with Display, optimized for readability at small sizes
- **UI/Labels:** Same as body (10-12px uppercase with letter-spacing for section headers)
- **Data/Tables:** Segoe UI Variable Text with `font-variant-numeric: tabular-nums` — aligned columns
- **Code:** Cascadia Code — ships with Windows Terminal, ligature support
- **Loading:** No web fonts. System-native stack: `"Segoe UI Variable Display/Text", "Segoe UI", system-ui, sans-serif`
- **Scale:**
- `--font-size-xxs`: 10px (micro labels, section headers)
- `--font-size-xs`: 12px (timestamps, muted text, channel descriptions)
- `--font-size-sm`: 13px (secondary content, voice user names)
- `--font-size-md`: 14px (body text, messages, input fields)
- `--font-size-lg`: 16px (channel names, headings)
- `--font-size-xl`: 20px (page titles, modal headers)
- `--font-size-xxl`: 24px (hero text, onboarding)
## Color
- **Approach:** Balanced — primary + secondary accent with full semantic palette
- **Primary accent:** `#00c8ff` (cyan) — the OwnCord signature. Used for links, focus rings, active states
- **Secondary accent:** `#7b2fff` (purple) — paired with cyan in gradient. Used for hover accent, gradient endpoints
- **Accent gradient:** `linear-gradient(135deg, #00c8ff, #7b2fff)` — the brand signature. Used on primary buttons, hero text, logo
- **Neutrals (Neon Glow theme):**
- `#0d0e10` bg-tertiary (deepest — app shell)
- `#111214` bg-secondary (sidebar, panels)
- `#1a1b1e` bg-primary (main content area)
- `#1f2023` bg-hover
- `#252629` bg-input
- `#2a2b2e` bg-active
- **Text hierarchy:**
- `#f2f3f5` header-primary (brightest — page titles)
- `#b5bac1` header-secondary (section headers)
- `#dbdee1` text-normal (body text)
- `#949ba4` text-muted (secondary info)
- `#80848e` text-faint (timestamps, placeholders)
- `#6d6f78` text-micro (lowest contrast)
- **Semantic:**
- Success: `#23a55a` (green — online, speaking, connected)
- Warning: `#f0b232` (yellow — poor connection, caution)
- Danger: `#f23f43` (red — error, disconnect, deafened)
- Info: `#00c8ff` (cyan — accent doubles as info)
- **Border glow:** `rgba(0, 200, 255, 0.08)` — subtle neon border treatment on panels and inputs. Increases to `0.15` on `:focus` and strong borders.
- **Dark mode:** This IS dark mode. The default theme uses Discord-standard dark tokens. Neon Glow deepens the blacks and adds cyan border glow. User can override accent via accent color picker.
## Spacing
- **Base unit:** 4px
- **Density:** Comfortable (not compact, not spacious — matches Discord)
- **Scale:** 2xs(2) xs(4) sm(8) md(16) lg(24) xl(32) 2xl(48) 3xl(64)
- **Key dimensions:**
- Sidebar width: 240px
- Header height: 48px
- Avatar size: 40px (messages), 24px (voice users, member list)
- Message group spacing: 17px
- Message content left offset: 72px (avatar + padding)
## Layout
- **Approach:** Grid-disciplined — fixed sidebar + flexible content area. Discord conventions.
- **Grid:** Sidebar (240px fixed) | Content (flex) | Optional member list (240px, collapsible)
- **Max content width:** None (fills available space minus sidebar)
- **Border radius:**
- `--radius-sm`: 4px (buttons, inputs, small elements)
- `--radius-md`: 8px (cards, panels, modals)
- `--radius-lg`: 16px (large containers, settings panel)
- `--radius-pill`: 24px (badges, pills, tags)
- `--radius-circle`: 50% (avatars)
## Motion
- **Approach:** Minimal-functional — only transitions that aid comprehension. Chat apps need speed, not spectacle.
- **Easing:** All use `ease` (CSS default). No custom cubic-bezier needed.
- **Duration:**
- `--transition-fast`: 100ms (icon swaps, hover states, speaking indicators)
- `--transition-normal`: 170ms (panel transitions, button state changes)
- `--transition-slow`: 200ms (modal open/close, overlay fade)
- **Animations:**
- Speaker glow pulse: `1.5s ease-in-out infinite` on `box-shadow` (green → brighter green → green)
- VAD meter bars: 50ms updates (matches audio analysis frame rate)
- Settings panel: scale animation on open (existing)
- Toast: slide-in from top-right, auto-dismiss after 5s
## Voice-Specific Design Tokens
These extend the base system for voice/video UI elements:
- **Speaker indicator (speaking):** `box-shadow: 0 0 0 2px var(--green), 0 0 8px rgba(35, 165, 90, 0.3)` with pulse animation
- **Connection quality colors:** excellent = `--green`, fair = `--yellow`, poor/bad = `--red`
- **VAD meter bars:** 4 bars, 4px wide, 12px max height. Active = `--green`, inactive = `--text-faint`
- **Grant Mic button:** `background: var(--accent)`, white text, full-width in voice widget
- **Reconnect button:** Same styling as Grant Mic
- **Quality warning banner:** `background: rgba(240, 178, 50, 0.08)`, `color: var(--yellow)`, `border-top: 1px solid rgba(240, 178, 50, 0.15)`
- **Volume slider tooltip:** Shows percentage on hover (e.g., "75%")
## Theming
- **Base theme:** Discord-standard dark tokens in `tokens.css`
- **Neon Glow theme:** Override class `body.theme-neon-glow` deepens backgrounds, adds cyan border glow, sets cyan-purple gradient accent
- **Accent override:** User can pick any accent color via color picker. `--accent-primary` and `--accent-gradient` derive from the user's choice.
- **Custom themes:** JSON import/export with CSS value sanitization (no CSS injection)
## Accessibility
- All interactive elements: `aria-label` + keyboard focusable
- Toggle buttons: `aria-pressed` attribute
- Status regions: `role="status"` + `aria-live="polite"`
- Alert toasts: `role="alert"` + `aria-live="assertive"`
- Decorative elements (VAD meter): `aria-hidden="true"`
- Minimum touch/click target: 32px (buttons), 44px recommended for primary actions
- Color contrast: all text tokens meet WCAG AA against their intended backgrounds
## Components
### Popover (anchored)
- **Position:** Anchored to trigger element. Flip horizontally/vertically when <100px from viewport edge. Clamp to viewport bounds.
- **Dimensions:** Content-driven, min-width 200px
- **Background:** `--bg-secondary` (#111214)
- **Border:** 1px `--border-glow` (rgba(0, 200, 255, 0.08))
- **Radius:** `--radius-md` (8px)
- **Shadow:** `0 8px 16px rgba(0, 0, 0, 0.24)` (elevation-medium)
- **Animation:** Fade + scale from 0.95 → 1.0 over 100ms (`--transition-fast`)
- **Dismiss:** Outside click (mousedown on overlay), Escape key
- **A11y:** `role="dialog"`, `aria-modal="true"`, `aria-label` required. Focus trap with Tab/Shift+Tab cycling. Return focus to trigger element on close.
- **First implementation:** `UserProfilePopup.ts` (300px wide, user profile content)
- **Reuse for:** Context menus, message action menus, future feature popovers
## Decisions Log
| Date | Decision | Rationale |
|------|----------|-----------|
| 2026-03-28 | Initial design system created | Formalized existing tokens.css + theme-neon-glow.css via /design-consultation. No new design — documented what exists. |
| 2026-03-28 | Speaker indicator: pulsing green glow | Current flat ring (2px box-shadow) lacks animation. Added pulse keyframes + outer glow for visibility. Matches Discord's animated green ring. |
| 2026-03-28 | Windows-native typography only | Segoe UI Variable is the sharpest font on Windows. No web font loading latency. Risk: cross-platform would need fallback stack. |
| 2026-03-28 | Neon gradient as brand identity | Cyan-to-purple gradient distinguishes OwnCord from Discord (blurple), Slack (aubergine), TeamSpeak (blue). |
| 2026-04-01 | Popover component pattern | Anchored popovers for UserProfilePopup. Edge-flip positioning, focus trap, fade+scale animation. Documented as reusable pattern for future context menus. |
| 2026-04-01 | DM Profile Sidebar: right panel 340px | Same slot as member list (hidden in DM mode). Slide-in 170ms. Toggle via DM header click. Local-only Note field. |
+88
View File
@@ -0,0 +1,88 @@
# TODOS
Deferred work items from engineering reviews.
## Completed (2026-03-29 voice/video polish pass)
- ~~Voice E2E Test Infrastructure~~ -- `tests/e2e/voice-lifecycle.spec.ts` (11 tests)
- ~~Voice Session Metrics~~ -- `voice_sessions` counter on `/api/v1/metrics`
- ~~Create DESIGN.md~~ -- full design system documentation at repo root
- ~~Extract AudioPipeline Class~~ -- `audioPipeline.ts`, `audioElements.ts`, `deviceManager.ts` (facade pattern)
- ~~Audio Pipeline + Event Handler Tests~~ -- `audio-pipeline.test.ts` (30 tests), `audio-elements.test.ts` (25 tests)
- ~~HTTPS Proxy Unit Tests~~ -- `livekit_proxy_test.go` (22 tests)
- ~~Migrate VAD to AudioWorklet~~ -- `public/vad-worklet.js` with setTimeout fallback
## Already Implemented (discovered 2026-03-29 — code analysis was stale)
- ~~Simulcast on Camera Video~~ -- `simulcast: quality !== "source"` in publishTrack options (livekitSession.ts:852)
- ~~Adaptive Bitrate on Screenshare~~ -- `dynacast: !isSource` + `adaptiveStream: !isSource` in Room options (livekitSession.ts:187-188)
- ~~LiveKit Proxy Port Exhaustion~~ -- already handles reuse (same host) + cleanup via shutdown channel (different host) in livekit_proxy.rs:196-208
## Deferred (from 2026-03-31 eng review — Mission Control)
### Auto-Pilot Token/Cost Tracking
**What:** Parse Claude CLI output for token usage and display estimated cost per agent job in the Mission Control dashboard.
**Why:** Auto-pilot spawns agents autonomously overnight. Without cost visibility, runs could burn through API credits unexpectedly. A simple per-job token counter provides awareness.
**Pros:** Cost awareness prevents bill shock. Enables setting daily budget limits in auto-pilot config.
**Cons:** Claude CLI output format may change. Token counts are approximate. Anthropic dashboard already shows global usage.
**Context:** Deferred from Mission Control Phase 2 (auto-pilot). Only matters if auto-pilot runs frequently. Consider as Phase 2.5 after auto-pilot ships and usage patterns are clear.
**Depends on:** Auto-pilot scheduler (Phase 2 of Mission Control design).
**Added:** 2026-03-31 (eng review of Mission Control design)
## Deferred (from 2026-03-30 eng review)
### Remote Video Stream Reuse (getRemoteVideoStream)
**What:** The `getRemoteVideoStream(userId, type)` accessor added for sidebar preview can be reused for PiP, mini-player, or notification previews.
**Why:** Currently only used by stream preview hover. Future features (PiP mode, floating mini-player, notification thumbnails) would benefit from the same API.
**Pros:** Zero additional work — the export already exists in livekitSession.ts. This TODO just tracks the reuse opportunity.
**Cons:** None — purely informational. No code change needed.
**Context:** Added during sidebar stream preview eng review (2026-03-30). The method is exported as a bound module-level function, consistent with getLocalCameraStream/getLocalScreenshareStream. Any future consumer can import it directly.
**Depends on:** Sidebar stream preview feature (this PR).
**Added:** 2026-03-30 (eng review of sidebar stream preview)
## Deferred (from 2026-03-29 CEO review)
### Voice E2E CI Integration (narrowed scope)
**What:** Set up LiveKit binary in CI for WebRTC-specific regression testing only.
**Why:** Mocked E2E tests (24 tests in `voice-lifecycle.spec.ts`) cover 90%+ of voice UI regressions. Real LiveKit CI is only needed for audio pipeline bugs, LiveKit SDK regressions, or WebRTC transport issues that mocks can't catch.
**Pros:** Catches WebRTC-specific regressions (codec negotiation, ICE failures, audio pipeline).
**Cons:** Requires Docker-in-CI setup with LiveKit binary. High maintenance for low-frequency bugs.
**Context:** Mocked voice E2E covers: join/leave flow, speaker indicators, permission recovery, device hot-swap, quality warnings, timer, token refresh, channel switching. Only pursue real LiveKit CI if evidence emerges of WebRTC-specific regressions that mocked tests miss.
**Depends on:** Voice E2E test infrastructure (done), mocked voice E2E expansion (done).
**Added:** 2026-03-29 (eng review of voice/video polish), **updated:** 2026-03-29 (scope narrowed after mocked E2E expansion)
**Added:** 2026-03-29 (eng review of voice/video polish)
## Deferred (from 2026-04-01 eng review — Open Source Ready)
### Soundboard Feature
**What:** Full-stack soundboard: sound upload/storage, playback to voice channel, UI component.
**Why:** Community feature for voice channels. Protocol type was pre-defined but nothing else existed.
**Pros:** Fun social feature for voice users. LiveKit supports audio injection.
**Cons:** Requires server-side audio mixing or LiveKit Ingress API. Significant scope (1-2 weeks human / 3-4h CC).
**Context:** Dead `SoundboardPlayPayload` protocol type removed in this PR. Feature needs: upload endpoint, storage, DB schema for sounds, playback handler, broadcast to voice participants, UI picker component. Consider LiveKit Ingress API for server-side audio injection.
**Depends on:** Voice infrastructure (done), LiveKit integration (done).
**Added:** 2026-04-01 (eng review of Open Source Ready plan)
### DM Sidebar Incremental Reconciliation
**What:** Replace O(n) DOM rebuild of DM sidebar with incremental reconciliation (differ/patch per DM item).
**Why:** Current implementation at SidebarArea.ts destroys and recreates the entire DM sidebar on every store change. Causes visual flicker with many DMs.
**Pros:** Smooth UI, reduced DOM churn, better perceived performance.
**Cons:** Complex refactor across multiple files. Risk of stale state bugs.
**Context:** Marked TODO(H16) in code. The SidebarArea.ts file split is complete (747 lines, SidebarDmSection.ts extracted). Consider the `reconcile.ts` utility that already exists in `lib/`.
**Depends on:** SidebarArea.ts file split (done).
**Added:** 2026-04-01 (eng review of Open Source Ready plan)
### API + WS Test Coverage Push to 80%
**What:** Push api package from 75.4% to 80%+ and ws package from 77.9% to 80%+.
**Why:** These are the only 2 Go packages below the 80% coverage target. All other packages (auth 94.9%, storage 85.1%, config 84.7%, db 80.6%, updater 87.3%, permissions 100%) meet the target.
**Pros:** Uniform 80%+ coverage across all packages.
**Cons:** Remaining gaps are in complex stateful handlers with diminishing returns.
**Context:** API gaps are in deep handler paths (register edge cases, DM list errors, TOTP flows, search rate limiting). WS gaps are in voice control success paths and buildReady filtering. TypeScript client is at 93.3% — well above target.
**Depends on:** Nothing.
**Added:** 2026-04-01 (eng review of Open Source Ready plan)