Files
voculinux/src/vocalinux/ui/action_handler.py
T
soubarnak ea7be5f111 feat(identity): rename user-visible identity to voculinux with one-time legacy import
The fork takes its own name everywhere a user can see it: binaries
(voculinux, voculinux-gui), desktop entry, autostart, tray, D-Bus
namespace (com.voculinux.Voculinux, no alias for the old name),
config/data/cache dirs, single-instance lock, IBus engine identity,
tray icons, resource prefixes, VOCULINUX_* environment names, and the
update-checker user agent. The internal Python module stays vocalinux
and the distribution metadata keeps the upstream URLs as provenance.

On first run a previous Vocalinux install's config.json, custom
dictionary, and portal restore token are copied once into the new
locations (new config always wins, no merge, no write-back); models
re-resolve through the pinned-checksum download path. An offline
naming-sweep test proves no old-name strings remain on user-visible
surfaces outside the allowlist.

Refs #18
2026-11-06 01:29:39 +05:30

111 lines
3.5 KiB
Python

"""
Action handler for Voculinux voice commands.
This module handles special voice commands like "delete that", "undo", etc.
"""
import logging
from typing import TYPE_CHECKING, Callable
if TYPE_CHECKING:
from ..text_injection.text_injector import TextInjector
logger = logging.getLogger(__name__)
class ActionHandler:
"""
Handles special voice command actions.
This class processes action commands from the speech recognition system
and performs the appropriate actions like deleting text, undoing, etc.
"""
# Keyboard shortcut mappings for simple forwarding actions.
# Actions not listed here have custom handlers defined as methods.
_SHORTCUT_ACTIONS: dict[str, str] = {
"undo": "ctrl+z",
"redo": "ctrl+y",
"select_all": "ctrl+a",
"select_line": "Home+shift+End",
"select_word": "ctrl+shift+Right",
"select_paragraph": "ctrl+shift+Down",
"cut": "ctrl+x",
"copy": "ctrl+c",
"paste": "ctrl+v",
}
def __init__(self, text_injector: "TextInjector"):
"""
Initialize the action handler.
Args:
text_injector: The text injector instance for performing actions
"""
self.text_injector = text_injector
self.last_injected_text = ""
# Build action dispatch table: custom handlers + shortcut-based actions
self.action_handlers: dict[str, Callable[[], bool]] = {
"delete_last": self._handle_delete_last,
}
for action, shortcut in self._SHORTCUT_ACTIONS.items():
self.action_handlers[action] = self._make_shortcut_handler(shortcut)
def handle_action(self, action: str) -> bool:
"""
Handle a voice command action.
Args:
action: The action to perform
Returns:
True if the action was handled successfully, False otherwise
"""
logger.debug(f"Handling action: {action}")
handler = self.action_handlers.get(action)
if handler:
try:
return handler()
except Exception as e:
logger.error(f"Error handling action '{action}': {e}")
return False
else:
logger.warning(f"Unknown action: {action}")
return False
def set_last_injected_text(self, text: str):
"""
Set the last injected text for undo/delete operations.
Args:
text: The last text that was injected
"""
self.last_injected_text = text
def _make_shortcut_handler(self, shortcut: str) -> Callable[[], bool]:
"""Create a handler that sends a keyboard shortcut via the text injector."""
def handler() -> bool:
return self.text_injector._inject_keyboard_shortcut(shortcut)
return handler
def _handle_delete_last(self) -> bool:
"""Handle 'delete that' command by sending backspace keys."""
if not self.last_injected_text:
logger.debug("No text to delete")
return True
# Send real BackSpace key events, one per character. Injecting U+0008 as
# text does nothing: ydotool has no keymap entry for it and IBus commits
# it as a literal control character.
success = self.text_injector.press_backspace(len(self.last_injected_text))
if success:
logger.debug(f"Deleted {len(self.last_injected_text)} characters")
self.last_injected_text = ""
return success