diff --git a/.claude/settings.local.json b/.claude/settings.local.json index fd3cd36..dc419f4 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -56,7 +56,11 @@ "Bash(sed -n '1,30p' packages/shared/src/components/channel/ChannelHeader.tsx)", "Bash(sed -n '1,30p' packages/shared/src/components/member/MemberListContainer.tsx)", "Bash(timeout 20 npm run dev:web)", - "Bash(timeout 8 npm run dev:web)" + "Bash(timeout 8 npm run dev:web)", + "Bash(grep -E '\"electron\":\\\\s*\"' package.json apps/electron/package.json)", + "Bash(grep -E '\"electron\":\\\\s*\"' package.json)", + "Bash(node --check apps/electron/main.cjs)", + "Bash(node --check apps/electron/preload.cjs)" ] } } diff --git a/CLAUDE.md b/CLAUDE.md index 724b8d4..d603bea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -131,6 +131,8 @@ All platform-specific APIs are accessed via the `usePlatform()` hook: - Profile banner: `userProfiles.bannerStorageId` (optional), resolved to `bannerUrl` in `auth.getPublicKeys`. `auth.updateProfileInternal` takes `bannerStorageId` + `removeBanner` (the remove path also `ctx.storage.delete`s the blob). All four profile card surfaces (`MemberProfilePopout`, `MemberProfileModal`, `MobileMemberProfileSheet`, `UserAreaProfilePopout`) render the image when present, fall back to accent color when not - Voice messages: mic button in `ChannelTextarea` records via `MediaRecorder` (picks `audio/webm;codecs=opus` where supported), stages the resulting `File` through the existing attachment pipeline — no new backend. Receivers render it via the standard `AttachmentAudio` player. Filename convention: `voice-message-{timestamp}.{webm|ogg|m4a}`. Voice-recorded messages set `isVoiceMessage: true` + `peaks: number[]` + `durationSec` in the attachment metadata; `EncryptedAttachment` dispatches those to `VoiceMessagePlayer` (pill with play button + waveform) instead of the full audio card - Push-to-talk: `voiceSettings.inputMode` is `'voice-activity'` (default) or `'push-to-talk'`. Paired with the `voice.pushToTalk` keybind (marked `pressAndHold: true`). `KeybindContext` dispatches `brycord:keybind:voice.pushToTalk:down` / `:up` events — pressAndHold actions never `preventDefault`, so binding PTT to a letter still lets you type. `VoiceContext` reads the settings via the `brycord:voice-settings-changed` window event, listens for the PTT events, and routes them through a configurable release-delay timer before reconciling the LiveKit mic track. All mic-on/mic-off sources (user mute, deafen, server mute, PTT gate) converge on a single `setMicrophoneEnabled` effect +- Plaintext cache: `SearchDatabase` has a `plaintext_cache` table (per-channel cap of 500) that persists decrypted message bodies encrypted at rest by the existing search DB key. `SearchContext` exposes `cachePlaintexts` / `getChannelPlaintexts`. `Messages.tsx` seeds the in-memory `decryptionCache` from it on cold channel open (source-scoped on the full ciphertext so edits naturally invalidate), and writes back after each successful decrypt batch. Cuts the "empty bubbles then fill in" wave on app restart +- Webcam video: camera publish already lived in `VoiceContext.setCamera` via LiveKit `setCameraEnabled`. Now also propagates `isCameraOn` through `voiceStates.updateState`. `CameraTile` attaches a participant's camera track to a `