fix: assorted bug fixes for lists, sounds, interrupts, and permissions #173

Merged
naomi merged 5 commits from fix/lists into main 2026-02-26 23:34:51 -08:00
Owner

Summary

  • Markdown lists: Explicitly set list-style-type: disc / decimal in the Markdown renderer — Tauri's WebView strips browser defaults, leaving bullets and numbers invisible.
  • Notification sounds: Moved all per-task sounds (success, error, permission, task-start) from a global characterState subscription into the per-conversation claude:state event handler, so background tabs receive their sounds correctly and tab-switching never replays a sound that already fired. Closes #172
  • Draft text: Persists inputValue per conversation tab so a half-typed prompt survives switching to another tab and back.
  • Interrupt messages: Replaced vague "Process interrupted" / "Disconnected" strings with source-specific descriptions (keyboard shortcut, stop button, unexpected crash) so it's clear what actually happened.
  • Silent prompt loss: When Claude Code exits whilst a prompt is in-flight, emits a visible error line telling the user their last prompt was not processed and to reconnect and retry.
  • Double disconnect: Added an intentional_stop flag to WslBridge so that stop() / interrupt() — which kill the process themselves — suppress the duplicate "Disconnected unexpectedly" message that handle_stdout's EOF path was also emitting.
  • Permission modal: Fixed two cooperating reactivity bugs — pendingPermissions was mutated in-place (.push()), causing Svelte's derived-store chain to receive the same array reference and skip re-rendering; PermissionModal.svelte also used $state() (runes mode) where plain let is required for correct store-subscription reactivity.

Test plan

  • Unordered and ordered lists render with visible bullets and numbers in the chat terminal
  • Completion sound plays once when a background tab finishes; switching back to that tab does not replay it
  • Sounds for error, permission request, and task-start also play for background tabs and do not replay on tab switch
  • Typing a prompt, switching tabs, and switching back restores the draft text
  • Pressing Ctrl+C shows "keyboard shortcut (Ctrl+C)"; clicking the stop button shows "via stop button"
  • If Claude exits mid-request, an error message appears prompting the user to resend
  • Clicking stop or pressing Ctrl+C produces exactly one disconnect message (not two)
  • When a tool requires permission, the permission modal appears and the user can approve or dismiss it

This PR was created with help from Hikari~ 🌸

## Summary - **Markdown lists**: Explicitly set `list-style-type: disc` / `decimal` in the Markdown renderer — Tauri's WebView strips browser defaults, leaving bullets and numbers invisible. - **Notification sounds**: Moved all per-task sounds (success, error, permission, task-start) from a global `characterState` subscription into the per-conversation `claude:state` event handler, so background tabs receive their sounds correctly and tab-switching never replays a sound that already fired. Closes #172 - **Draft text**: Persists `inputValue` per conversation tab so a half-typed prompt survives switching to another tab and back. - **Interrupt messages**: Replaced vague "Process interrupted" / "Disconnected" strings with source-specific descriptions (keyboard shortcut, stop button, unexpected crash) so it's clear what actually happened. - **Silent prompt loss**: When Claude Code exits whilst a prompt is in-flight, emits a visible error line telling the user their last prompt was not processed and to reconnect and retry. - **Double disconnect**: Added an `intentional_stop` flag to `WslBridge` so that `stop()` / `interrupt()` — which kill the process themselves — suppress the duplicate "Disconnected unexpectedly" message that `handle_stdout`'s EOF path was also emitting. - **Permission modal**: Fixed two cooperating reactivity bugs — `pendingPermissions` was mutated in-place (`.push()`), causing Svelte's derived-store chain to receive the same array reference and skip re-rendering; `PermissionModal.svelte` also used `$state()` (runes mode) where plain `let` is required for correct store-subscription reactivity. ## Test plan - [ ] Unordered and ordered lists render with visible bullets and numbers in the chat terminal - [ ] Completion sound plays once when a background tab finishes; switching back to that tab does not replay it - [ ] Sounds for error, permission request, and task-start also play for background tabs and do not replay on tab switch - [ ] Typing a prompt, switching tabs, and switching back restores the draft text - [ ] Pressing Ctrl+C shows "keyboard shortcut (Ctrl+C)"; clicking the stop button shows "via stop button" - [ ] If Claude exits mid-request, an error message appears prompting the user to resend - [ ] Clicking stop or pressing Ctrl+C produces exactly one disconnect message (not two) - [ ] When a tool requires permission, the permission modal appears and the user can approve or dismiss it ✨ This PR was created with help from Hikari~ 🌸
hikari added 5 commits 2026-02-26 22:42:27 -08:00
Prevents sounds from re-firing on tab switch and ensures background
tab completions receive their sounds. All sounds now fire from the
claude:state event handler in tauri.ts using per-conversation flags,
with rules.ts retained only for the connection sound.

Closes #172
Draft text typed in the input bar is now stored per-tab so switching
conversations no longer clears an in-progress prompt.
Messages now indicate how the interrupt was triggered (keyboard shortcut,
stop button, or unexpected crash) so the cause is immediately clear.
fix: restore permission modal reactivity by replacing array mutation with new array creation
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m6s
CI / Lint & Test (pull_request) Successful in 16m21s
CI / Build Linux (pull_request) Successful in 20m11s
CI / Build Windows (cross-compile) (pull_request) Successful in 30m24s
aa40d09b29
Previously, pendingPermissions was mutated in-place via .push(), causing
Svelte's reactivity chain to potentially receive the same array reference
and skip re-rendering the PermissionModal. Switching to spread syntax
guarantees a new reference on every update.

Also removed $state() from PermissionModal's local variables to match
the Svelte 4 reactive pattern used by other working components (Terminal),
avoiding rune-mode signal equality short-circuits.
naomi merged commit 89a0bdd8f1 into main 2026-02-26 23:34:51 -08:00
naomi deleted branch fix/lists 2026-02-26 23:34:51 -08:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: nhcarrigan/hikari-desktop#173