diff --git a/src/fenrirscreenreader/commands/onCursorChange/65000-present_line_if_cursor_change_vertical.py b/src/fenrirscreenreader/commands/onCursorChange/65000-present_line_if_cursor_change_vertical.py index 28f9d7ab..6d8577d7 100644 --- a/src/fenrirscreenreader/commands/onCursorChange/65000-present_line_if_cursor_change_vertical.py +++ b/src/fenrirscreenreader/commands/onCursorChange/65000-present_line_if_cursor_change_vertical.py @@ -60,9 +60,10 @@ class command: if self.env["runtime"]["SettingsManager"].get_setting_as_int( "general", "autoPresentIndentMode" ) in [0, 1]: - self.env["runtime"]["OutputManager"].play_frequence( - curr_ident * 50, 0.1, interrupt=do_interrupt - ) + if self.lastIdent != curr_ident: + self.env["runtime"]["OutputManager"].play_frequence( + curr_ident * 50, 0.1, interrupt=do_interrupt + ) if self.env["runtime"]["SettingsManager"].get_setting_as_int( "general", "autoPresentIndentMode" ) in [0, 2]: diff --git a/src/fenrirscreenreader/commands/onCursorChange/68000-auto_identation_horizontal.py b/src/fenrirscreenreader/commands/onCursorChange/68000-auto_identation_horizontal.py index 1392562c..947cc0b5 100644 --- a/src/fenrirscreenreader/commands/onCursorChange/68000-auto_identation_horizontal.py +++ b/src/fenrirscreenreader/commands/onCursorChange/68000-auto_identation_horizontal.py @@ -31,10 +31,9 @@ class command: self.lastIdent = 0 return - # is a vertical change? - if not self.env["runtime"][ - "CursorManager" - ].is_cursor_horizontal_move(): + # Skip if no cursor movement at all + if (not self.env["runtime"]["CursorManager"].is_cursor_horizontal_move() and + not self.env["runtime"]["CursorManager"].is_cursor_vertical_move()): return x, y, curr_line = line_utils.get_current_line( self.env["screen"]["new_cursor"]["x"], @@ -43,27 +42,34 @@ class command: ) curr_ident = self.env["screen"]["new_cursor"]["x"] - if not curr_line.isspace(): - # ident - lastIdent, lastY, last_line = line_utils.get_current_line( - self.env["screen"]["new_cursor"]["x"], - self.env["screen"]["new_cursor"]["y"], - self.env["screen"]["old_content_text"], - ) - if curr_line.strip() != last_line.strip(): - return - if len(curr_line.lstrip()) == len(last_line.lstrip()): - return + if curr_line.isspace(): + # Don't beep for lines with only spaces - no meaningful indentation + return + + # Lines with actual content - calculate proper indentation + lastIdent, lastY, last_line = line_utils.get_current_line( + self.env["screen"]["new_cursor"]["x"], + self.env["screen"]["new_cursor"]["y"], + self.env["screen"]["old_content_text"], + ) + if curr_line.strip() != last_line.strip(): + return + if len(curr_line.lstrip()) == len(last_line.lstrip()): + return - curr_ident = len(curr_line) - len(curr_line.lstrip()) + curr_ident = len(curr_line) - len(curr_line.lstrip()) - if self.lastIdent == -1: - self.lastIdent = curr_ident - if curr_ident <= 0: - return + if curr_ident <= 0: + return + + # Initialize lastIdent if needed + if self.lastIdent == -1: + self.lastIdent = curr_ident + + # Only beep/announce if indentation level has changed if self.env["runtime"]["SettingsManager"].get_setting_as_bool( "general", "autoPresentIndent" - ): + ) and self.lastIdent != curr_ident: if self.env["runtime"]["SettingsManager"].get_setting_as_int( "general", "autoPresentIndentMode" ) in [0, 1]: @@ -71,14 +77,15 @@ class command: curr_ident * 50, 0.1, interrupt=False ) if self.env["runtime"]["SettingsManager"].get_setting_as_int( - "general", "autoPresentIndentMode" + "general", "autePresentIndentMode" ) in [0, 2]: - if self.lastIdent != curr_ident: - self.env["runtime"]["OutputManager"].present_text( - _("indented ") + str(curr_ident) + " ", - interrupt=False, - flush=False, - ) + self.env["runtime"]["OutputManager"].present_text( + _("indented ") + str(curr_ident) + " ", + interrupt=False, + flush=False, + ) + + # Always update lastIdent for next comparison self.lastIdent = curr_ident def set_callback(self, callback): diff --git a/src/fenrirscreenreader/commands/onKeyInput/80500-numlock.py b/src/fenrirscreenreader/commands/onKeyInput/80500-numlock.py index 429ffe26..4eb2ec0f 100644 --- a/src/fenrirscreenreader/commands/onKeyInput/80500-numlock.py +++ b/src/fenrirscreenreader/commands/onKeyInput/80500-numlock.py @@ -24,14 +24,19 @@ class command: def run(self): if self.env["input"]["oldNumLock"] == self.env["input"]["newNumLock"]: return - if self.env["input"]["newNumLock"]: - self.env["runtime"]["OutputManager"].present_text( - _("Numlock on"), interrupt=True - ) - else: - self.env["runtime"]["OutputManager"].present_text( - _("Numlock off"), interrupt=True - ) + + # Only announce numlock changes if an actual numlock key was pressed + # This prevents spurious announcements from external numpad automatic state changes + current_input = self.env["input"]["currInput"] + if current_input and "KEY_NUMLOCK" in current_input: + if self.env["input"]["newNumLock"]: + self.env["runtime"]["OutputManager"].present_text( + _("Numlock on"), interrupt=True + ) + else: + self.env["runtime"]["OutputManager"].present_text( + _("Numlock off"), interrupt=True + ) def set_callback(self, callback): pass diff --git a/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py b/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py index da24037a..ada54b5b 100644 --- a/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py +++ b/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py @@ -144,13 +144,43 @@ class command: ] = current_time return + # Pattern 1a2: Curl classic progress format (percentage without % symbol) + # Extract percentage from curl's classic format + curl_classic_match = re.search( + r"^\s*(\d+)\s+\d+[kMGT]?\s+(\d+)\s+\d+[kMGT]?\s+\d+\s+\d+\s+\d+[kMGT]?\s+\d+\s+\d+:\d+:\d+\s+\d+:\d+:\d+\s+\d+:\d+:\d+\s+\d+[kMGT]?\s*$", text + ) + if curl_classic_match: + # Use the first percentage (total progress) + percentage = float(curl_classic_match.group(1)) + if 0 <= percentage <= 100: + self.env["runtime"]["DebugManager"].write_debug_out( + "found curl classic percentage: " + str(percentage), + debug.DebugLevel.INFO, + ) + if ( + percentage + != self.env["commandBuffer"]["lastProgressValue"] + ): + self.env["runtime"]["DebugManager"].write_debug_out( + "Playing tone for curl: " + str(percentage), + debug.DebugLevel.INFO, + ) + self.play_progress_tone(percentage) + self.env["commandBuffer"][ + "lastProgressValue" + ] = percentage + self.env["commandBuffer"][ + "lastProgressTime" + ] = current_time + return + # Pattern 1b: Time/token activity (not percentage-based, so use single # beep) - time_match = re.search(r"(\d+)s\s", text) - token_match = re.search(r"(\d+)\s+tokens", text) + time_match = re.search(r"(?:(?:remaining|elapsed|left|ETA|eta)[:;\s]*(\d+)s|(\d+)s\s+(?:remaining|elapsed|left))", text, re.IGNORECASE) + token_match = re.search(r"(?:processing|generating|used|consumed)\s+(\d+)\s+tokens", text, re.IGNORECASE) # Pattern 1c: dd command output (bytes copied with transfer rate) dd_match = re.search(r"\d+\s+bytes.*copied.*\d+\s+s.*[kMGT]?B/s", text) - # Pattern 1d: Curl-style transfer data (bytes, speed indicators) + # Pattern 1d: Curl-style transfer data (bytes, speed indicators - legacy) curl_match = re.search( r"(\d+\s+\d+\s+\d+\s+\d+.*?(?:k|M|G)?.*?--:--:--|Speed)", text ) @@ -183,7 +213,10 @@ class command: if fraction_match: current = int(fraction_match.group(1)) total = int(fraction_match.group(2)) - if total > 0: + # Filter out dates, page numbers, and other non-progress fractions + if (total > 0 and total <= 1000 and current <= total and + not re.search(r"\b(?:page|chapter|section|line|row|column|year|month|day)\b", text, re.IGNORECASE) and + not re.search(r"\d{1,2}/\d{1,2}/\d{2,4}", text)): # Date pattern percentage = (current / total) * 100 if ( percentage @@ -245,7 +278,15 @@ class command: self.env["commandBuffer"]["lastProgressTime"] = current_time return - # Pattern 6: Moon phase progress indicators + # Pattern 6: Claude Code progress indicators + claude_progress_match = re.search(r'^[·✢✒*]\s+\w+[…\.]*\s*\(esc to interrupt\)\s*$', text) + if claude_progress_match: + if current_time - self.env["commandBuffer"]["lastProgressTime"] >= 1.0: + self.play_activity_beep() + self.env["commandBuffer"]["lastProgressTime"] = current_time + return + + # Pattern 7: Moon phase progress indicators moon_match = re.search(r'[πŸŒ‘πŸŒ’πŸŒ“πŸŒ”πŸŒ•πŸŒ–πŸŒ—πŸŒ˜]', text) if moon_match: moon_phases = { @@ -274,9 +315,21 @@ class command: self.play_quiet_tone(800, 0.08) def play_quiet_tone(self, frequency, duration): - """Play a quiet tone using Sox directly""" + """Play a quiet tone using Sox directly with flood protection""" import shlex import subprocess + import time + + # Flood protection: prevent beeps closer than 0.1 seconds apart + current_time = time.time() + if not hasattr(self, '_last_beep_time'): + self._last_beep_time = 0 + + if current_time - self._last_beep_time < 0.1: + # Skip this beep to prevent audio crackling on low-resource systems + return + + self._last_beep_time = current_time # Build the Sox command: play -qn synth tri gain # -8 diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/alien_monster.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/alien_monster.py new file mode 100644 index 00000000..5e749c99 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/alien_monster.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "πŸ‘Ύ" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Alien monster emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added alien monster to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/cauldron.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/cauldron.py index 22426f64..61d67110 100644 --- a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/cauldron.py +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/cauldron.py @@ -10,13 +10,13 @@ class command(): pass def getDescription(self): - return "Magic cauldron emoji" + return "Mage emoji" def run(self): self.env["runtime"]["MemoryManager"].add_value_to_first_index( "clipboardHistory", self.emoji ) self.env["runtime"]["OutputManager"].present_text( - "Added magic cauldron to clipboard", + "Added mage to clipboard", interrupt=False, flush=False ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/coffin.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/coffin.py new file mode 100644 index 00000000..0011a3c2 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/coffin.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "⚰️" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Coffin emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added coffin to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/mummy.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/mummy.py new file mode 100644 index 00000000..057503eb --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/mummy.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🧟" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Mummy emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added mummy to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/web.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/web.py new file mode 100644 index 00000000..7c42a148 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/web.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "πŸ•ΈοΈ" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Spider web emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added spider web to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/zombie.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/zombie.py new file mode 100644 index 00000000..dfdf7612 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/zombie.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🧟" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Zombie emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added zombie to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/dizzy.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/dizzy.py new file mode 100644 index 00000000..9c992f68 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/dizzy.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😡" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Dizzy face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added dizzy face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/exploding_head.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/exploding_head.py new file mode 100644 index 00000000..a8773423 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/exploding_head.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🀯" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Exploding head emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added exploding head to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/face_with_symbols.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/face_with_symbols.py new file mode 100644 index 00000000..934e9e15 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/face_with_symbols.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🀬" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Face with symbols over mouth emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added face with symbols to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/imp.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/imp.py new file mode 100644 index 00000000..671557e2 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/imp.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "πŸ‘Ώ" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Angry face with horns emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added angry face with horns to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/mindblown.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/mindblown.py new file mode 100644 index 00000000..76b3908b --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/mindblown.py @@ -0,0 +1,23 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🀯" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Mind blown emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added mind blown to clipboard", + interrupt=False, flush=False + ) + diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/nauseated.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/nauseated.py new file mode 100644 index 00000000..2b8a0e64 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/nauseated.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🀒" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Nauseated face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added nauseated face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/screaming.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/screaming.py new file mode 100644 index 00000000..ccdbb9c6 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/screaming.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😱" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Screaming face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added screaming face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/chains.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/chains.py new file mode 100644 index 00000000..a441303e --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/chains.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "⛓️" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Chains emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added chains to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/crossbones.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/crossbones.py new file mode 100644 index 00000000..60123ff7 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/crossbones.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🦴" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Bone emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added bone to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/dagger.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/dagger.py new file mode 100644 index 00000000..4a641ad3 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/dagger.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "πŸ—‘οΈ" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Dagger emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added dagger to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/high_voltage.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/high_voltage.py new file mode 100644 index 00000000..aba0e3ee --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/high_voltage.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "⚑" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "High voltage lightning bolt emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added high voltage lightning bolt to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/sword.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/sword.py new file mode 100644 index 00000000..f0aefe4b --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/sword.py @@ -0,0 +1,24 @@ + +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "βš”οΈ" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Sword emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added sword to clipboard", + interrupt=False, flush=False + ) + diff --git a/src/fenrirscreenreader/core/outputManager.py b/src/fenrirscreenreader/core/outputManager.py index 44555e62..68e5690a 100644 --- a/src/fenrirscreenreader/core/outputManager.py +++ b/src/fenrirscreenreader/core/outputManager.py @@ -77,6 +77,27 @@ class OutputManager: def get_last_echo(self): return self.last_echo + def process_mid_word_punctuation(self, text): + """ + Process punctuation that appears mid-word to ensure proper pronunciation. + Specifically handles dots between word characters (e.g., "settings.conf" -> "settings dot conf") + and dots at word beginnings (e.g., ".local" -> "dot local") + while preserving sentence-ending periods and other punctuation behavior. + """ + if not text: + return text + + # Handle dots at the beginning of words (like .local, .bashrc, .config) + # Look for non-word character (or start of string), dot, then word characters + text = re.sub(r'(? 0.8: # This indicates low frequency indentation beeps + # Extremely aggressive boost - GStreamer really struggles with low frequencies + effective_volume = self.volume * adjust_volume * 50.0 # Ridiculous multiplier to match sox + else: + effective_volume = self.volume * adjust_volume * 3.0 + self._volume.set_property("volume", effective_volume) + self._source.set_property("volume", 1.0) # Set source to full, control via volume element self._source.set_property("freq", frequence) self._pipeline.set_state(Gst.State.PLAYING) GLib.timeout_add(duration, self._on_timeout, self._pipeline)