[
  {
    "path": ".flake8",
    "content": "[flake8]\nmax-line-length = 120\nexclude =\n    .git,\n    __pycache__,\n    .venv,\n    build,\n    dist,\n\n# ANN101: Missing type annotation for self in method\n# ANN102: Missing type annotation for cls in method\n# D400: First line should end with a period for a docstring\n# D401: First line should be in imperative mood for a docstring\n# ANN003: Missing type annotation for a kwargs argument\n\nextend-ignore=ANN101,ANN102,D400,D401,ANN003,N802\nsuppress-none-returning=true"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# UV\n#   Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#uv.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# pdm\n#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.\n#pdm.lock\n#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it\n#   in version control.\n#   https://pdm.fming.dev/latest/usage/project/#working-with-version-control\n.pdm.toml\n.pdm-python\n.pdm-build/\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n.idea/\n\n# Ruff stuff:\n.ruff_cache/\n\n# PyPI configuration file\n.pypirc\n\n.vscode/\n\n.python-version"
  },
  {
    "path": "Contributing.md",
    "content": "# Contributing to Orange Intelligence  \n\nWe’re thrilled that you want to contribute to Orange Intelligence! While we don’t have a fully established contribution process at the moment, here’s what it should look like:  \n\n---\n\n## 🛠 Contribution Workflow  \n\n1. **Open an Issue**  \n   - Start by opening an issue in this repository.  \n   - Use a proper tag to classify your issue:  \n     - `ux`: For UI/UX improvements.  \n     - `bug`: For bugs or unexpected behavior.  \n     - `feature-request`: For new features you'd like to see.  \n     - `extension-function`: For proposing new functions/extensions to be added to the floating window.  \n   - Wait for feedback before diving into implementation. We don’t want you to waste valuable time on something that might not align with the project’s goals.  \n\n2. **Create a Pull Request (PR)**  \n   - Once your issue is approved, fork the repository and start working on your changes.  \n   - When ready, submit a PR linked to your issue.  \n\n3. **Make the CI Happy**  \n   - Ensure your code passes all linting and tests. Run these locally before pushing your changes.  \n\n4. **Celebrate Your Contribution!**  \n   - Once your PR is merged, enjoy the satisfaction of contributing to an open-source project. 🎉  \n\n---\n\n## 🧹 Code Style  \n\nWe strive to maintain clean, consistent, and readable code. Please follow these practices:  \n\n- Run linters (`black`, `flake8`) before submitting your PR.  \n- Include unit tests for new features or fixes where applicable.  \n\n---\n\n## 📝 Notes  \n\n- If you have ideas but aren’t sure how to implement them, feel free to open an issue just to discuss!  \n- Don’t hesitate to ask questions if you’re stuck—collaboration is key.  \n\n---\n\nThank you for helping make Orange Intelligence better! 🧡  \n"
  },
  {
    "path": "README.md",
    "content": "# 🍊 Orange Intelligence  \n\n**A Better Open Source Version of Apple Intelligence for macOS**  \n\nOrange Intelligence is a powerful, fully customizable productivity tool for macOS. With its elegant floating window interface, you can capture, process, and replace text seamlessly across any application. Whether you're running basic text processing, leveraging the power of large language models (LLMs) like OpenAI or local LLaMA, or creating complex agent systems, Orange Intelligence empowers you to work smarter, faster, and better.  \n\n---\n\n## 🌟 Why Orange Intelligence?  \n\nApple Intelligence is closed, limited, and inflexible. Orange Intelligence brings the power of customization and open source innovation to macOS, making it the perfect productivity tool for developers, researchers, and AI enthusiasts.  \n\n---\n## Demo\n\n| Feature                          | Demo GIF                      |\n|----------------------------------|-------------------------------|\n| **Variables and text processing examples** | ![alt text](example1.gif) |\n| **LLMs with local Ollama**       | ![alt text](example2.gif)     |\n\n\n## ✨ Features  \n\n- **Floating Text Processor**: Double-tap the `Option` key to open a sleek floating window.  \n- **Run Any Python Function**: Execute any Python code, from simple string manipulations to advanced AI/LLM integrations (OpenAI, local LLaMA, or your own agents).  \n- **Fully Customizable**: Add your own Python logic to extend its functionality and tailor the app to your specific workflows.  \n- **Global variable replacement**: Global variable replacement (You no longer need to open your notes app to do a simple copy and paste!)\n---\n\n## 🛠️ How It Works  \n\nOrange Intelligence simplifies text processing with a three-step pipeline:  \n\n1. **Capture Text from Any Application**  \n   - Using a clever Applescript trick, the app simulates a global `Cmd+C` to grab the content of the clipboard from the active application.  \n\n2. **Process the Text**  \n   - The floating window opens in focus, allowing the user to select which Python function to run. The selected function processes the clipboard text (e.g., formatting, AI generation, etc.).  \n\n3. **Replace the Text**  \n   - Once processed, the floating window closes, the focus returns to the previous app, and a global `Cmd+V` pastes the updated content back into the app.  \n\n---\n\n## 🚀 Getting Started  \n\n### Prerequisites  \n\n1. **Install Python 3.9+**  \n2. **Install Poetry** for dependency management:  \n   ```bash\n   pip install poetry\n   ```  \n3. **Grant Permissions**: Ensure your Python interpreter has the following rights:  \n   - **Accessibility**: To allow replacing text via `Cmd+V`.  \n   - **Input Monitoring**: To listen for global key shortcuts like the `Option` key.  \n\n4. **Optional Dependencies**:  \n   - **Ollama**: If you want to run local LLaMA models, ensure [Ollama](https://ollama.ai/) is installed and running.  \n   - If you have a custom setup, you might need to adapt the Python code for your environment.  \n\n5. **Optional Configuration**:  \n   - Adjust the log level in `config.py` if needed.  \n\n### Installation  \n\n1. Clone this repository:  \n   ```bash\n   git clone [https://github.com/sharingan-no-kakashi/orange-intelligence.git]\n   cd orange-intelligence\n   ```  \n2. Install dependencies using Poetry:  \n   ```bash\n   poetry install\n   ```  \n3. Start the app using Make:  \n   ```bash\n   make run\n   ```  \n   (Use `make help` to see other available commands.)  \n\n4. When the **orange icon** appears in the system tray, the app is ready to use.  \n\n### Usage  \n\n1. Highlight any text in a macOS application.  \n2. Double-tap the `Option` key to bring up the floating window.  \n3. Select a function to apply to the highlighted text.  \n\n---\n\n## 🧩 Customization  \n\n- Add custom logic by defining callable Python objects in the `extensions` package.  \n- **Every function (or callable object)** defined in the `__init__.py` file of the `extensions` package will automatically appear as an option in the floating window.  \n\nExample:  \n```python\n# extensions/__init__.py\ndef reverse_text(input_text):\n    return input_text[::-1]\n```  \n\n---\n\n## 📝 To-Do and Future Improvements  \n\nThese are the features currently in progress:  \n\n- **Custom Prompts**: Add the ability to pass custom prompts (hint: that's why you see `**kwargs` in the code).  \n- **Text Playground**: Open a new custom window for text processing, allowing users to combine LLM/Python/text utilities in a single workspace.  \n- **Clipboard Restoration**: Automatically restore the content of the clipboard after processing operations.  \n- **UI/UX Enhancements**: Improve the design and user experience for better usability.  \n\n- **Code improvements**: The codebase feels a bit hacky at the moment, there are lots debatable decisions (subprocessing/time.sleep(), controller feels a bit overwhelmed of logic) etc\n- **Other platforms support** There is probably no reason why this cannot be extended to other platforms linux/window.  \n- **CI/CD** Setup a nice ci pipeline to run the lint/banding/mypy and handling versions/releases\n---\n\n## 🏗️ Tech Stack  \n\n- **Python**: The core logic of the app is written in Python for flexibility and extensibility.  \n- **PyQt6**: Provides a polished and responsive UI for the floating window, built with a clean MVC architecture.  \n- **Applescript**: Facilitates system-level clipboard interactions and application switching.  \n\n---\n\n\n## 📝 License  \n\nThis project is licensed under the [MIT License](LICENSE).  \n\n---\n\n## 🤝 Contributing  \n\nWe welcome contributions! Check out the [CONTRIBUTING.md](Contributing.md) for guidelines on how to get involved.  \n\n---\n\n## 📧 Contact  \n\nQuestions, feedback, or ideas? Reach out via Github issues.\n\n---\n\n**Empower your workflow with 🍊 Orange Intelligence — because better is open source.**  \n"
  },
  {
    "path": "app.py",
    "content": "import logging.config\nimport sys\n\nfrom config import CONFIG\nfrom PyQt6.QtWidgets import QApplication\nfrom utils import avoid_dock_macos_icon\n\nfrom core.controller import Controller\nfrom core.model import Model\n\n\ndef main():\n    logging.config.dictConfig(CONFIG[\"logging\"])\n\n    app = QApplication(sys.argv)\n\n    avoid_dock_macos_icon()\n\n    model = Model()\n\n    controller = Controller(model=model, view=app)\n\n    # Run the event loop\n    sys.exit(controller.view.exec())\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "config.py",
    "content": "import os\nimport sys\nimport tempfile\n\nLOGGING_LEVEL = \"DEBUG\"\n\nCONFIG = {\n    \"app\": {\n        \"name\": \"orange-ai\",\n        \"icon\": \"assets/icon.png\",\n    },\n    \"variables\": {\n        \"import_bash_profile\": False,\n    },\n    \"ollama\": {\n        \"name\": \"ollama\",\n        \"url\": \"https://ollama.com\",\n        \"default_model\": \"llama3.1\",\n    },\n    \"openai\": {\"api_key\": os.environ.get(\"OPENAPI_KEY\"), \"default_model\": \"gpt-3.5-turbo\"},\n    \"logging\": {\n        \"version\": 1,\n        \"disable_existing_loggers\": False,\n        \"formatters\": {\n            \"detailed\": {\"format\": \"%(asctime)s - %(levelname)s - %(name)s - %(message)s\"},\n        },\n        \"handlers\": {\n            \"stream\": {\n                \"level\": LOGGING_LEVEL,\n                \"class\": \"logging.StreamHandler\",\n                \"stream\": sys.stdout,\n                \"formatter\": \"detailed\",\n            },\n            \"file\": {\n                \"level\": LOGGING_LEVEL,\n                \"class\": \"logging.FileHandler\",\n                \"filename\": tempfile.NamedTemporaryFile(delete=False).name,\n                \"formatter\": \"detailed\",\n            },\n        },\n        \"loggers\": {\n            \"\": {  # Root logger configuration\n                \"handlers\": [\"stream\", \"file\"],\n                \"level\": LOGGING_LEVEL,\n                \"propagate\": True,\n            },\n        },\n    },\n}\n"
  },
  {
    "path": "core/__init__.py",
    "content": ""
  },
  {
    "path": "core/controller.py",
    "content": "import logging\nimport time\n\nimport pyperclip\nfrom pynput import keyboard\nfrom PyQt6.QtCore import QTimer, pyqtSignal\nfrom PyQt6.QtWidgets import QApplication\nfrom utils import cmd_v, get_current_process_id, get_focused_text, put_app_in_focus, return_app_in_focus\n\nfrom core.model import Model\nfrom core.views.floating_window import FloatingWindow\nfrom core.views.system_tray import SystemTray\nfrom core.views.text_processing import TextWindow\n\nLOG = logging.getLogger(__name__)\n\n\nclass Controller:\n    def __init__(self, model: Model, view: QApplication):\n        self.view = view\n        self.model = model\n        self.option_key = False\n        self.last_time = 0.0\n        self.floating_window_open = False\n        self.text_window_open = False\n        self.focused_process_id = \"\"\n        self.focused_text = \"\"\n        self.processed_text = \"\"\n        self.cmd_pressed = False\n        self.option_pressed = False\n        self.this_process_id = get_current_process_id()\n        self.setup_windows()\n        self.listener = keyboard.Listener(on_press=self.on_press, on_release=self.on_release)\n        self.listener.start()\n\n    def get_window_tabs_items(self) -> dict[str, list[str]]:\n        return self.model.sections\n\n    def update_floating_window_status(self, statue: bool):\n        self.floating_window_open = statue\n\n    def update_text_window_status(self, status: bool):\n        self.text_window_open = status\n\n    def setup_event_handlers(self):\n        self.view.floating_window.custom_signal.connect(self.process_text)\n        self.view.floating_window.event_put_app_focus.connect(self.put_this_app_in_focus)\n        self.view.text_window.event_put_app_focus.connect(self.put_this_app_in_focus)\n\n        self.view.floating_window.windows_event.connect(self.update_floating_window_status)\n        self.view.text_window.windows_event.connect(self.update_text_window_status)\n\n    def recreate_text_window(self):\n        LOG.debug(\"Recreating text window\")\n        self.view.text_window = TextWindow(\n            processing_text=self.focused_text, functions_list=self.model.get_all_functions_flattened()\n        )\n        LOG.debug(\"Text window recreated\")\n\n    def setup_windows(self) -> None:\n        self.view.main_window = SystemTray()\n\n        # Create the system tray icon\n        self.view.main_window.show()\n\n        # Create the floating window, passing the key listener to it\n        tabs = self.get_window_tabs_items()\n\n        self.view.floating_window = FloatingWindow(tabs)\n        self.recreate_text_window()\n        self.setup_event_handlers()\n\n    def get_focused_text(self) -> None:\n        self.focused_text = get_focused_text()\n\n    def return_app_in_focus(self) -> None:\n        self.focused_process_id = return_app_in_focus()\n\n    def put_previous_app_in_focus(self) -> None:\n        return put_app_in_focus(self.focused_process_id)\n\n    def put_this_app_in_focus(self) -> None:\n        return put_app_in_focus(self.this_process_id)\n\n    def set_processed_text(self, section: str, item: str, **kwargs) -> str:\n        processed_text = self.model.process_text(section, item, self.focused_text, **{})\n        pyperclip.copy(processed_text)\n        self.put_previous_app_in_focus()\n        time.sleep(0.3)\n        cmd_v()\n        return self.processed_text\n\n    def process_text(self, section: str, item: str) -> None:\n        QTimer.singleShot(0, lambda: self.set_processed_text(section, item))\n\n    def on_press(self, key: keyboard.Key) -> None:\n        try:\n            # Detect if the pressed key is Cmd or Option (Alt)\n            if key == keyboard.Key.alt_l or key == keyboard.Key.alt_r:\n                self.option_pressed = True\n            elif key == keyboard.Key.cmd_l or key == keyboard.Key.cmd_r:\n                self.cmd_pressed = True\n\n            # If both Cmd and Option keys are pressed simultaneously, open another window\n            if self.cmd_pressed and self.option_pressed:\n                if not self.text_window_open:\n                    self.open_text_window()\n                    self.text_window_open = True\n                self.cmd_pressed = False  # Reset after action to avoid repeated detection\n                self.option_pressed = False\n\n            # If only the Option key is pressed, follow the original logic\n            elif self.option_pressed:\n                current_time = time.time()\n\n                # If two presses are detected within 1 second, open the window\n                if current_time - self.last_time < 0.8:\n                    if self.floating_window_open:\n                        self.close_floating_window()\n                    else:\n                        self.open_floating_window()\n                self.last_time = current_time\n                self.option_pressed = False  # Reset after action\n\n        except AttributeError as e:\n            LOG.debug(f\"AttributeError: {e}\")\n            pass\n\n    def _open_text_window(self) -> None:\n        LOG.debug(\"Opening text window\")\n        self.recreate_text_window()\n\n    def open_text_window(self) -> None:\n        pass  # This is still unreliable\n\n    def open_floating_window(self) -> None:\n        self.get_focused_text()\n        self.return_app_in_focus()\n        QTimer.singleShot(0, self.view.floating_window.show)\n        self.floating_window_open = True\n\n    def close_floating_window(self) -> None:\n        QTimer.singleShot(0, self.view.floating_window.close)\n        self.put_previous_app_in_focus()\n        self.floating_window_open = False\n\n    def on_release(self, key: keyboard.Key) -> None:\n        # Reset flags when keys are released\n        if key == keyboard.Key.alt_l or key == keyboard.Key.alt_r:\n            self.option_pressed = False\n        elif key == keyboard.Key.cmd_l or key == keyboard.Key.cmd_r:\n            self.cmd_pressed = False\n"
  },
  {
    "path": "core/model.py",
    "content": "import logging\n\nfrom utils import load_all_available_functions\n\nimport extensions\nimport extensions.ollama.example\nfrom extensions.variables import variables\n\nLOG = logging.getLogger(__name__)\n\nfrom functools import reduce\n\n\nclass Model:\n    def __init__(self):\n        self.functions = load_all_available_functions(extensions)\n        self.sections = self.get_sections()\n        self.variables = variables\n\n    def get_all_functions_flattened(self) -> dict[str, str]:\n        return reduce(lambda x, y: {**x, **y}, self.functions.values(), {})\n\n    def get_sections(self) -> dict[str, list[str]]:\n        sections = {section: list(functions.keys()) for section, functions in self.functions.items()}\n        sections[\"variables\"] = list(variables.keys())\n\n        return sections\n\n    def process_text(self, section: str, function_name: str, input_text: str, **kwargs) -> str:\n        if section == \"variables\":\n            return self.variables[function_name]\n\n        return self.functions[section][function_name](input_text, **kwargs)\n"
  },
  {
    "path": "core/views/__init__.py",
    "content": ""
  },
  {
    "path": "core/views/floating_window.py",
    "content": "import logging\n\nfrom PyQt6.QtCore import Qt, pyqtSignal\nfrom PyQt6.QtGui import QCloseEvent, QHideEvent, QKeyEvent, QShowEvent\nfrom PyQt6.QtWidgets import QListWidget, QListWidgetItem, QTabWidget, QVBoxLayout, QWidget\n\nfrom core.views.styling.floating_window_style import FloatingWindowStyleOptions\n\nLOG = logging.getLogger(__name__)\n\n\nclass FloatingWindow(QWidget):\n    custom_signal = pyqtSignal(str, str)\n    windows_event = pyqtSignal(bool)\n    process_text_event = pyqtSignal(str, str, str)\n    event_put_app_focus = pyqtSignal()\n\n    def __init__(self, tab_sections: dict):\n        super().__init__()\n        # Set macOS-style window appearance\n        self.setWindowTitle(FloatingWindowStyleOptions.title)\n        self.setGeometry(200, 200, 400, 300)\n        self.setStyleSheet(FloatingWindowStyleOptions.base)\n\n        self.tab_widget = QTabWidget()\n        self.tab_scroll_positions = {}\n        self.set_up_tab_widget(tab_sections)\n\n    def keyPressEvent(self, event: QKeyEvent) -> None:\n        current_index = self.tab_widget.currentIndex()\n        current_widget = self.tab_widget.currentWidget()\n\n        if event.key() == Qt.Key.Key_Escape:\n            self.close()\n\n        # Retrieve the QListWidget inside the current tab\n        if current_widget:\n            list_widget = current_widget.findChild(QListWidget)\n            if list_widget and isinstance(list_widget, QListWidget):\n                # If the current tab contains a QListWidget\n                if event.key() == Qt.Key.Key_Up:\n                    current_row = list_widget.currentRow()\n                    new_row = max(0, current_row - 1)\n                    list_widget.setCurrentRow(new_row)\n                elif event.key() == Qt.Key.Key_Down:\n                    current_row = list_widget.currentRow()\n                    new_row = min(list_widget.count() - 1, current_row + 1)\n                    list_widget.setCurrentRow(new_row)\n\n                if event.key() == Qt.Key.Key_Return:\n                    # Handle Enter key\n                    current_item = list_widget.currentItem()  # Get the currently selected item\n                    if current_item:\n                        tab_name = self.tab_widget.tabText(current_index)\n                        row_text = current_item.text()\n                        self.handle_enter_key(tab_name, row_text, current_index)\n\n                    else:\n                        LOG.debug(\"No item selected in the current list.\")\n            else:\n                LOG.debug(\"Current tab does not contain a QListWidget.\")\n        else:\n            LOG.debug(\"No current widget in the tab.\")\n\n        # Handle Left and Right arrow keys to switch tabs\n        if event.key() == Qt.Key.Key_Right:\n            next_index = (current_index + 1) % self.tab_widget.count()\n            self.tab_widget.setCurrentIndex(next_index)\n        elif event.key() == Qt.Key.Key_Left:\n            previous_index = (current_index - 1) % self.tab_widget.count()\n            self.tab_widget.setCurrentIndex(previous_index)\n        else:\n            # Pass other key events to the parent class\n            super().keyPressEvent(event)\n\n    def set_up_tab_widget(self, tab_sections: dict) -> None:\n        # Create the tab widget\n        self.tab_widget.setTabsClosable(False)\n        self.tab_widget.setStyleSheet(FloatingWindowStyleOptions.tab_widget)\n\n        # Create tabs\n        self.create_tabs(tab_sections)\n\n        # Main layout\n        main_layout = QVBoxLayout()\n        main_layout.setContentsMargins(0, 0, 0, 0)\n        main_layout.addWidget(self.tab_widget)\n        self.setLayout(main_layout)\n\n        # Initialize scroll positions for all tabs\n        for i in range(self.tab_widget.count()):\n            self.tab_scroll_positions[i] = 0\n\n    def create_tabs(self, tab_sections: dict) -> None:\n        for section_name, tab_section in tab_sections.items():\n            tab = QWidget()\n            tab_layout = QVBoxLayout()\n\n            list_widget = QListWidget()\n            list_widget.setStyleSheet(FloatingWindowStyleOptions.list_widget)\n            tab_layout.addWidget(list_widget)\n\n            for key in tab_section:\n                item = QListWidgetItem(key)\n                list_widget.addItem(item)\n            list_widget.setCurrentRow(0)  # Set the current row to the first item\n\n            tab.setLayout(tab_layout)  # Set layout for the tab\n            self.tab_widget.addTab(tab, section_name)  # Add the tab\n\n    def handle_enter_key(self, tab_name: str, text_item: str, tab_index: int) -> None:\n        \"\"\"Close the window and trigger the processText function.\"\"\"\n        self.custom_signal.emit(tab_name, text_item)\n        self.close()  # Close the window\n\n    def closeEvent(self, event: QCloseEvent) -> None:\n        # Hide the window instead of closing it to prevent application shutdown\n        event.ignore()\n        self.hide()\n        # Update the key listener state when the window is hidden\n        self.windows_event.emit(False)\n\n    def hideEvent(self, event: QHideEvent) -> None:\n        # Ensure the key listener flag is updated when the window is hidden\n        self.windows_event.emit(False)\n\n    def showEvent(self, event: QShowEvent) -> None:\n        super().showEvent(event)\n        # Ensure the window is focused when shown\n        self.raise_()  # Raise the window to the top\n        self.activateWindow()  # Make the window active (focused)\n\n        self.windows_event.emit(True)\n\n        self.event_put_app_focus.emit()\n"
  },
  {
    "path": "core/views/styling/__init__.py",
    "content": ""
  },
  {
    "path": "core/views/styling/floating_window_style.py",
    "content": "class FloatingWindowStyleOptions:\n    geometry = \"200, 200, 400, 300\"\n\n    title = \"Orange GUI\"\n\n    base = \"\"\"\n        QWidget {\n            background-color: #f4f4f4; \n            border: 1px solid #ccc;\n            border-radius: 10px;\n            padding: 0;\n        }\n    \"\"\"\n\n    tab_widget = \"\"\"\n        QTabWidget::pane { \n            border: none; \n            background: transparent; \n        }\n        QTabBar::tab { \n            background: #f8f8f8; \n            border: none; \n            padding: 8px 16px; \n            margin: 2px; \n            border-top-left-radius: 8px; \n            border-top-right-radius: 8px; \n        }\n        QTabBar::tab:selected { \n            background: #ffffff; \n            font-weight: bold; \n            color: #007aff; \n        }\n        QTabBar::tab:hover { \n            background: #e9e9e9; \n        }\n    \"\"\"\n\n    search_bar = \"\"\"\n        QLineEdit { \n            background-color: #ffffff; \n            border: 1px solid #d1d1d1; \n            border-radius: 6px; \n            padding: 6px; \n            margin: 6px 0; \n        }\n    \"\"\"\n\n    list_widget = \"\"\"\n        QListWidget { \n            background-color: #ffffff; \n            border: 1px solid #d1d1d1; \n            border-radius: 6px; \n            margin: 8px; \n        }\n        QListWidget::item { \n            padding: 8px; \n            font-size: 14px; \n            color: #333; \n        }\n        QListWidget::item:selected { \n            background-color: #007aff; \n            color: #ffffff; \n            font-weight: bold; \n            border-radius: 4px; \n            margin: 2px; \n        }\n        QListWidget::item:hover { \n            background-color: #e0e0e0; \n            color: #000; \n        }\n    \"\"\"\n"
  },
  {
    "path": "core/views/system_tray.py",
    "content": "from config import CONFIG\nfrom PyQt6.QtCore import QCoreApplication\nfrom PyQt6.QtGui import QAction, QIcon\nfrom PyQt6.QtWidgets import QMenu, QSystemTrayIcon\n\n\nclass SystemTray(QSystemTrayIcon):\n    def __init__(self):\n        icon = CONFIG.get(\"app\").get(\"icon\")\n\n        super().__init__(QIcon(icon))\n\n        # Create the tray menu\n        self.menu = QMenu()\n\n        # Add actions to the menu\n        self.create_menu_actions()\n\n        # Set the menu to the tray icon\n        self.setContextMenu(self.menu)\n\n    def create_menu_actions(self):\n        \"\"\"Creates and adds actions to the context menu.\"\"\"\n\n        # Action to quit the application\n        quit_action = QAction(\"❌ Quit\", self)\n        quit_action.triggered.connect(self.quit_app)\n        quit_action.setToolTip(\"Quit the application\")\n\n        # Add actions to the menu\n        self.menu.addAction(quit_action)\n\n    def open_settings(self):\n        \"\"\"Placeholder for a settings dialog.\"\"\"\n        self.show_message(\"Settings\", \"Settings window would appear here.\")\n\n    def show_message(self, title: str, message: str):\n        \"\"\"Displays a message balloon from the system tray.\"\"\"\n        self.showMessage(title, message, QSystemTrayIcon.MessageIcon.Information)\n\n    def quit_app(self):\n        \"\"\"Exits the application.\"\"\"\n        QCoreApplication.quit()\n"
  },
  {
    "path": "core/views/text_processing.py",
    "content": "import logging\n\nfrom PyQt6.QtCore import Qt, pyqtSignal\nfrom PyQt6.QtGui import QCloseEvent, QHideEvent, QShowEvent\nfrom PyQt6.QtWidgets import (\n    QAbstractItemView,\n    QFrame,\n    QHBoxLayout,\n    QLineEdit,\n    QListWidget,\n    QPushButton,\n    QSplitter,\n    QTabWidget,\n    QTextEdit,\n    QVBoxLayout,\n    QWidget,\n)\n\nLOG = logging.getLogger(__name__)\n\n\nclass TextWindow(QWidget):\n    custom_signal = pyqtSignal(str, str)\n    windows_event = pyqtSignal(bool)\n    process_text_event = pyqtSignal(str, str, str)\n    event_put_app_focus = pyqtSignal()\n\n    def __init__(self, processing_text, functions_list):\n        super().__init__()\n        LOG.debug(f\"Creating text window processing text, {processing_text}\")\n        # Layout for the main window\n        main_layout = QHBoxLayout(self)\n\n        self.processing_text = processing_text\n\n        # Create QTabWidget for text areas\n        self.text_tab_widget = QTabWidget()\n        self.functions_list = functions_list\n        # Add initial text tab\n        self.text_tab = QWidget()\n        text_layout = QVBoxLayout(self.text_tab)\n        self.text_widget = QTextEdit()\n        self.text_widget.setText(self.processing_text)\n        text_layout.addWidget(self.text_widget)\n        self.text_tab_widget.addTab(self.text_tab, \"Text 1\")\n\n        # Create QTabWidget for functions list\n        self.function_tab_widget = QTabWidget()\n\n        # Tab 1 - Function list\n        self.function_list_tab = QWidget()\n        function_layout = QVBoxLayout(self.function_list_tab)\n        self.function_list_widget = QListWidget()\n        function_layout.addWidget(self.function_list_widget)\n        self.function_tab_widget.addTab(self.function_list_tab, \"Functions 1\")\n\n        # Add function names to the list\n        for function_name in functions_list:\n            self.function_list_widget.addItem(function_name)\n\n        # Set the function list widget to be selectable\n        self.function_list_widget.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)\n\n        # Tab 2 - Text area for functions\n        self.function_text_tab = QWidget()\n        function_text_layout = QVBoxLayout(self.function_text_tab)\n        function_text_widget = QTextEdit()\n        function_text_layout.addWidget(function_text_widget)\n        self.function_tab_widget.addTab(self.function_text_tab, \"Function Text\")\n\n        # Split the main layout into two sections: one for text and one for functions\n        splitter = QSplitter(Qt.Orientation.Horizontal)\n        splitter.addWidget(self.text_tab_widget)\n        splitter.addWidget(self.function_tab_widget)\n\n        # Set stretch factors to ensure text and function sections take 90% of the space\n        splitter.setStretchFactor(0, 9)  # Text section takes 9 parts\n        splitter.setStretchFactor(1, 9)  # Function section takes 9 parts\n\n        # Add splitter to the main layout\n        main_layout.addWidget(splitter)\n\n        # Bottom input area with 3 text inputs and buttons, occupying 10% of the horizontal space\n        bottom_layout = QVBoxLayout()\n\n        # Create a container frame for the bottom section to control its width\n        bottom_frame = QFrame()\n        bottom_frame.setLayout(bottom_layout)\n\n        # Create 3 text input fields and 3 run buttons, and add them vertically\n        self.inputs = [QLineEdit() for _ in range(3)]\n        self.run_buttons = [QPushButton(\"Run\") for _ in range(3)]\n\n        for input_field, button in zip(self.inputs, self.run_buttons):\n            bottom_layout.addWidget(input_field)\n            bottom_layout.addWidget(button)\n\n        # Add the bottom frame to the main layout, occupying 10% of the horizontal space\n        bottom_frame.setFixedWidth(self.width() // 10)  # Set the width to 10% of the window size\n        main_layout.addWidget(bottom_frame)\n\n        self.setLayout(main_layout)\n        self.setWindowTitle(\"Text Display and Function List\")\n        self.resize(800, 600)\n\n        # Connect context menus for adding new tabs\n        self.text_tab_widget.tabBar().setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)\n        self.text_tab_widget.tabBar().customContextMenuRequested.connect(self.showTextTabMenu)\n\n        self.function_tab_widget.tabBar().setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)\n        self.function_tab_widget.tabBar().customContextMenuRequested.connect(self.showFunctionTabMenu)\n\n        # Connect the Enter key event to the function application\n        self.function_list_widget.keyPressEvent = self.handle_function_key_event\n\n    def handle_function_key_event(self, event):\n        if event.key() == Qt.Key.Key_Return:  # Detect Enter key press\n            # Get the selected function name from the list\n            selected_item = self.function_list_widget.currentItem()\n            if selected_item:\n                function_name = selected_item.text()\n                # Apply the function to the displayed text\n                self.apply_function(function_name)\n        else:\n            super().keyPressEvent(event)  # Pass the event to the base class if it's not Enter\n\n    def apply_function(self, function_name):\n        # Check if the function name exists in the functions list\n\n        if function_name in self.functions_list:\n            # Apply the function to the current text in the text widget\n            func = self.functions_list[function_name]\n            new_text = func(self.text_widget.toPlainText())\n            self.text_widget.setText(new_text)  # Update the text widget with the result\n\n    def showTextTabMenu(self, pos):\n        # Create and show context menu for adding new text tabs\n        menu = self.text_tab_widget.tabBar().createStandardContextMenu()\n        add_tab_action = menu.addAction(\"Add New Text Tab\")\n        add_tab_action.triggered.connect(self.add_new_text_tab)\n        menu.exec(self.text_tab_widget.tabBar().mapToGlobal(pos))\n\n    def showFunctionTabMenu(self, pos):\n        # Create and show context menu for adding new function tabs\n        menu = self.function_tab_widget.tabBar().createStandardContextMenu()\n        add_tab_action = menu.addAction(\"Add New Function Tab\")\n        add_tab_action.triggered.connect(self.add_new_function_tab)\n        menu.exec(self.function_tab_widget.tabBar().mapToGlobal(pos))\n\n    def add_new_text_tab(self):\n        # Create new text tab\n        new_text_tab = QWidget()\n        new_text_layout = QVBoxLayout(new_text_tab)\n        new_text_widget = QTextEdit()\n        new_text_layout.addWidget(new_text_widget)\n\n        # Add the new text tab\n        new_tab_index = self.text_tab_widget.addTab(new_text_tab, f\"Text {self.text_tab_widget.count() + 1}\")\n\n        # Optionally set the new tab as the current tab\n        self.text_tab_widget.setCurrentIndex(new_tab_index)\n\n    def add_new_function_tab(self):\n        # Create new function list tab\n        new_function_list_tab = QWidget()\n        new_function_list_layout = QVBoxLayout(new_function_list_tab)\n        new_function_list_widget = QListWidget()\n        new_function_list_widget.addItem(\"Uppercase\")  # Default function item\n        new_function_list_widget.addItem(\"Lowercase\")\n        new_function_list_layout.addWidget(new_function_list_widget)\n\n        # Add the new function list tab\n        new_function_list_tab_index = self.function_tab_widget.addTab(\n            new_function_list_tab, f\"Functions {self.function_tab_widget.count() + 1}\"\n        )\n\n        # Create new function text tab\n        new_function_text_tab = QWidget()\n        new_function_text_layout = QVBoxLayout(new_function_text_tab)\n        new_function_text_widget = QTextEdit()\n        new_function_text_layout.addWidget(new_function_text_widget)\n\n        # Add the new function text tab\n        new_function_text_tab_index = self.function_tab_widget.addTab(\n            new_function_text_tab, f\"Function Text {self.function_tab_widget.count()}\"\n        )\n\n        # Optionally set the new function list tab as the current tab\n        self.function_tab_widget.setCurrentIndex(new_function_list_tab_index)\n\n    def closeEvent(self, event: QCloseEvent) -> None:\n        # Hide the window instead of closing it to prevent application shutdown\n        event.ignore()\n        self.hide()\n        # Update the key listener state when the window is hidden\n        self.windows_event.emit(False)\n\n    def hideEvent(self, event: QHideEvent) -> None:\n        # Ensure the key listener flag is updated when the window is hidden\n        self.windows_event.emit(False)\n\n    def showEvent(self, event: QShowEvent) -> None:\n        LOG.debug(\"Text window shown\")\n        super().showEvent(event)\n        # Ensure the window is focused when shown\n        self.raise_()  # Raise the window to the top\n        self.activateWindow()  # Make the window active (focused)\n\n        self.windows_event.emit(True)\n\n        self.event_put_app_focus.emit()\n"
  },
  {
    "path": "extensions/__init__.py",
    "content": ""
  },
  {
    "path": "extensions/basics/__init__.py",
    "content": "import json\n\n\ndef upper_case(text: str, **kwargs) -> str:\n    return text.upper()\n\n\ndef lower_case(text: str, **kwargs) -> str:\n    return text.lower()\n\n\ndef pretty_json(text: str, **kwargs) -> str:\n    return json.dumps(json.loads(text), indent=4)\n\n\ndef a_complex_task_you_do_not_want_to_implement_now(text: str, **kwargs) -> str:\n    return \"This is a complex task that you do not want to implement now.\"\n"
  },
  {
    "path": "extensions/langraph/__init__.py",
    "content": ""
  },
  {
    "path": "extensions/langraph/langraph.py",
    "content": ""
  },
  {
    "path": "extensions/ollama/__init__.py",
    "content": "from extensions.ollama.example import improve_grammar, make_it_polite, translate_to_english  # noqa: F401\n"
  },
  {
    "path": "extensions/ollama/example.py",
    "content": "import logging\n\nfrom config import CONFIG\n\nfrom ollama import ChatResponse, chat\n\nLOG = logging.getLogger(__name__)\n\n\ndef ollama(prompt: str) -> str:\n    response: ChatResponse = chat(\n        model=CONFIG[\"ollama\"][\"default_model\"],\n        messages=[\n            {\n                \"role\": \"user\",\n                \"content\": prompt,\n            },\n        ],\n    )\n    # or access fields directly from the response object\n    LOG.debug(response.message.content)\n    return response.message.content\n\n\ndef improve_grammar(input_text: str, **kwargs) -> str:\n    prompt = (\n        \"Improve the grammar of this sentence. Return only the improved sentence, do not add anything else. \"\n        + input_text\n    )\n    return ollama(prompt)\n\n\ndef translate_to_english(input_text: str, **kwargs) -> str:\n    prompt = (\n        \"Translate this sentence to English. Return only the translated sentence, do not add anything else. \"\n        + input_text\n    )\n    return ollama(prompt)\n\n\ndef make_it_polite(input_text: str, **kwargs) -> str:\n    prompt = (\n        \"Convert this sentence to a polite one. Return only the converted sentence, do not add anything else. \"\n        + input_text\n    )\n    return ollama(prompt)\n"
  },
  {
    "path": "extensions/openai/__init__.py",
    "content": "from extensions.openai.utils import _chat_completion_endpoint\n\n\ndef make_a_joke(text: str, **kwargs) -> str:\n    prompt = f\"Tell me a joke about {text}\"\n\n    return _chat_completion_endpoint(prompt)\n"
  },
  {
    "path": "extensions/openai/utils.py",
    "content": "from config import CONFIG\n\nfrom openai import OpenAI\n\n\ndef _chat_completion_endpoint(content: str) -> str:\n    client = OpenAI(api_key=CONFIG[\"openai\"][\"api_key\"])\n\n    chat_completion = client.chat.completions.create(\n        messages=[\n            {\n                \"role\": \"user\",\n                \"content\": content,\n            }\n        ],\n        model=CONFIG[\"openai\"][\"default_model\"],\n    )\n\n    return chat_completion.choices[0].message.content\n"
  },
  {
    "path": "extensions/variables.py",
    "content": "import os\n\ndev_db_user = os.environ.get(\"dev_db_user\") or \"user\"\n\nvariables = {\n    \"my name:\": \"Yoda\",\n    \"thing i always forget\": \"42\",\n    \"s3 bucket with a weird name\": \"prod-thing-i-always-forget\",\n    \"Prd kafka endpoint\": \"https://prod-thing-i-always-forget.s3.amazonaws.com\",\n    \"that thing once told me\": \"John Snow knows nothing\",\n    \"Json config dev\": '{\"s3bucket\":\"dev-bucket\", \"db\": {\"user\": \"%s\"} }' % dev_db_user,\n}\n"
  },
  {
    "path": "makefile",
    "content": "PROJECT ?= orange-intelligence\nVERSION ?= $(shell grep -m 1 version pyproject.toml | tr -s ' ' | tr -d '\"' | tr -d \"'\" | cut -d' ' -f3)\nPYTHON_VERSION = $(shell grep 'python =' pyproject.toml | sed -n 's/^python = [\"^]*\\([0-9]*\\.[0-9]*\\)\\(.*\\)\"/\\1/p')\n\nversion: ## Show the current version\n\techo \"Current version: $(VERSION)\"\n\nhelp: ## Show help\n\t@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort |\\\n\t\tawk 'BEGIN {FS = \":.*?## \"}; {printf \"\\033[36m%-16s\\033[0m %s\\n\", $$1, $$2}'\n\n.DEFAULT_GOAL=help\n\n.python-version: ## Installs the correct version of python if required\n\t$(if $(shell pyenv versions --bare | grep \"^$(PYTHON_VERSION)\"),, pyenv install $(PYTHON_VERSION))\n\tpyenv local $(shell pyenv versions --bare | grep \"^$(PYTHON_VERSION)\" | tail -n 1)\n\n.venv: .python-version ## Activates and installs the poetry environment.\n\tpoetry env use ~/.pyenv/versions/$(shell cat $<)/bin/python && \\\n\tpoetry install\n\nlint: .venv ## Lint the project\n\tpoetry run flake8\n\nmypy: .venv ## Run mypy\n\tpoetry run mypy\n\ntest: .venv ## Test the project\n\tpoetry run pytest\n\nfmt: .venv ## Run formatting tools for project\n\tpoetry run black . $(if $(CI),--check ,) && poetry run isort . $(if $(CI),--check ,)\n\nbandit: .venv ## Run bandit\n\tpoetry run bandit -c pyproject.toml -r .\n\ncheck: fmt lint mypy bandit test ; ## Run all checks\n\nrun:\n\tpoetry run python3 app.py\n\nclean: ## Clean the project\n\trm -rf .venv\n\trm -rf .python-version\n\trm -rf dist/\n\trm -rf __pycache__\n\trm -rf .pytest_cache\n\trm -rf .mypy_cache\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[tool.poetry]\nname = \"orange-intelligence\"\nversion = \"0.1.0\"\ndescription = \"The Orange Intelligence for mac os x\"\nauthors = [\"Pietro\"]\nreadme = \"README.md\"\npackages = [\n    { include = \"core\" },\n    { include = \"extensions\" },\n]\n\n[tool.poetry.dependencies]\npython = \"^3.9\"\npynput = \"1.7.7\"\npyperclip=\"1.9.0\"\nPyQt6=\"6.8.0\"\nollama=\"0.4.7\"\npyobjc-framework-Cocoa=\"11.0\"\nopenai=\"1.60.1\"\n\n[tool.poetry.dev-dependencies]\nblack = \"^23.7.0\"\nflake8 = \"^6.1.0\"\nisort = \"^5.12.0\"\nmypy = \"^1.8.0\"\npytest = \"^7.4.0\"\npytest-cov = \"^4.1.0\"\nflake8-eradicate = \"^1.5.0\"\nflake8-pytest-style = \"^1.7.2\"\npep8-naming = \"^0.13.3\"\nflake8-bugbear = \"^24.2.6\"\nflake8-annotations = \"^3.0.1\"\nflake8-simplify = \"^0.21.0\"\nflake8-print = \"^5.0.0\"\nbandit = {extras = [\"toml\"], version = \"^1.7.7\"}\n\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\n\n\n[tool.black]\nline-length = 120\ntarget-version = ['py312']\ninclude = '\\.pyi?$'\nexclude = '''\n^/(\n  (\n    \\.eggs\n    | .venv\n    | \\.git\n    | build\n    | dist\n    | notebooks\n  )\n)\n'''\n\n[tool.isort]\nline_length = 120\nprofile = \"black\"\nsrc_paths = [\"core\", \"tests\", \"extensions\"]\n\n[tool.pytest.ini_options]\nminversion = \"6.0\"\ntestpaths = [\"tests\"]\npython_files = [\"tests.py\", \"test_*.py\", \"*_tests.py\"]\n\n[tool.coverage.report]\nexclude_also = [\n    \"if __name__ == .__main__.:\",\n    \"if TYPE_CHECKING:\",\n    ]\n\n[tool.mypy]\nfiles = [\"core/**/*.py\", \"extensions/**/*.py\", \"tests/**/*.py\", \"utils/**/*.py\"]\nfollow_imports = \"normal\"\nstrict_optional = \"False\"\nwarn_redundant_casts = \"True\"\nwarn_unused_ignores = \"True\"\ndisallow_any_generics = \"False\"\ncheck_untyped_defs = \"True\"\nno_implicit_reexport = \"True\"\nno_implicit_optional = \"True\"\ndisallow_untyped_defs = \"False\"\nignore_missing_imports = \"True\"\nnamespace_packages = \"True\"\ndisallow_any_unimported = \"True\"\nexclude = []\n\n[tool.bandit]\nexclude_dirs = [\"tests\", \".venv\", \"dist\"]"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/test_dummy.py",
    "content": "def test_dummy_assert_true():\n    assert True\n"
  },
  {
    "path": "utils/__init__.py",
    "content": "import importlib\nimport logging\nimport os\nimport pkgutil\nimport subprocess\nimport sys\nimport time\nimport types\nimport typing\n\nimport pyperclip\nfrom AppKit import NSApp\nfrom PyQt6.QtCore import QProcess\n\nLOG = logging.getLogger(__name__)\n\n\ndef get_current_process_id() -> str:\n    return f\"{os.getpid()}\"\n\n\ndef get_focused_text() -> str:\n    cmd_c()\n    time.sleep(0.3)\n    # Get clipboard content\n    clipboard_content = pyperclip.paste()\n    return clipboard_content.strip()\n\n\ndef cmd_v() -> None:\n    applescript = \"\"\"\n    tell application \"System Events\"\n        keystroke \"v\" using {command down}\n    end tell\n    \"\"\"\n    subprocess.run([\"osascript\", \"-e\", applescript])\n\n\ndef return_app_in_focus() -> str:\n    command = \"\"\"/usr/bin/osascript -e 'tell application \"System Events\"\n                set frontApp to first application process whose frontmost is true\n                return unix id of frontApp\n                end tell' \"\"\"\n    res = subprocess.run(command, shell=True, stdin=sys.stdin, stdout=subprocess.PIPE, stderr=sys.stderr, text=True)\n    return res.stdout.strip()\n\n\ndef cmd_c() -> None:\n    applescript = \"\"\"\n    tell application \"System Events\"\n        keystroke \"c\" using {command down}\n    end tell\n    \"\"\"\n    subprocess.run([\"osascript\", \"-e\", applescript])\n\n\ndef put_app_in_focus(process_id: str) -> None:\n    script = f\"\"\"tell application \"System Events\"\n                 set frontmost of (first process whose unix id is {process_id}) to true\n                 end tell\"\"\"\n    QProcess.startDetached(\"/usr/bin/osascript\", [\"-e\", script])\n\n\ndef put_this_app_in_focus() -> None:\n    this_process_id = get_current_process_id()\n    return put_app_in_focus(this_process_id)\n\n\ndef import_package_init_functions(package: types.ModuleType) -> dict[str, dict[str, typing.Callable]]:\n    # List all submodules (modules and subpackages) in the package\n    submodules = [module.name for module in pkgutil.iter_modules(package.__path__)]\n    callables = {}\n\n    for submodule in submodules:\n        # Dynamically import the __init__.py of the subpackage\n        try:\n            subpackage = importlib.import_module(f\"{package.__name__}.{submodule}\")\n            if hasattr(subpackage, \"__init__\"):\n                # Get all callables (functions and callable objects) in the __init__.py of the subpackage\n                subpackage_callables = {\n                    item: getattr(subpackage, item)\n                    for item in dir(subpackage)\n                    if callable(getattr(subpackage, item))\n                    if not item.startswith(\"_\")\n                }\n\n                # Store callables with their names\n                callables[submodule] = subpackage_callables\n\n        except Exception as e:\n            LOG.error(f\"Error importing {submodule}: {e}\")\n\n    return callables\n\n\ndef load_all_available_functions(package: types.ModuleType) -> dict[str, dict[str, typing.Callable]]:\n    return {name: functions for name, functions in import_package_init_functions(package).items() if len(functions) > 0}\n\n\ndef avoid_dock_macos_icon():\n    NSApp.setActivationPolicy_(1)\n"
  }
]