Hello, I've been trying to fix 2025 api changes but I have no success. Below I'm pasting llm description for what i have tried so far.
If you don't have time to put on this, please share any possibilities I can work on..
The addon successfully plays sounds for UI feedback when navigating with the Tab key or Shift+Tab. However, no sounds are played when using single-letter quick navigation keys in browse mode (e.g., h for heading, k for link, b for button, etc.), even though NVDA correctly speaks the new element.
The goal is to have Unspoken play the appropriate sound for the element that the browse mode caret lands on after a quick navigation key press.
Steps to Reproduce
- Install the Unspoken add-on.
- Ensure sounds are enabled in the Unspoken settings panel.
- Open a web page in a browser (e.g., Firefox, Chrome).
- Press Tab to move focus to an interactive element.
◦ Result: A sound plays correctly.
- Press k to navigate to the next link on the page.
◦ Expected Behavior: A sound should play for the link element.
◦ Actual Behavior: NVDA's speech synthesizer announces the link, but no sound is played by the add-on.
Technical Investigation and Findings
A significant amount of debugging was performed to isolate the cause of this issue. The investigation has concluded that standard NVDA event handlers and even script interception on a GlobalPlugin do not seem to capture this specific type of navigation.
Here is a summary of the methods attempted and the key findings:
Standard Event Handlers (GlobalPlugin):
◦ event_gainFocus & event_becomeNavigatorObject: These events fire correctly for focus changes (like Tab navigation) but are not triggered by single-letter quick navigation, as this action only moves the virtual caret within the browse mode buffer and does not change the actual system focus.
◦ event_caret: This event was also tested. It did not fire for single-letter quick navigation. It did, however, fire for every line change when using arrow keys in nvda log viewr, which was an undesirable side effect.
◦ event_navigatorObject_changed: This event also did not fire.
2.
Core Function Hooking (Monkey-Patching):
◦ We directly hooked NVDAObjects.api.setNavigatorObject to see if it was being called "under the hood."
◦ Critical Finding: This hook fired for Tab navigation but did not fire for single-letter quick navigation. This definitively proves that browse mode's internal caret movement uses a mechanism separate from the standard navigator object API.
3.
Script Interception (as per NVDA Developer Guide):
◦ Based on the developer documentation, the next logical step was to intercept the browse mode scripts themselves (e.g., script_nextHeading).
◦ Attempt A (Dynamic exec): An initial attempt was made to dynamically generate and attach all possible script_* methods to the GlobalPlugin class using an exec() loop. This had no effect.
◦ Attempt B (Robust getattr): A more robust and Pythonic approach using a getattr method on the GlobalPlugin was implemented. This acts as a "catch-all" for any script calls that aren't explicitly defined. This method is confirmed to work for intercepting other scripts, but it failed to intercept any of the browse mode navigation scripts.
Conclusion and Hypothesis
The core of the problem is that single-letter navigation in browse mode does not generate any of the standard events that a GlobalPlugin can easily listen for.
Furthermore, even the documented method of overriding scripts on a GlobalPlugin does not seem to work for this specific case. The most likely hypothesis is that the active BrowseMode object's own scripts (which are part of NVDA's core) have a higher priority in the dispatch chain. These scripts handle the navigation and speech output, but they do not appear to propagate the event or script call further in a way that allows a GlobalPlugin to easily hook in and perform a post-action task like playing a sound.
Any potential solution will likely require a deeper, more complex method of hooking directly into the BrowseMode class itself, rather than relying on the standard GlobalPlugin event and script system.
Hello, I've been trying to fix 2025 api changes but I have no success. Below I'm pasting llm description for what i have tried so far.
If you don't have time to put on this, please share any possibilities I can work on..
The addon successfully plays sounds for UI feedback when navigating with the Tab key or Shift+Tab. However, no sounds are played when using single-letter quick navigation keys in browse mode (e.g., h for heading, k for link, b for button, etc.), even though NVDA correctly speaks the new element.
The goal is to have Unspoken play the appropriate sound for the element that the browse mode caret lands on after a quick navigation key press.
Steps to Reproduce
◦ Result: A sound plays correctly.
◦ Expected Behavior: A sound should play for the link element.
◦ Actual Behavior: NVDA's speech synthesizer announces the link, but no sound is played by the add-on.
Technical Investigation and Findings
A significant amount of debugging was performed to isolate the cause of this issue. The investigation has concluded that standard NVDA event handlers and even script interception on a GlobalPlugin do not seem to capture this specific type of navigation.
Here is a summary of the methods attempted and the key findings:
Standard Event Handlers (GlobalPlugin):
◦ event_gainFocus & event_becomeNavigatorObject: These events fire correctly for focus changes (like Tab navigation) but are not triggered by single-letter quick navigation, as this action only moves the virtual caret within the browse mode buffer and does not change the actual system focus.
◦ event_caret: This event was also tested. It did not fire for single-letter quick navigation. It did, however, fire for every line change when using arrow keys in nvda log viewr, which was an undesirable side effect.
◦ event_navigatorObject_changed: This event also did not fire.
2.
Core Function Hooking (Monkey-Patching):
◦ We directly hooked NVDAObjects.api.setNavigatorObject to see if it was being called "under the hood."
◦ Critical Finding: This hook fired for Tab navigation but did not fire for single-letter quick navigation. This definitively proves that browse mode's internal caret movement uses a mechanism separate from the standard navigator object API.
3.
Script Interception (as per NVDA Developer Guide):
◦ Based on the developer documentation, the next logical step was to intercept the browse mode scripts themselves (e.g., script_nextHeading).
◦ Attempt A (Dynamic exec): An initial attempt was made to dynamically generate and attach all possible script_* methods to the GlobalPlugin class using an exec() loop. This had no effect.
◦ Attempt B (Robust getattr): A more robust and Pythonic approach using a getattr method on the GlobalPlugin was implemented. This acts as a "catch-all" for any script calls that aren't explicitly defined. This method is confirmed to work for intercepting other scripts, but it failed to intercept any of the browse mode navigation scripts.
Conclusion and Hypothesis
The core of the problem is that single-letter navigation in browse mode does not generate any of the standard events that a GlobalPlugin can easily listen for.
Furthermore, even the documented method of overriding scripts on a GlobalPlugin does not seem to work for this specific case. The most likely hypothesis is that the active BrowseMode object's own scripts (which are part of NVDA's core) have a higher priority in the dispatch chain. These scripts handle the navigation and speech output, but they do not appear to propagate the event or script call further in a way that allows a GlobalPlugin to easily hook in and perform a post-action task like playing a sound.
Any potential solution will likely require a deeper, more complex method of hooking directly into the BrowseMode class itself, rather than relying on the standard GlobalPlugin event and script system.