feat(sound): loop a "waiting to connect" cue during SSH agent approval - #148
Merged
Merged
Conversation
When an SFTP connection reaches the SSH agent for a signature, an external agent such as Bitwarden puts up an approval dialog that, for a screen-reader user, often opens behind the main window with nothing to announce it. The connection then just sits there. SFTP auth now calls an optional `AgentAuthNotice` on the connect worker just before the first agent signature request. The UI turns that into a `ConnectAwaitingAgent` event, logs and announces "Waiting for SSH key approval", and starts the new `connect_waiting` cue on a loop. `Connected` and `ConnectFailed` stop it, as does starting or tearing down any connection. `connect_waiting` is a first-class sound event: it shows in Settings' per-event mute list and is validated like the others. No audio ships for it (the built-in pack ships none), so a pack opts in by adding a `connections/connect_waiting.ogg` entry. Looping playback is new: `play_looping_sound_file` decodes once into a `SamplesBuffer`, repeats it, and hands back a `LoopHandle` whose drop (or `stop()`) ends the sound within one audio buffer. It is not counted by `wait_for_playback`, so the exit wait never blocks on it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARKC56K8mFfAPPc98JPfCP
Orinks
pushed a commit
that referenced
this pull request
Sep 2, 2026
The default sound pack ships no audio in this repo. `default_pack_json()`
is `{"sounds": {}}`, the Windows build stages only the exe, prism.dll,
and version.txt, and `ensure_default_pack` writes just the manifest.
Existing users have cues only because the `~/.portkeydrop` -> platform
config migration carried the Python-era pack across; a fresh nightly or
installer install gets a silent default pack.
This adds the one file the `connect_waiting` event (PR #148) needs, at a
suggested assets path. It is wired to nothing yet: bundling the default
pack and pointing `default_pack_json()` at it is a separate decision,
noted in the PR.
connect_waiting.ogg is my own work, contributed under the project's
licence. 48 kHz stereo Ogg Vorbis, 3s, designed to loop.
Changelog: none
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARKC56K8mFfAPPc98JPfCP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a looping "Waiting to connect" sound cue that plays while an SFTP
connection is stalled waiting for the SSH agent to approve the key.
An external SSH agent (Bitwarden's agent, a smartcard) shows that approval in a
dialog that, for a screen-reader user, tends to open behind the Portkey Drop
window with nothing to announce it. The connection then sits there with no
feedback. This fills that gap.
How it works
AgentAuthNotice(portkeydrop-core::protocols) -- a new optionalArc<dyn Fn()>hook onSftpClient/create_client. SFTP auth calls it onceon the connect worker, immediately before the first
authenticate_publickey_withagent signature request -- the exact point an external agent can prompt.
events::notify_awaiting_agentcall,which posts
AppEvent::ConnectAwaitingAgent.MainFrame::handle_eventlogs and announces "Waiting for SSH key approval"and calls
AppState::start_waiting_sound(), which loops theconnect_waitingcue.
ConnectedandConnectFailedcallstop_waiting_sound(); so doesdisconnect_quietly()(top of every connect, and disconnect) andon_close().Looping playback (new in the sound layer)
play_looping_sound_filedecodes the file once into aSamplesBuffer, repeatsit, and returns a
LoopHandle. Dropping the handle -- or calling.stop()--sets a flag the playback source checks per sample, so the loop ends within one
audio buffer.
SoundPlayer::play_event_loopingis the pack-aware wrapper.Looping cues are not counted by
wait_for_playback, so the exit-sound waitnever blocks on one.
Sound event
connect_waitingis registered insound_events.rsunder Connections, so itshows in Settings' per-event mute list and is validated like any other cue. No
audio is bundled for it (the built-in pack ships no audio at all in this repo);
a pack opts in with a
connections/connect_waiting.oggentry. Until then theevent still logs and announces -- just silently.
Tests
player.rs:StopFlagends the source when the flag is set and reports itsreal channel layout;
LoopHandlestops onstop()and on drop; a missing orundecodable file yields no handle; volume 0 yields an inert handle without
touching the device.
events.rs:notify_awaiting_agentpostsConnectAwaitingAgentwithoutwaiting for a reply.
state.rs:start_waiting_sound/stop_waiting_soundare a safe no-op inthe silent (headless / test) state.
sound_events.rs:connect_waitingis a known event.cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,and
cargo test --workspaceall pass. The Linux (Ubuntu) clippy path for thecfg(unix)agent code could not be run locally (no WSL distro); the change tothat function is a new parameter used only in shared code.
Independence from #147
Branched from
dev, not from #147. Both touchsoundpacks/player.rsandCHANGELOG.md, so whichever merges second needs a small conflict resolve in theuseblock / changelog list -- they are otherwise unrelated and can merge ineither order.
🤖 Generated with Claude Code