Add audio feedback for autocomplete suggestions

- Implemented autocomplete sound notification system for accessibility
- Added gentle 150ms chime (800Hz) when autocomplete suggestions appear
- Enhanced AutocompleteTextEdit to accept sound_manager parameter
- Created automatic sound generation for default pack (autocomplete.wav)
- Added play_autocomplete() method to SoundManager with 30% volume
- Sound includes 20ms fade in/out to prevent audio clicks
- Provides valuable audio feedback for screen reader users

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-20 20:53:18 -04:00
parent daca679fb9
commit 2e0c7263b4
3 changed files with 55 additions and 5 deletions

View File

@ -153,12 +153,53 @@ class SoundManager:
"success": "success.wav",
"error": "error.wav",
"expand": "expand.wav",
"collapse": "collapse.wav"
"collapse": "collapse.wav",
"autocomplete": "autocomplete.wav"
}
}
with open(pack_file, 'w') as f:
json.dump(pack_data, f, indent=2)
# Create default autocomplete sound if it doesn't exist
autocomplete_sound = default_dir / "autocomplete.wav"
if not autocomplete_sound.exists():
self.create_autocomplete_sound(autocomplete_sound)
def create_autocomplete_sound(self, output_path: Path):
"""Create a simple autocomplete notification sound"""
try:
# Parameters for a gentle notification chime
sample_rate = 22050 # Lower sample rate for smaller file
duration = 0.15 # Short 150ms sound
frequency = 800 # Pleasant mid-range frequency
# Generate time array
t = np.linspace(0, duration, int(sample_rate * duration), False)
# Create a gentle chime with fade in/out to avoid clicks
tone = np.sin(2 * np.pi * frequency * t)
# Add slight fade in/out to prevent audio clicks
fade_samples = int(sample_rate * 0.02) # 20ms fade
tone[:fade_samples] *= np.linspace(0, 1, fade_samples)
tone[-fade_samples:] *= np.linspace(1, 0, fade_samples)
# Reduce volume to be gentle
tone *= 0.3
# Convert to 16-bit PCM
audio = (tone * 32767).astype(np.int16)
# Write WAV file
with wave.open(str(output_path), 'w') as wav_file:
wav_file.setnchannels(1) # Mono
wav_file.setsampwidth(2) # 16-bit
wav_file.setframerate(sample_rate)
wav_file.writeframes(audio.tobytes())
except Exception as e:
print(f"Failed to create autocomplete sound: {e}")
def load_current_pack(self):
"""Load the currently selected sound pack"""
@ -393,6 +434,10 @@ class SoundManager:
"""Play thread collapse sound"""
self.play_event("collapse")
def play_autocomplete(self):
"""Play autocomplete available sound"""
self.play_event("autocomplete")
def test_sound(self, event_type: str):
"""Test play a specific sound type"""
self.play_event(event_type)

View File

@ -16,9 +16,12 @@ class AutocompleteTextEdit(QTextEdit):
mention_requested = Signal(str) # Emitted when user types @ to request user list
emoji_requested = Signal(str) # Emitted when user types : to request emoji list
def __init__(self, parent=None):
def __init__(self, parent=None, sound_manager=None):
super().__init__(parent)
# Sound manager for audio feedback
self.sound_manager = sound_manager
# Lists for autocomplete
self.mention_list = [] # Will be populated from followers/following
self.emoji_list = [] # Will be populated from instance custom emojis
@ -199,8 +202,6 @@ class AutocompleteTextEdit(QTextEdit):
match_full = name_lower.startswith(prefix_lower)
if match_username or match_full:
matches.append(name)
else:
if matches:
self.show_completer(matches, prefix, start_pos, 'mention')
@ -256,6 +257,10 @@ class AutocompleteTextEdit(QTextEdit):
popup = self.completer.popup()
popup.setAccessibleName(f"{completion_type.title()} Autocomplete")
# Play autocomplete available sound
if self.sound_manager:
self.sound_manager.play_autocomplete()
def hide_completer(self):
"""Hide the completer"""
if self.completer:

View File

@ -74,7 +74,7 @@ class ComposeDialog(QDialog):
layout.addWidget(self.char_count_label)
# Main text area with autocomplete
self.text_edit = AutocompleteTextEdit()
self.text_edit = AutocompleteTextEdit(sound_manager=self.sound_manager)
self.text_edit.setAccessibleName("Post Content")
self.text_edit.setAccessibleDescription("Enter your post content here. Type @ for mentions, : for emojis. Press Tab to move to post options.")
self.text_edit.setPlaceholderText("What's on your mind? Type @ for mentions, : for emojis")