diff --git a/distro-packages/Arch-Linux/cthulhu-git/PKGBUILD b/distro-packages/Arch-Linux/cthulhu-git/PKGBUILD index 3eade34..c3c0251 100644 --- a/distro-packages/Arch-Linux/cthulhu-git/PKGBUILD +++ b/distro-packages/Arch-Linux/cthulhu-git/PKGBUILD @@ -1,24 +1,25 @@ # Maintainer: Storm Dragon +# shellcheck shell=bash disable=SC2034,SC2154,SC2164 pkgbase=cthulhu-git pkgname=(cthulhu-git cthulhu-wine-access-git) _pkgname=cthulhu pkgver=2026.05.25.r423.g0576b6f -pkgrel=1 +pkgrel=2 pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca" url="https://git.stormux.org/storm/cthulhu" arch=(x86_64 aarch64 armv7h) -license=(LGPL) -provides=("${_pkgname}") +license=(LGPL-2.1-or-later) +provides=("${_pkgname}=${pkgver}") conflicts=("${_pkgname}") depends=( # Core AT-SPI accessibility at-spi2-core + glib2 gobject-introspection-runtime python-gobject python-cairo gtk3 - python-pywayland # Audio and speech speech-dispatcher gstreamer @@ -26,57 +27,44 @@ depends=( gst-plugins-good # pulsesink, more decoders # Braille support - brltty + python-brltty liblouis # Plugin system and D-Bus remote control python-pluggy python-tomlkit python-dasbus - - # AI Assistant dependencies (for screenshots, HTTP requests, and actions) - python-requests - python-pyautogui - + python-msgpack + python-tornado + python-xlib + # Desktop integration hicolor-icon-theme libwnck3 pango - + # System utilities - python python-setproctitle - socat # for self-voicing feature xorg-xkbcomp - xorg-xmodmap ) optdepends=( - 'espeak-ng: Alternative TTS engine' - 'festival: Alternative TTS engine' - 'flite: Lightweight TTS engine' - 'espeak: Legacy TTS engine' + 'git: Install and update third-party plugin sources' + 'python-pywayland: Wayland shared workspace backend' + 'piper-tts: Piper speech backend' + 'socat: Send text to the Self Voice socket from the command line' # AI Assistant providers (optional) + 'python-requests: AI Assistant plugin and HTTP provider support' + 'python-pyautogui: AI Assistant mouse and keyboard actions' 'claude-code: Claude AI provider support' - 'openai-codex: ChatGPT AI provider support' + 'openai-codex: OpenAI Codex provider support' 'gemini-cli: Gemini AI provider support' 'ollama: Local AI model support' # OCR plugin dependencies (optional) - 'python-pillow: Image processing for OCR and AI Assistant' + 'python-pillow: Image processing for OCR' 'python-pytesseract: Python wrapper for Tesseract OCR engine' - 'python-pdf2image: PDF to image conversion for OCR' - 'python-scipy: Scientific computing for OCR color analysis' - 'python-webcolors: Color name lookup for OCR text decoration' 'tesseract: OCR engine for text recognition' - 'tesseract-data-eng: English language data for Tesseract' - - # nvda2cthulhu plugin (optional) - 'python-msgpack: Msgpack decoding for nvda2cthulhu' - 'python-tornado: WebSocket server for nvda2cthulhu' - - # Window Title Reader plugin (optional) - 'python-xlib: X11 access for Wine window title plugin' ) optdepends_x86_64=( 'cthulhu-wine-access-git: Wine, Proton, NVDA Controller, and Tolk integration' @@ -84,24 +72,13 @@ optdepends_x86_64=( makedepends=( git meson - ninja - python-build - python-installer - python-wheel ) makedepends_x86_64=( cmake wine ) -install=cthulhu.install -source=( - "${_pkgname}::git+https://git.stormux.org/storm/${_pkgname}.git#branch=master" - "cthulhu.install" -) -b2sums=( - 'SKIP' - 'SKIP' -) +source=("${_pkgname}::git+https://git.stormux.org/storm/${_pkgname}.git#branch=master") +sha512sums=('SKIP') pkgver() { cd "${_pkgname}" @@ -125,7 +102,7 @@ build() { package_cthulhu-git() { arch=(any) - provides=(cthulhu) + provides=("cthulhu=${pkgver}") conflicts=(cthulhu) cd "${_pkgname}" meson install -C _build --destdir "$pkgdir" diff --git a/distro-packages/Arch-Linux/cthulhu-git/cthulhu.install b/distro-packages/Arch-Linux/cthulhu-git/cthulhu.install deleted file mode 100644 index 0fbb304..0000000 --- a/distro-packages/Arch-Linux/cthulhu-git/cthulhu.install +++ /dev/null @@ -1,11 +0,0 @@ -post_install() { - gtk-update-icon-cache -q -t -f usr/share/icons/hicolor -} - -post_upgrade() { - post_install -} - -post_remove() { - gtk-update-icon-cache -q -t -f usr/share/icons/hicolor -} diff --git a/distro-packages/Arch-Linux/cthulhu/PKGBUILD b/distro-packages/Arch-Linux/cthulhu/PKGBUILD index f61ae3a..50d1c34 100644 --- a/distro-packages/Arch-Linux/cthulhu/PKGBUILD +++ b/distro-packages/Arch-Linux/cthulhu/PKGBUILD @@ -1,79 +1,67 @@ # Maintainer: Storm Dragon +# shellcheck shell=bash disable=SC2034,SC2154,SC2164 pkgbase=cthulhu pkgname=(cthulhu cthulhu-wine-access) -pkgver=2026.05.25 +pkgver=2026.07.18 pkgrel=1 pkgdesc="Desktop-agnostic screen reader with plugin system, forked from Orca" url="https://git.stormux.org/storm/cthulhu" arch=(x86_64 aarch64 armv7h) -license=(LGPL) +license=(LGPL-2.1-or-later) depends=( # Core AT-SPI accessibility at-spi2-core + glib2 gobject-introspection-runtime python-gobject python-cairo gtk3 - python-pywayland # Audio and speech speech-dispatcher gstreamer gst-plugins-base # playbin, audiotestsrc, basic decoders gst-plugins-good # pulsesink, more decoders - + # Braille support - brltty + python-brltty liblouis - + # Plugin system and D-Bus remote control python-pluggy python-tomlkit python-dasbus - - # AI Assistant dependencies (for screenshots, HTTP requests, and actions) - python-requests - python-pyautogui - + python-msgpack + python-tornado + python-xlib + # Desktop integration hicolor-icon-theme libwnck3 pango - + # System utilities - python python-setproctitle - socat # for self-voicing feature xorg-xkbcomp - xorg-xmodmap ) optdepends=( - 'espeak-ng: Alternative TTS engine' - 'festival: Alternative TTS engine' - 'flite: Lightweight TTS engine' - 'espeak: Legacy TTS engine' - + 'git: Install and update third-party plugin sources' + 'python-pywayland: Wayland shared workspace backend' + 'piper-tts: Piper speech backend' + 'socat: Send text to the Self Voice socket from the command line' + # AI Assistant providers (optional) + 'python-requests: AI Assistant plugin and HTTP provider support' + 'python-pyautogui: AI Assistant mouse and keyboard actions' 'claude-code: Claude AI provider support' - 'openai-codex: ChatGPT AI provider support' + 'openai-codex: OpenAI Codex provider support' 'gemini-cli: Gemini AI provider support' 'ollama: Local AI model support' - - # OCR plugin dependencies (optional) - 'python-pillow: Image processing for OCR and AI Assistant' - 'python-pytesseract: Python wrapper for Tesseract OCR engine' - 'python-pdf2image: PDF to image conversion for OCR' - 'python-scipy: Scientific computing for OCR color analysis' - 'python-webcolors: Color name lookup for OCR text decoration' - 'tesseract: OCR engine for text recognition' - 'tesseract-data-eng: English language data for Tesseract' - - # nvda2cthulhu plugin (optional) - 'python-msgpack: Msgpack decoding for nvda2cthulhu' - 'python-tornado: WebSocket server for nvda2cthulhu' - # Window Title Reader plugin (optional) - 'python-xlib: X11 access for Wine window title plugin' + # OCR plugin dependencies (optional) + 'python-pillow: Image processing for OCR' + 'python-pytesseract: Python wrapper for Tesseract OCR engine' + 'tesseract: OCR engine for text recognition' ) optdepends_x86_64=( 'cthulhu-wine-access: Wine, Proton, NVDA Controller, and Tolk integration' @@ -81,24 +69,13 @@ optdepends_x86_64=( makedepends=( git meson - ninja - python-build - python-installer - python-wheel ) makedepends_x86_64=( cmake wine ) -install=cthulhu.install -source=( - "git+https://git.stormux.org/storm/cthulhu.git#tag=${pkgver}" - "cthulhu.install" -) -b2sums=( - 'SKIP' - 'SKIP' -) +source=("git+https://git.stormux.org/storm/cthulhu.git#tag=${pkgver}") +sha512sums=('SKIP') build() { cd cthulhu @@ -108,7 +85,7 @@ build() { package_cthulhu() { arch=(any) - provides=(cthulhu) + provides=("cthulhu=${pkgver}") conflicts=(cthulhu) cd cthulhu meson install -C _build --destdir "$pkgdir" diff --git a/distro-packages/Arch-Linux/cthulhu/cthulhu.install b/distro-packages/Arch-Linux/cthulhu/cthulhu.install deleted file mode 100644 index 0fbb304..0000000 --- a/distro-packages/Arch-Linux/cthulhu/cthulhu.install +++ /dev/null @@ -1,11 +0,0 @@ -post_install() { - gtk-update-icon-cache -q -t -f usr/share/icons/hicolor -} - -post_upgrade() { - post_install -} - -post_remove() { - gtk-update-icon-cache -q -t -f usr/share/icons/hicolor -} diff --git a/meson.build b/meson.build index 36c37a2..f66f40e 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('cthulhu', - version: '2026.05.25', + version: '2026.07.18', meson_version: '>= 1.0.0', ) @@ -56,12 +56,8 @@ optional_modules = { 'louis': 'contracted braille', 'speechd': 'speech output', 'dasbus': 'D-Bus remote controller', - 'psutil': 'system information commands', 'pywayland': 'Wayland shared workspace backend', 'gi.repository.Wnck': 'mouse review', - 'pdf2image': 'PDF processing for OCR', - 'scipy': 'Scientific computing for OCR analysis', - 'webcolors': 'Color name resolution for OCR', } summary = {} diff --git a/src/cthulhu/cthulhuVersion.py b/src/cthulhu/cthulhuVersion.py index 3c66bbe..b927b5a 100644 --- a/src/cthulhu/cthulhuVersion.py +++ b/src/cthulhu/cthulhuVersion.py @@ -23,5 +23,5 @@ # Forked from Orca screen reader. # Cthulhu project: https://git.stormux.org/storm/cthulhu -version = "2026.07.17" +version = "2026.07.18" codeName = "wine-access" diff --git a/src/cthulhu/plugins/OCR/plugin.py b/src/cthulhu/plugins/OCR/plugin.py index 996e6dd..ef0c631 100644 --- a/src/cthulhu/plugins/OCR/plugin.py +++ b/src/cthulhu/plugins/OCR/plugin.py @@ -47,28 +47,6 @@ try: except ImportError: PYTESSERACT_AVAILABLE = False -# pdf2image -try: - from pdf2image import convert_from_path - PDF2IMAGE_AVAILABLE = True -except ImportError: - PDF2IMAGE_AVAILABLE = False - -# scipy -try: - from scipy.spatial import KDTree - SCIPY_AVAILABLE = True -except ImportError: - SCIPY_AVAILABLE = False - -# webcolors -try: - from webcolors import CSS3_HEX_TO_NAMES - from webcolors import hex_to_rgb - WEBCOLORS_AVAILABLE = True -except ImportError: - WEBCOLORS_AVAILABLE = False - # GTK/GDK try: gi.require_version("Gtk", "3.0") @@ -594,9 +572,7 @@ class OCRDesktop(Plugin): """Get color information for OCR text (simplified version).""" if not self._colorCalculation: return 'unknown' - if not SCIPY_AVAILABLE or not WEBCOLORS_AVAILABLE: - return 'unknown' - + # Simplified color calculation - just return "unknown" for now # Full implementation would require the color analysis from ocrdesktop return 'unknown' diff --git a/tests/test_plugin_preferences_regressions.py b/tests/test_plugin_preferences_regressions.py new file mode 100644 index 0000000..3b67415 --- /dev/null +++ b/tests/test_plugin_preferences_regressions.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +"""Regression tests for the plugin controls in Preferences.""" + +import unittest +from unittest import mock + +from cthulhu import cthulhu # Import first to satisfy the application's initialization order. +from cthulhu import cthulhu_gui_prefs + + +class PluginPreferencesRegressionTests(unittest.TestCase): + """Keep plugin state changes independent from their presentation widgets.""" + + @staticmethod + def _create_gui(): + gui = cthulhu_gui_prefs.CthulhuSetupGUI.__new__( + cthulhu_gui_prefs.CthulhuSetupGUI + ) + gui.prefsDict = { + "activePlugins": ["HiddenPlugin", "SourceOne"], + "pluginSources": ["https://example.com/plugins.git"], + } + gui._available_plugins = {"HiddenPlugin", "SourceOne", "SourceTwo", "Required"} + gui._plugin_entries = { + "SourceOne": {"active": True, "can_toggle": True}, + "SourceTwo": {"active": False, "can_toggle": True}, + "Required": {"active": True, "can_toggle": False}, + } + gui._plugin_canonical_map = { + "SourceOne": "SharedPlugin", + "SourceTwo": "SharedPlugin", + "Required": "Required", + } + gui._plugin_group_map = { + "SharedPlugin": ["SourceOne", "SourceTwo"], + "Required": ["Required"], + } + gui._update_selected_plugin_controls = mock.Mock() + gui._update_plugin_tabs = mock.Mock() + return gui + + def test_active_plugins_include_hidden_and_selected_plugins(self): + gui = self._create_gui() + + self.assertEqual( + gui._get_active_plugins_from_ui(), + ["HiddenPlugin", "SourceOne", "Required"], + ) + + def test_enabling_plugin_disables_other_copy_with_same_name(self): + gui = self._create_gui() + + gui._set_plugin_active("SourceTwo", True) + + self.assertFalse(gui._plugin_entries["SourceOne"]["active"]) + self.assertTrue(gui._plugin_entries["SourceTwo"]["active"]) + gui._update_selected_plugin_controls.assert_called_once_with() + gui._update_plugin_tabs.assert_called_once_with() + + def test_required_plugin_cannot_be_disabled(self): + gui = self._create_gui() + + gui._set_plugin_active("Required", False) + + self.assertTrue(gui._plugin_entries["Required"]["active"]) + gui._update_plugin_tabs.assert_not_called() + + def test_apply_does_not_modify_plugin_sources(self): + gui = self._create_gui() + gui._populate_plugin_list = mock.Mock() + + gui._apply_plugin_changes() + + self.assertEqual( + gui.prefsDict["pluginSources"], + ["https://example.com/plugins.git"], + ) + + +if __name__ == "__main__": + unittest.main()