diff --git a/crates/web/Cargo.toml b/crates/web/Cargo.toml index 367fd4dc..359d7cbc 100644 --- a/crates/web/Cargo.toml +++ b/crates/web/Cargo.toml @@ -33,6 +33,7 @@ web-sys = { version = "0.3", features = [ "ScrollIntoViewOptions", "ScrollBehavior", "ScrollLogicalPosition", "DisplayMediaStreamConstraints", "AudioContext", "BaseAudioContext", "AudioNode", + "AudioBuffer", "AnalyserNode", "MediaStreamAudioSourceNode", "RtcSignalingState", "RtcRtpTransceiver", "Response", "Headers", diff --git a/crates/web/src/app.rs b/crates/web/src/app.rs index 2f6e40ae..c2fb7dbf 100644 --- a/crates/web/src/app.rs +++ b/crates/web/src/app.rs @@ -192,6 +192,11 @@ pub fn App() -> impl IntoView { // the same `UploadQueue`. provide_context(crate::upload_state::UploadQueue::new()); + // Phase 3b T6 — single-instance voice-note player. Each + // `` reads this from context to coordinate + // "starting one pauses any other" per spec §Voice note. + provide_context(crate::voice_note_player::VoiceNotePlayer::new()); + // Phase 3c.3 — per-channel reaction recency. Drives the picker's // "recent" shelf in both the composer's emoji button and the // row's "more reactions" toolbar. The Resource refreshes when diff --git a/crates/web/src/components/attachment/voice_note.rs b/crates/web/src/components/attachment/voice_note.rs index 8d1b5876..b6fab239 100644 --- a/crates/web/src/components/attachment/voice_note.rs +++ b/crates/web/src/components/attachment/voice_note.rs @@ -1,21 +1,439 @@ //! `` — voice-note playback card per //! `docs/specs/2026-04-19-ui-design/files-inline.md` §Voice note. //! -//! T6 of the phase-3b plan lands the full surface (play / pause IconBtn, -//! `