From 26c6e32c5972e9a679708552d2108e2df10ad74b Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 7 Jun 2025 01:26:21 -0400 Subject: [PATCH] Add the ability to add custom prompts to the settings file, generic prompts are covered with the existing detection code. --- config/settings/settings.conf | 16 +++++ .../commands/commands/silence_until_prompt.py | 58 ++++++++++++---- .../onScreenUpdate/66000-prompt_detector.py | 67 ++++++++++++++----- 3 files changed, 112 insertions(+), 29 deletions(-) diff --git a/config/settings/settings.conf b/config/settings/settings.conf index 81439351..0c00682c 100644 --- a/config/settings/settings.conf +++ b/config/settings/settings.conf @@ -214,6 +214,22 @@ list= vmenuPath= quickMenu=speech#rate;speech#pitch;speech#volume +[prompt] +# Custom prompt patterns for silence until prompt feature +# You can add your own shell prompt patterns as regular expressions +# Each pattern should be on a separate line, format: customPatterns=pattern1,pattern2,pattern3 +# Examples: +# For PS1='[\u@\h \W] \$ ' use: \[.*@.*\s.*\]\s*[$#>]\s* +# For "[storm@fenrir ~] $" use: \[.*@.*\s.*\]\s*[$#>]\s* +# For custom prompts ending with specific strings, use patterns like: .*your_prompt_ending$ +customPatterns= + +# Specific prompt strings to match exactly (useful for very specific custom prompts) +# Format: exactMatches=prompt1,prompt2,prompt3 +# Examples: +# exactMatches=[storm@fenrir ~] $,[root@fenrir ~] # +exactMatches= + [time] # automatic time anouncement enabled=False diff --git a/src/fenrirscreenreader/commands/commands/silence_until_prompt.py b/src/fenrirscreenreader/commands/commands/silence_until_prompt.py index ebcf6d56..4c9008cf 100644 --- a/src/fenrirscreenreader/commands/commands/silence_until_prompt.py +++ b/src/fenrirscreenreader/commands/commands/silence_until_prompt.py @@ -46,22 +46,52 @@ class command(): if not self.env['commandBuffer']['silenceUntilPrompt']: return False - # Look for common shell prompt patterns - # $ prompt (user) - # # prompt (root) - # > prompt (some shells) - # Also check for common prompt prefixes like user@host:path$ - promptPatterns = [ - r'[^$]*\$$', # Ends with $ (user prompt) - r'[^#]*#$', # Ends with # (root prompt) - r'[^>]*>$', # Ends with > (some shells) - r'.*[\w@]+:.*[$#>]\s*$', # user@host:path$ style - ] + # First check for exact matches from settings (with backward compatibility) + try: + exactMatches = self.env['runtime']['settingsManager'].getSetting('prompt', 'exactMatches') + if exactMatches: + exactList = [match.strip() for match in exactMatches.split(',') if match.strip()] + for exactMatch in exactList: + if text.strip() == exactMatch: + self.env['runtime']['debug'].writeDebugOut("Found exact prompt match: " + exactMatch, debug.debugLevel.INFO) + self.disableSilence() + return True + except: + # Prompt section doesn't exist in settings, skip custom exact matches + pass + + # Get custom patterns from settings (with backward compatibility) + promptPatterns = [] + try: + customPatterns = self.env['runtime']['settingsManager'].getSetting('prompt', 'customPatterns') + # Add custom patterns from settings if they exist + if customPatterns: + customList = [pattern.strip() for pattern in customPatterns.split(',') if pattern.strip()] + promptPatterns.extend(customList) + except: + # Prompt section doesn't exist in settings, skip custom patterns + pass + + # Add default shell prompt patterns + promptPatterns.extend([ + r'^\s*\\\$\s*$', # Just $ (with whitespace) + r'^\s*#\s*$', # Just # (with whitespace) + r'^\s*>\s*$', # Just > (with whitespace) + r'.*@.*[\\\$#>]\s*$', # Contains @ and ends with prompt char (user@host style) + r'^\[.*\]\s*[\\\$#>]\s*$', # [anything]$ style prompts + r'^[a-zA-Z0-9._-]+[\\\$#>]\s*$', # Simple shell names like bash-5.1$ + ]) for pattern in promptPatterns: - if re.search(pattern, text.strip()): - self.disableSilence() - return True + try: + if re.search(pattern, text.strip()): + self.env['runtime']['debug'].writeDebugOut("Found prompt pattern: " + pattern, debug.debugLevel.INFO) + self.disableSilence() + return True + except re.error as e: + # Invalid regex pattern, skip it and log the error + self.env['runtime']['debug'].writeDebugOut("Invalid prompt pattern: " + pattern + " Error: " + str(e), debug.debugLevel.ERROR) + continue return False diff --git a/src/fenrirscreenreader/commands/onScreenUpdate/66000-prompt_detector.py b/src/fenrirscreenreader/commands/onScreenUpdate/66000-prompt_detector.py index 5ccc083a..d172ba8a 100644 --- a/src/fenrirscreenreader/commands/onScreenUpdate/66000-prompt_detector.py +++ b/src/fenrirscreenreader/commands/onScreenUpdate/66000-prompt_detector.py @@ -40,25 +40,62 @@ class command(): # Debug: Print what we're checking self.env['runtime']['debug'].writeDebugOut("Prompt detector checking: '" + text + "'", debug.debugLevel.INFO) - # Look for common shell prompt patterns - promptPatterns = [ - r'[^$]*\$$', # Ends with $ (user prompt) - r'[^#]*#$', # Ends with # (root prompt) - r'[^>]*>$', # Ends with > (some shells) - r'.*[\w@]+:.*[$#>]\s*$', # user@host:path$ style - ] + # First check for exact matches from settings (with backward compatibility) + try: + exactMatches = self.env['runtime']['settingsManager'].getSetting('prompt', 'exactMatches') + if exactMatches: + exactList = [match.strip() for match in exactMatches.split(',') if match.strip()] + for exactMatch in exactList: + if text.strip() == exactMatch: + self.env['runtime']['debug'].writeDebugOut("Found exact prompt match: " + exactMatch, debug.debugLevel.INFO) + self._restoreSpeech() + return True + except: + # Prompt section doesn't exist in settings, skip custom exact matches + pass + + # Get custom patterns from settings (with backward compatibility) + promptPatterns = [] + try: + customPatterns = self.env['runtime']['settingsManager'].getSetting('prompt', 'customPatterns') + # Add custom patterns from settings if they exist + if customPatterns: + customList = [pattern.strip() for pattern in customPatterns.split(',') if pattern.strip()] + promptPatterns.extend(customList) + except: + # Prompt section doesn't exist in settings, skip custom patterns + pass + + # Add default shell prompt patterns + promptPatterns.extend([ + r'^\s*\\\$\s*$', # Just $ (with whitespace) + r'^\s*#\s*$', # Just # (with whitespace) + r'^\s*>\s*$', # Just > (with whitespace) + r'.*@.*[\\\$#>]\s*$', # Contains @ and ends with prompt char (user@host style) + r'^\[.*\]\s*[\\\$#>]\s*$', # [anything]$ style prompts + r'^[a-zA-Z0-9._-]+[\\\$#>]\s*$', # Simple shell names like bash-5.1$ + ]) for pattern in promptPatterns: - if re.search(pattern, text.strip()): - self.env['runtime']['debug'].writeDebugOut("Found prompt pattern: " + pattern, debug.debugLevel.INFO) - # Disable silence mode - self.env['commandBuffer']['silenceUntilPrompt'] = False - # Re-enable speech - self.env['runtime']['settingsManager'].setSetting('speech', 'enabled', 'True') - self.env['runtime']['outputManager'].presentText(_("Speech restored"), soundIcon='SpeechOn', interrupt=True) - return True + try: + if re.search(pattern, text.strip()): + self.env['runtime']['debug'].writeDebugOut("Found prompt pattern: " + pattern, debug.debugLevel.INFO) + self._restoreSpeech() + return True + except re.error as e: + # Invalid regex pattern, skip it and log the error + self.env['runtime']['debug'].writeDebugOut("Invalid prompt pattern: " + pattern + " Error: " + str(e), debug.debugLevel.ERROR) + continue return False + + def _restoreSpeech(self): + """Helper method to restore speech when prompt is detected""" + # Disable silence mode + self.env['commandBuffer']['silenceUntilPrompt'] = False + # Re-enable speech + self.env['runtime']['settingsManager'].setSetting('speech', 'enabled', 'True') + self.env['runtime']['outputManager'].presentText(_("Speech restored"), soundIcon='SpeechOn', interrupt=True) def setCallback(self, callback): pass \ No newline at end of file