# Sounds Tab Design Date: 2026-04-05 ## Context Cthulhu currently stores several non-speech sound settings in the general settings file, but the preferences dialog does not present them coherently: - The General tab contains an inline Sound section for backend, theme, and role-sound presentation. - The progress-bar beep checkbox lives in the General tab's progress-bar section. - `soundVolume` exists in settings and the user's active profile, but there is no UI control for it. - `enableSound`, `playSoundForRole`, `playSoundForState`, `playSoundForPositionInSet`, and `playSoundForValue` exist as persisted settings but are not currently exposed in the dialog. - `progressBarBeepInterval` exists in runtime defaults but is not part of the normal persisted general-settings key list. The result is that sound-related behavior is split across the dialog and some active settings cannot be inspected or changed from the UI. ## Goals - Add a dedicated `Sounds` preferences tab. - Move currently exposed sound-related controls out of `General` and into `Sounds`. - Move the progress-bar beep option into `Sounds` while leaving the rest of the progress-bar section in `General`. - Expose currently hidden sound-related settings in the UI. - Add a UI control for `soundVolume`. - Expose and persist `progressBarBeepInterval`. - Preserve the existing settings model and save format. - Avoid clobbering unrelated settings when saving preferences. ## Non-Goals - No refactor of the broader preferences architecture. - No changes to sound playback implementation outside what is needed to load and save settings correctly. - No new settings schema beyond persisting the existing hidden `progressBarBeepInterval`. - No dynamic enable/disable logic for sound controls in this pass. ## Design Summary Add a new notebook page labeled `Sounds` and make it the single home for non-speech sound configuration. Remove the inline Sound frame from `General`. Keep the existing progress-bar speech/braille controls and shared progress-bar behavior controls in `General`, but move beep-specific controls into `Sounds`. The new tab will use existing settings keys wherever possible and will save through the existing `prefsDict` and settings-manager flow. ## Sounds Tab Layout The `Sounds` tab will contain three sections. ### Output - `enableSound` checkbox - `soundVolume` slider - `soundSink` combo box `soundVolume` should be represented by a native `GtkScale` and persisted back to the existing floating-point `soundVolume` setting. The control should initialize from `prefsDict.get("soundVolume", settings.soundVolume)`. For this implementation, the slider should match the stored setting directly rather than introducing a new percentage representation. That keeps the implementation small and avoids conversion-only complexity in a settings path that already exists. ### Presentation - `soundTheme` combo box - `roleSoundPresentation` combo box - `playSoundForRole` checkbox - `playSoundForState` checkbox - `playSoundForPositionInSet` checkbox - `playSoundForValue` checkbox These controls will expose already-existing settings and use the same `prefsDict` update pattern already used by the current dialog. ### Alerts - `beepProgressBarUpdates` checkbox - `progressBarBeepInterval` spin button This section controls only beep-based progress-bar alerts. The label should make it clear that the interval is beep-specific, not the general progress-bar announcement interval. ## General Tab Changes Keep these controls in `General`: - `speakProgressBarUpdates` - `brailleProgressBarUpdates` - `progressBarUpdateInterval` - `progressBarVerbosity` - `ignoreStatusBarProgressBars` Remove these from `General`: - the inline Sound frame - `beepProgressBarUpdates` This preserves the current organization for general progress-bar announcement behavior while moving sound-only concerns into the new tab. ## Settings and Persistence ### Existing Keys Reused The new UI will continue to use the existing settings keys: - `enableSound` - `soundVolume` - `soundSink` - `soundTheme` - `roleSoundPresentation` - `playSoundForRole` - `playSoundForState` - `playSoundForPositionInSet` - `playSoundForValue` - `beepProgressBarUpdates` ### Persisted Hidden Key Add `progressBarBeepInterval` to the persisted general-settings key list so it round-trips through: - defaults in `settings.py` - settings-manager general settings - TOML backend save/load - `prefsDict` in the preferences dialog This avoids the current mismatch where beep interval exists as a runtime default but is not part of the normal persisted general-settings path. ### Save Behavior The implementation should continue to update individual keys in `prefsDict` and then rely on the existing general-settings save flow. No bulk replacement of the whole settings structure should be introduced. This is specifically intended to avoid accidental overwrites of unrelated settings already present in `user-settings.toml`. ## Default Values - Keep `progressBarUpdateInterval = 10` as the default for speech and braille progress-bar updates. - Keep `progressBarBeepInterval = 0` as the default for beep-based progress-bar updates. - Keep `soundVolume = 0.5` as the existing default unless the user explicitly changes it. The new UI should reflect these values accurately on load. ## Accessibility Use native GTK controls and follow the existing preferences-dialog patterns: - every label must be associated with its control via `mnemonic_widget` - checkboxes should use the existing `checkButtonToggled` convention when possible - combo boxes should keep proper `label-for` / `labelled-by` relationships - no custom-drawn controls - no keyboard traps The new tab should be fully reachable with standard tab navigation. ## Implementation Notes Expected files to change: - `src/cthulhu/cthulhu-setup.ui` - `src/cthulhu/cthulhu_gui_prefs.py` - `src/cthulhu/settings.py` Implementation should: - add the new notebook page and move or recreate the relevant widgets there - initialize and save the new sound controls using existing dialog patterns - add explicit handling for `soundVolume` - add explicit handling for `progressBarBeepInterval` - keep all unrelated preference behavior unchanged ## Verification The implementation should be considered complete only after all of the following are checked: 1. The preferences dialog opens and the new `Sounds` tab is present. 2. Existing sound settings load into the correct controls. 3. Changing sound settings updates `prefsDict` without breaking unrelated settings. 4. Saving preferences preserves unrelated keys in `user-settings.toml`. 5. `progressBarBeepInterval` is written and reloaded correctly. 6. The General tab still handles speech/braille progress-bar settings correctly. 7. Touched Python files pass compile checks. 8. Manual verification confirms keyboard navigation and label association remain intact.