# llms.txt Source: https://docs.lynxhub.app/ai-agents/llms-txt Index LynxHub documentation for AI models using automated llms.txt and llms-full.txt files. LynxHub provides automated `llms.txt` and `llms-full.txt` files so AI models and developer tools can index and process the documentation site efficiently. The `llms.txt` standard is a machine-readable directory format designed specifically for Large Language Models (LLMs), similar to how `sitemap.xml` functions for search engine web crawlers. ## Available endpoints You can access LynxHub documentation index files at the following canonical URLs: * **`llms.txt` Index:** [`https://docs.lynxhub.app/llms.txt`](https://docs.lynxhub.app/llms.txt) (also available at `https://docs.lynxhub.app/.well-known/llms.txt`) * **`llms-full.txt` Complete Content:** [`https://docs.lynxhub.app/llms-full.txt`](https://docs.lynxhub.app/llms-full.txt) (also available at `https://docs.lynxhub.app/.well-known/llms-full.txt`) ## File formats explained LynxHub documentation serves two distinct text files depending on your AI model's context needs: ### `llms.txt` The `llms.txt` file acts as a structured directory. It lists all available documentation pages alongside their descriptions and direct Markdown links. * Contains the site title, summary description, and categorized links to every page. * Allows AI tools to quickly discover where specific LynxHub features or configuration settings are documented. * Keeps context size low while enabling targeted sub-fetches. ### `llms-full.txt` The `llms-full.txt` file concatenates the entire LynxHub documentation into a single Markdown document. * Contains complete documentation text, code examples, and page content. * Useful when you want to feed the entire documentation suite directly into an LLM context window in a single request. ## Connect AI tools to `llms.txt` You can use `llms.txt` with various AI coding assistants and LLM tools: Pass `https://docs.lynxhub.app/llms.txt` directly to web-connected AI tools such as ChatGPT, Claude, or custom LLM agents. Ask the agent to review the index file to locate the specific LynxHub topic you need help with. ```text theme={null} Please check https://docs.lynxhub.app/llms.txt to find documentation on LynxHub extension development guide. ``` If your LLM context window supports large inputs, provide `https://docs.lynxhub.app/llms-full.txt` to give the model complete knowledge of LynxHub in one pass. ## When to use `llms.txt` Use `llms.txt` when you want a lightweight overview of all LynxHub documentation pages. * You need to discover available documentation topics. * You want to retrieve specific pages on demand without consuming excessive context. * You are configuring custom RAG pipelines or web scrapers. * You need interactive, real-time doc search inside your IDE (use [MCP Server](/ai-agents/mcp-server)). * You want AI agents to execute structured LynxHub workflows (use [skill.md](/ai-agents/skill-md)). # Model Context Protocol Source: https://docs.lynxhub.app/ai-agents/mcp-server Connect AI tools like Claude, Cursor, and VS Code to LynxHub's hosted search MCP server. LynxHub hosts an open [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) search server at `https://docs.lynxhub.app/mcp`. Connecting your AI application to the LynxHub MCP server allows your model to interactively search, browse, and retrieve up-to-date LynxHub documentation while generating responses. ## Hosted MCP URL You can connect your AI client to LynxHub's public search MCP server using: * **MCP Endpoint:** [`https://docs.lynxhub.app/mcp`](https://docs.lynxhub.app/mcp) * **Discovery Endpoint:** `https://docs.lynxhub.app/.well-known/mcp` ## Available MCP tools and resources When connected, your AI agent gains access to the following capabilities: ### Tools * **`search`:** Searches across all LynxHub documentation pages to find relevant snippets and topic URLs matching a query. * **`query docs filesystem`:** Reads virtual documentation directories and fetches full Markdown contents of specific LynxHub pages in a single batch call. * **`submit feedback`:** Allows AI agents to report outdated or missing documentation back to the LynxHub team. ### Resources * **`skill.md`:** Exposes [LynxHub skills](/ai-agents/skill-md) as MCP resources so your agent can inspect actionable workflows without additional CLI setup. ## Connect AI tools to the LynxHub MCP server Follow the setup instructions below for your preferred AI tool or editor: To connect LynxHub documentation to Claude: Navigate to the **Connectors** section in your [Claude Settings](https://claude.ai/settings/connectors). Click **Add custom connector** and fill in the details: * **Name:** `LynxHub` * **URL:** `https://docs.lynxhub.app/mcp` In your Claude chat window, click the attachments icon (+), select **LynxHub**, and ask your question. To add the LynxHub MCP server to Claude Code, run the following terminal command: ```bash theme={null} claude mcp add --transport http LynxHub https://docs.lynxhub.app/mcp ``` Verify the connection by running: ```bash theme={null} claude mcp list ``` To add LynxHub search to Cursor: Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on macOS), search for `Open MCP settings`, and select **Add custom MCP**. Add the `LynxHub` server configuration to your `mcp.json` file: ```json theme={null} { "mcpServers": { "LynxHub": { "url": "https://docs.lynxhub.app/mcp" } } } ``` Ask Cursor Chat: *"How do I configure terminal hotkeys in LynxHub?"* Cursor will automatically call the LynxHub search MCP tool. To connect LynxHub MCP in VS Code: 1. Create or open `.vscode/mcp.json` in your workspace. 2. Add the LynxHub server entry: ```json theme={null} { "servers": { "LynxHub": { "type": "http", "url": "https://docs.lynxhub.app/mcp" } } } ``` To connect LynxHub MCP to Codex CLI: Open `~/.codex/config.toml` and add the following configuration block: ```toml theme={null} [mcp_servers.lynxhub] url = "https://docs.lynxhub.app/mcp" ``` ## Which AI integration to use and when LynxHub provides three complementary AI integration standards. Refer to the matrix below to choose the best option for your workflow: | Feature | `llms.txt` | `skill.md` | MCP Server | | :------------------ | :------------------------------------ | :---------------------------------- | :--------------------------------------- | | **Primary Purpose** | Documentation index for LLMs | Actionable capabilities & workflows | Real-time interactive doc search | | **Access URL** | `https://docs.lynxhub.app/llms.txt` | `https://docs.lynxhub.app/skill.md` | `https://docs.lynxhub.app/mcp` | | **Best For** | Static indexing & RAG pipelines | Plugin development & task execution | Coding in IDEs (Cursor, VS Code, Claude) | | **Context Usage** | Low (index) to High (`llms-full.txt`) | Low (loaded on demand) | Zero context overhead until searched | | **Interactivity** | Passive read | Guided step-by-step | Active tool calling & batch queries | **Pro-tip:** For the best coding experience in Cursor or VS Code, connect the **MCP Server** for real-time doc lookups, and load **`skill.md`** when building custom LynxHub extensions. # skill.md Source: https://docs.lynxhub.app/ai-agents/skill-md Integrate LynxHub capabilities and actionable workflows into AI agents using skill.md. LynxHub hosts a `skill.md` file at the root of the documentation site to describe LynxHub capabilities, required parameters, and workflows directly to AI agents. The `skill.md` specification follows the [agent-skills](https://agentskills.io/specification) standard. It allows AI coding assistants (such as Cursor, Claude Code, Antigravity, and custom subagents) to understand how to build extensions, manage modules, configure settings, and resolve issues within LynxHub. ## Hosted skill URL You can access the LynxHub skill file at: * **Skill File:** [`https://docs.lynxhub.app/skill.md`](https://docs.lynxhub.app/skill.md) ## Add LynxHub skills to your AI agent You can easily equip your AI agent with LynxHub skills using the command line or MCP resource auto-discovery. ### Install using the Skills CLI Run the following command in your terminal to fetch and install LynxHub skills directly into your AI agent context: ```bash theme={null} npx skills add https://docs.lynxhub.app/ ``` This adds LynxHub operational rules, module architecture details, and plugin development steps into your agent environment. ### Discover via MCP resources When you connect your AI agent to the [LynxHub search MCP server](/ai-agents/mcp-server), the `skill.md` file is automatically exposed as an MCP resource. Your agent can inspect and utilize these skills without requiring a separate CLI installation. ## `llms.txt` vs `skill.md` While both formats assist AI models, they target different operational needs: * **`llms.txt` is a documentation index:** It lists available documentation pages so agents know where to find informational text. * **`skill.md` is a capability framework:** It instructs agents on what tasks they can execute, what inputs they need, and what constraints apply when interacting with LynxHub. ## What `skill.md` enables for agents When an AI agent loads `https://docs.lynxhub.app/skill.md`, it learns: 1. **Plugin and extension development:** Steps for building renderer frontends, main process handlers, and UI slots. 2. **Module management:** Instructions for configuring card methods, installation steppers, and post-install hooks. 3. **Application configuration:** Best practices for tuning hotkeys, terminal shell environments, browser behavior, and performance limits. ## When to use `skill.md` Use `skill.md` when you want your AI agent to actively assist you with complex development or configuration tasks. * You are building LynxHub extensions or custom modules. * You want your agent to follow verified step-by-step development workflows. * You are prompting an AI coding assistant to write or refactor LynxHub code. * You only need to search or reference standard text documentation (use [llms.txt](/ai-agents/llms-txt)). * You want interactive real-time documentation retrieval inside your IDE (use [MCP Server](/ai-agents/mcp-server)). # Browser Settings Source: https://docs.lynxhub.app/application/configuration/browser Configure the integrated web browser, audio controls, downloads, user agents, and browser data clearing. Customize the behavior, download routing, and identity of LynxHub’s integrated browser. ## Audio control Control how audio is played across your browser tabs: * **Global Mute:** Mutes all audio output from browser tabs. Individual tab mute buttons remain functional but will have no audible effect when global mute is active. * To toggle this, look for **Global Mute** in the **Browser** section of settings. ## Downloads Customize download destinations and prompt behaviors: * **Download Location:** Displays the folder where downloaded files are saved. Click **Change Location** to choose another folder on your system. * **Download Behavior:** * **Use default location:** Saves all downloads automatically to the specified directory. * **Always ask where to save:** Prompts you to select a directory and rename the file for each download. ## Link handling Choose where clicked web links are opened: * **Open links externally:** When enabled, any links clicked within the browser or dashboard will open in your computer’s default system web browser (e.g. Chrome, Firefox, Safari) rather than inside LynxHub. * Toggle this under **Open links externally** in the settings. ## User agent Websites use user agents to identify the browser type. LynxHub allows you to customize this header to resolve compatibility issues: * **User Agent presets:** * **LynxHub (Default):** The standard identification header sent by LynxHub. * **Electron:** Identifies the browser as standard Electron. * **Chrome:** Identifies the browser as standard Google Chrome. * **Custom:** Allows you to input a custom user agent string. * **Custom user agent override:** Select **Custom** from the dropdown menu, paste your user agent string into the text field, and click the save icon. If a web interface fails to load or blocks you (for example, warning that your browser is unsupported), try switching your user agent to **Chrome**. ## Clear browser data Wipes local web storage and cache directories. You can selectively clear: * **Cache:** Clears temporary web assets to force websites to reload fresh files. * **Cookies:** Clears website session data and logins. * **Favorites:** Wipes your bookmarked URL links. * **History:** Clears your web navigation history. * **Fav Icons:** Wipes cached website icons. To clear data: 1. In the **Browser** section, select the checkboxes for the data types you want to delete. 2. Click **Clear** to wipe the selected data. # Data Management Source: https://docs.lynxhub.app/application/configuration/data-management Manage application storage folders, plugin update check frequencies, image cache statistics, and factory reset parameters. Configure where LynxHub stores its files, how often it checks for updates, and how to reset your settings. ## Root data folder All of LynxHub's local files, including dashboard cards, installed modules, extensions, and helper binaries, are saved in a single root data directory. To manage your data folder: 1. In settings, navigate to the **Data** section. 2. The current directory path is displayed on the main folder button: * Click the path button to open this folder in your system's file explorer. 3. To change the storage directory: * Click **Change**. * Select a new folder on your drive. * The application will automatically prompt you to restart to apply the change. Changing this folder is useful if your primary OS drive has limited space and you want to move large AI model binaries or web extensions to a secondary drive (e.g. an SSD). ## Card update frequency Configure how frequently the application checks for updates for your installed dashboard cards: * **Update Check Frequency (Minutes):** Sets the time interval (in minutes) between update checks. The default is `30` minutes, with a minimum of `2` minutes. * Adjust this using the increment or decrement arrows under **Card Update Interval** in settings. ## Image cache LynxHub caches remote images locally to accelerate page load speeds. The cache automatically purges files older than 7 days. To view cache statistics or manually clear space: 1. Open settings and scroll to the **Image Cache** section. 2. View active statistics: * **Cached Items:** The total number of images saved in the local cache. * **Cache Size:** The disk space currently consumed by cached images. * **Last Cleanup:** The date of the last automatic cache purge. 3. Click **Clear Image Cache** to manually delete all cached images. ## Reset settings If you experience persistent errors or wish to start fresh, you can reset the application configuration database back to factory defaults. Performing a reset will wipe all custom settings, saved quick commands, hotkeys, and history. It will not delete your downloaded card modules or extensions, but it will unlink them from your current dashboard. To perform a factory reset: 1. Scroll to the **Clear** section in settings. 2. Click **Reset Settings (Restart Required)**. 3. A confirmation dialog will appear. Click **Reset & Restart** to wipe the database and reload the application. # General Settings Source: https://docs.lynxhub.app/application/configuration/general Configure appearance, operating system integration, telemetry, and confirmation prompts. Customize how LynxHub integrates with your system and manages confirmation alerts. ## Theme Select the visual style for the LynxHub interface: * **Dark:** A dark theme designed to reduce eye strain. * **Light:** A light, high-contrast theme. * **System Default:** Automatically matches your operating system appearance settings. To change your theme: 1. Open **Settings** from the navigation bar. 2. Under the **General** section, locate **Theme**. 3. Select your preference from the dropdown menu. ## OS integration Configure how the LynxHub window interacts with your operating system taskbar, dock, or system tray. To customize taskbar behavior: 1. Under **General**, locate the **Taskbar Options** (or **Dock Options** on macOS). 2. Choose one of the following modes: * **Taskbar & Tray** / **Dock & Tray:** The app appears in both your taskbar/dock and the system tray. This is the default. * **Taskbar Only** / **Dock Only:** The app only appears in the taskbar or dock. * **Tray Only:** Hides the taskbar/dock icon. The app runs silently in the background and is accessed via the system tray. This option is not available on Linux. * **Taskbar when focused, Tray when minimized:** Shows the app in the taskbar or dock while focused. Minimized windows are hidden and run from the system tray. Using **Tray Only** or **Tray when minimized** is useful to keep LynxHub running in the background without cluttering your taskbar. ## Window titles Enable or disable dynamic window titles: * **Dynamic App Title:** When enabled, the application updates the window title and taskbar name based on your active tab or running process. * Toggle this setting under **Dynamic App Title** in the **General** section. ## Telemetry and crash reporting Help the development team identify bugs and optimize performance by sharing anonymous diagnostic data: * **Help Improve LynxHub:** Toggles anonymous error reporting via Sentry. It sends crash dumps and error stack traces. * **Include User Interactions:** Captures anonymous UI clicks and keyboard navigation events immediately preceding a crash. This helps developers trace the exact steps that caused the issue. To configure telemetry: 1. Locate **Help Improve LynxHub** in the settings. 2. Toggle the switch to enable or disable telemetry. 3. If telemetry is enabled, you can also toggle **Include User Interactions**. ## Confirmations Manage confirmation dialogs to prevent accidental actions. You can toggle confirmation prompts for the following events: * **Close App:** Prompts you before exiting the LynxHub application. * **Close Tab:** Prompts you before closing a tab that has an active terminal session. * **Terminate Process:** Prompts you before stopping a running background execution. * **Send Exit Signal (Terminal):** Prompts you before sending a termination signal to a terminal process. You can bypass any confirmation modal. Hold the `CTRL` key on your keyboard while clicking the close or exit buttons to skip the prompt. # Performance Settings Source: https://docs.lynxhub.app/application/configuration/performance Configure GPU acceleration, memory allocations, caching, rendering profiles, and hardware parameters. Optimize LynxHub’s speed, resource consumption, and graphic rendering capabilities to match your hardware. Most changes in the **Performance** settings tab require you to restart the LynxHub application before they take effect. ## Rendering and GPU acceleration Adjust how visual assets and pages are rendered: * **Use Hardware Acceleration:** Enables GPU-assisted UI rendering. If you experience UI freezing, lagging, or black screens, disable this setting to fall back to CPU software rendering. * **GPU Rasterization:** Offloads layout and page drawing tasks to the GPU. This improves scrolling performance and animation smoothness. * **Zero-Copy GPU Buffers:** Optimizes image and video memory transfers directly between your system RAM and GPU VRAM. Disable this if you notice visual glitches or checkerboard rendering artifacts. * **High DPI Support:** Enables high-resolution scaling. This prevents blurry layouts on 4K or high-density monitors. * **Enable Skia Graphite:** Enables Skia Graphite rendering. This uses the Dawn backend by default. ## Media and execution Optimize local script and media execution: * **Hardware Video Decoding:** Uses GPU hardware to decode videos. Disable this if web players freeze or crash during playback. * **WebAssembly SIMD:** Enables Single Instruction Multiple Data (SIMD) vector optimization for WebAssembly runtimes. This improves execution speed for local AI web interfaces. * **Parallel Downloading:** Enables multi-connection downloading to accelerate file download speeds. ## JavaScript and cache limits Manage heap sizes and disk usage: * **JavaScript Memory Limit:** Sets the V8 JavaScript engine’s maximum heap allocation. * **2 GB:** Minimizes RAM footprint. Good for budget setups. * **4 GB (Default):** Balanced limit suitable for most developer and user environments. * **8 GB:** Allocates a larger heap. Useful when loading huge datasets, large AI modules, or complex WebAssembly layouts to prevent "Out of Memory" crashes. * **Disk Cache Size:** Controls how much disk space is allocated to browser caching: * **Default:** Uses browser-managed defaults. * **256 MB:** Small footprint for devices with limited storage. * **512 MB:** Balanced cache limit. * **1 GB:** Large cache capacity to accelerate load times on frequently visited sites. ## Display and color Configure color gamut outputs and autoplay policies: * **Force Color Profile:** Forces the rendering engine to output using a specific color space: * **Default:** System-defined color profile. * **sRGB:** Standard color gamut suitable for most displays. * **Display P3:** Wide color gamut recommended for compatible high-dynamic-range (HDR) screens. * **Color Spin (Gamma 2.4):** Alternative gamma calibration profile. * **Media Autoplay Policy:** Configures when audio and video play automatically: * **Default:** Follows standard browser autoplay restrictions. * **Allow Autoplay:** Allows video/audio to start playing immediately without requiring interaction. * **Require Gesture:** Requires some user interaction (like scrolling or typing) before playing. * **Require Page Activation:** Requires a click or touch event on the page before playback starts. ## Advanced hardware overrides Overrule standard graphics limits for advanced setups: * **Force High-Performance GPU:** Enforces the use of a discrete, high-performance GPU on multi-GPU systems (such as laptops with integrated Intel graphics and a dedicated NVIDIA/AMD graphics card). * **Ignore GPU Blocklist:** Overrides Chromium's internal driver compatibility checks. This allows you to force hardware acceleration on older or unsupported graphics drivers, but may cause rendering crashes. * **Disable GPU VSync:** Bypasses vertical synchronization limits. This reduces input latency for terminal entries but can cause noticeable screen tearing. * **Disable Frame Rate Limit:** Removes the default 60 FPS rendering cap. Enabling this makes scrolling feel buttery smooth on high-refresh-rate monitors (120Hz, 144Hz+), but increases power consumption and heat generation. # Startup & Hotkeys Source: https://docs.lynxhub.app/application/configuration/startup-hotkeys Configure startup behaviors, initial window states, active session restoration, and keyboard shortcuts. Configure how LynxHub behaves when it starts and manage keyboard shortcuts for faster navigation. ## Startup settings Adjust how the application launches and restores your workspace. To customize startup behavior: 1. Open **Settings** and select the **Startup** tab. 2. Configure the following settings: * **Launch on System Startup (Windows only):** Automatically starts LynxHub when your system boots up. * **Start in Last Window Size and Position:** Restores the exact window dimensions and desktop position from your last session. * **Start Maximized:** Launches the application in a maximized state. * **Start Minimized:** Launches the application minimized (often directly to the system tray/dock, depending on your taskbar settings). Combine this with **Launch on System Startup** to start LynxHub silently in the background. * **Resume Last Session:** Restores the last active page when reopening the app. If disabled, the application opens to the **Home** page. * **Disable Loading Window Animations:** Disables loading window animations. This improves startup performance on lower-end devices or VMs. ## Hotkeys LynxHub provides customizable keyboard shortcuts for application, tab, browser, and terminal actions. To manage your hotkeys: 1. Navigate to **Settings** and select **Hotkeys**. 2. To record a new shortcut: * Click the keyboard icon next to the action. * Press the key combination on your keyboard (e.g., `Ctrl` + `Shift` + `T`). * The new shortcut saves automatically. 3. Click **Reset to Defaults** at the bottom of the section to revert all shortcuts. ### Default hotkeys The application includes the following default shortcuts: #### Application | Action | Windows/Linux Shortcut | macOS Shortcut | Description | | :------------------------ | :--------------------- | :------------- | :---------------------------------------------- | | **Toggle Fullscreen** | `F11` | `F12` | Switches between fullscreen and windowed modes. | | **Toggle Navigation Bar** | `Alt` + `A` | `Alt` + `A` | Shows or hides the navigation panel. | #### Tab management | Action | Windows/Linux Shortcut | macOS Shortcut | Description | | :---------------------- | :--------------------- | :-------------------- | :-------------------------------------------------------------- | | **New Tab** | `Ctrl` + `T` | `Cmd` + `T` | Opens a new blank tab. | | **New Browser Tab** | `Ctrl` + `Shift` + `T` | `Cmd` + `Shift` + `T` | Opens a new browser tab. | | **New Terminal Tab** | `Alt` + `T` | `Cmd` + `Alt` + `T` | Opens a new terminal tab. | | **New Combination Tab** | `Ctrl` + `E` | `Cmd` + `E` | Opens a combined browser and terminal tab. | | **Toggle View** | `Alt` + `Q` | `Alt` + `Q` | Toggles between terminal and browser views in combination tabs. | | **Switch Tab** | `Ctrl` + `Tab` | `Cmd` + `Tab` | Switches between open tabs. | | **Next Tab** | `Alt` + `Right Arrow` | `Alt` + `Right Arrow` | Switches to the next tab. | | **Previous Tab** | `Alt` + `Left Arrow` | `Alt` + `Left Arrow` | Switches to the previous tab. | | **Close Active Tab** | `Ctrl` + `W` | `Cmd` + `W` | Closes the active tab. | #### Browser | Action | Windows/Linux Shortcut | macOS Shortcut | Description | | :--------------------- | :--------------------- | :------------------ | :------------------------------------------------ | | **Toggle DevTools** | `Ctrl` + `Shift` + `I` | `Cmd` + `Alt` + `I` | Opens or closes browser Developer Tools. | | **Find in web page** | `Ctrl` + `F` | `Cmd` + `F` | Searches for text within the current browser tab. | | **Refresh Active Tab** | `F5` | `F5` | Reloads the current tab. | #### Terminal * **Terminal Quick Commands (1-6):** Executes the corresponding terminal quick command in the active terminal session. These do not have default keybindings, but you can assign custom shortcuts in settings. # Terminal Settings Source: https://docs.lynxhub.app/application/configuration/terminal Configure the built-in terminal emulator, Windows integration, text rendering, cursor styles, and exit behaviors. Customize the behavior and appearance of LynxHub’s integrated terminal emulator. ## Windows integration Manage how terminal processes are executed on Windows: * **Use Conpty (Windows):** Specifies whether the terminal should use Windows’ native ConPTY system. * **Auto:** Automatically determines the best configuration. This is the default. * **Yes:** Explicitly forces the use of ConPTY. * **No:** Disables ConPTY. Disabling this can help resolve terminal spawning issues on older Windows builds or in virtual environments. ## Text and buffer Configure text size, font formatting, and buffer sizes: * **Font Size:** Sets the terminal font size in pixels. The default is `14`. * **Scrollback:** Sets the maximum number of lines kept in the terminal scroll history. The default is `1000`. Increase this if you run commands with extensive logs. * **Enable Ligatures:** Toggle font ligatures. When enabled, character sequences like `=>`, `!=`, or `===` are rendered as unified symbols. * **Output Color:** Enables or disables colored terminal logs. Disabling this will render all text in plain monochrome. ## Cursor styling Adjust the cursor's appearance to fit your preference: * **Blink Cursor:** Toggles whether the terminal cursor flashes. * **Cursor Style:** Selects the shape of the cursor when active: * **Bar** (Default) * **Block** * **Underline** * **Cursor Inactive Style:** Selects the shape of the cursor when the terminal pane loses focus: * **Bar** * **Block** * **Underline** * **Outline** * **None** (Hides the cursor entirely when inactive) ## Terminal shortcuts and quick commands You can configure up to 6 custom quick commands: 1. Under the **Terminal** settings, locate the **Terminal Quick Commands** slots. 2. Fill in a **Label** (what displays on the button) and the **Command** text (e.g. `npm run dev` or `git status`). 3. These commands appear as shortcut buttons in active terminal tabs. 4. You can trigger them using hotkeys (assignable in the **Hotkeys** settings). ## Tab and exit behavior Configure how tabs respond when shell processes finish: * **Close Tab on Exit:** Automatically closes the terminal tab when the shell process finishes (e.g. typing `exit`). * **Send "Y" with Exit:** Automatically sends `y` followed by `Enter` when terminating a process. This bypasses prompts like `Terminate batch job (Y/N)?` when stopping terminal tasks. ## Link handling Configure link interaction in terminal outputs: * **Open Links in New Tab:** Automatically opens clicked URLs inside a new LynxHub browser tab. * **Middle Click:** You can middle-click any URL in the terminal to force it to open in a new tab, regardless of this setting. * **Resize Delay:** Sets the delay (in milliseconds) before the terminal viewport adapts to window resizing. The default is `77ms`. Tweak this if you notice visual lag or rendering artifacts when resizing the window. ## Reset terminal settings To revert all terminal configuration settings, cursor styles, and quick commands back to their defaults: 1. Under the **Terminal** settings, scroll to the bottom of the page. 2. Click **Reset All Terminal Settings**. # Card launch configuration Source: https://docs.lynxhub.app/application/launch-configuration Configure start arguments, pre-launch scripts, window layouts, and address detection for individual cards. Each card module on the dashboard has its own launch configuration. Use this configuration to manage launch arguments, pre-launch scripts, browser viewports, and custom commands. To open the launch configuration panel: 1. Locate the card on your dashboard. 2. Click the **3 dots menu** in the top-right corner of the card. 3. Click **Launch Config** from the dropdown menu. *** ## Visual argument manager The visual argument manager allows you to toggle command-line arguments without editing scripts: * **Presets**: Choose from pre-configured argument presets or save your own custom presets using the **Save Preset** option. * **Custom arguments**: Toggle individual argument flags (such as `--use-cpu`, `--lowvram`, or `--xformers`) to control how the local AI model runs on your hardware. * **Support custom arguments toggle**: You can enable or disable custom visual argument rendering using the **Custom Arguments UI** setting. *** ## Pre-launch configurations Configure scripts and resources that execute before the main backend process boots. ### Terminal commands Add commands to prepare your execution environment: * Use this to activate virtual environments: `conda activate myenv` or `.\venv\Scripts\activate`. * Use this to set environment variables on Windows: `set CUDA_VISIBLE_DEVICES=0`. * Use this to set environment variables on Linux/macOS: `export CUDA_VISIBLE_DEVICES=0`. To add a command: 1. Click **Add Command** under the **Pre-Launch Commands** section. 2. Type the shell command. 3. The host executes these commands in sequence in the terminal tab before launching the module. ### Open files and folders Automatically open files or directories in native system applications when the card launches: * Select **Add File** or **Add Folder**. * Specify the absolute path (such as `D:\AI\outputs` or `D:\AI\config.json`). * When the card launches, LynxHub opens these folders or configuration files in your default explorer or editor. *** ## Custom run configurations Override the default startup scripts and configure process settings: * **Custom launch command**: Enable this to bypass default run commands. Enter your own command line execution string (e.g. `python launch.py --listen --theme dark`). * **Launch behavior**: Choose the viewport display layout when running the card: * **Split View (Default)**: Show terminal stream output on the left and the web browser on the right. * **Terminal Only**: Hides the browser and displays only the terminal tab. * **Browser Only**: Hides the terminal tab and opens the browser full-screen. * **None**: Launches the process invisibly in the background. * **Browser load delay**: Set the delay (in milliseconds) before the integrated browser starts loading the address. The default is `0ms`. * **Auto-close terminal on exit**: Enable this to close the terminal tab automatically when the backend process terminates. *** ## Address detection LynxHub reads the stdout stream from the terminal process to detect when the local web server is ready. Customize how LynxHub captures this address: * **Automatic detection**: Uses standard port patterns (such as `http://127.0.0.1:7860`) to redirect the browser. * **Custom port override**: Force the browser to open a specific address or port. * **Regex catch pattern**: Set a custom regular expression pattern to parse the console output and extract the local URL. # Quickstart Source: https://docs.lynxhub.app/application/quickstart Get up and running with the LynxHub desktop application. LynxHub consolidates your AI workflow into a single, unified workspace. You can configure, manage, and run local AI interfaces. ## Prerequisites Before you install LynxHub, make sure you have the required software on your system: * **Git:** Install the latest version of [Git](https://git-scm.com/downloads) to handle repository operations. * **PowerShell 7+ (Windows only):** Download and install [PowerShell 7+](https://github.com/PowerShell/PowerShell/releases/latest) for terminal operations. ## Get started Follow these steps to set up and run LynxHub. Get the latest release of LynxHub: * Download public releases from [GitHub Releases](https://github.com/KindaBrazy/LynxHub/releases/latest) or the official [Download Page](https://lynxhub.app/download). * Get insider or early access versions from the [Patreon Collection](https://www.patreon.com/collection/714004). Run the downloaded installer or launch the portable executable on your machine. If you are on macOS: 1. Right-click (or Control-click) the application. 2. Click **Open** from the menu. 3. Click **Open** in the security confirmation dialog. You only need to perform this bypass on the first launch. When the app starts, you see a catalog of preloaded AI tools on the dashboard. Use the search bar at the top or filter by categories to find the tool you want to install. Click on the tool's card to open the installation menu. Choose to either perform a fresh installation or link a pre-existing directory: * **To install fresh:** Click the clone option in the modal to begin download and setup. * **To locate an existing installation:** Click the **Locate** button at the bottom of the installation window and select your existing WebUI folder on your drive. Click the **3 dots menu** on your interface card and select **Launch Config**. Use the visual argument manager to enable desired flags. You can save presets here for quick switching. Click directly on the card itself to start the WebUI. LynxHub starts the backend process in a built-in terminal tab, and the built-in browser automatically loads the interface when ready. ## Explore features Once you are set up, explore more capabilities built into LynxHub: * **Git Control:** Select **Repo Config** from the **3 dots menu** to switch branches, stash changes, and reset repositories. * **Auto Updates & Updates:** Toggle auto-updates or manually check for new versions of WebUI modules directly from the card options. * **Terminal & Browser Workspace:** Switch tabs or spawn separate windows for terminal commands and web browsing. If you run into issues, join the community on the [LynxHub Discord](https://discord.gg/e8rBzhtcnK). # Troubleshooting Source: https://docs.lynxhub.app/application/troubleshooting Resolve common issues, rendering problems, and system errors in LynxHub. If you run into issues while using LynxHub, use this guide to resolve common problems. ## macOS security warning LynxHub is currently an unsigned macOS application. To bypass the security warning on your first launch: 1. Right-click (or Control-click) the application. 2. Click **Open** from the menu. 3. Click **Open** in the confirmation dialog. You only need to perform this bypass once. ## Prerequisites errors Ensure you have installed the required system tools to prevent app issues: * **Git missing:** Repository operations, branch switching, and cloning will fail. Download and install [Git](https://git-scm.com/downloads) and ensure it is in your system path. * **PowerShell 7+ missing:** Terminal processes may fail or behave unexpectedly on Windows. Download and install [PowerShell 7+](https://github.com/PowerShell/PowerShell/releases/latest). ## Startup loop or empty window If the application fails to start, gets stuck on the loading window, or opens as a solid black or white screen, a corrupted or incompatible plugin is likely causing the crash. To resolve this issue: 1. Navigate to the plugins folder for your operating system: * **Windows:** `%USERPROFILE%\Documents\LynxHub\Plugins` * **macOS:** `~/Documents/LynxHub/Plugins` * **Linux:** `~/Documents/LynxHub/Plugins` 2. Delete all directories and files inside this folder to uninstall all plugins. 3. Launch LynxHub again. 4. If the application launches successfully, you can reinstall your plugins one by one to identify the faulty plugin. 5. If the application still fails to start, submit a bug report on GitHub. ## Rendering and graphics issues If you experience user interface freezing, lagging, or black screens, disable hardware acceleration: 1. Click **Settings** in the application sidebar. 2. Click **Performance** to open the performance options. 3. Disable the **Use Hardware Acceleration** toggle. 4. Restart the application. ## Terminal spawning failures If the integrated terminal fails to spawn or crashes immediately on Windows: 1. Click **Settings** in the application sidebar. 2. Click **Terminal** to open the terminal options. 3. Change the **Use Conpty (Windows)** setting to **No**. 4. Restart the application. ## Factory reset If you experience persistent errors, you can reset your settings to defaults: 1. Click **Settings** in the application sidebar. 2. Click **Data Management** to open the data settings. 3. Scroll down to the **Clear** section. 4. Click **Reset Settings (Restart Required)**. 5. Click **Reset & Restart** in the confirmation dialog. Performing a settings reset wipes all custom settings, saved quick commands, hotkeys, and history. It does not delete your downloaded modules or extensions. ## GitHub issues If you cannot resolve your issue, report it on the GitHub repository: 1. Open the [GitHub Issues](https://github.com/KindaBrazy/LynxHub/issues) page. 2. Search the existing issues to see if your problem has already been reported. 3. If the issue is new, click **New Issue**. 4. Select the bug report template. 5. Complete the template by specifying: * Your operating system. * The LynxHub version. * Step-by-step instructions to reproduce the behavior. * **Log files:** Attach the log files (`main.log`) to help debug the issue. You can find the log directory at: * **Windows:** `%USERPROFILE%\AppData\Roaming\LynxHub\logs` * **macOS:** `~/Library/Logs/LynxHub` * **Linux:** `~/.config/LynxHub/logs` * Any screenshots or log outputs. 6. Click **Submit New Issue**. ## Discord support You can get real-time assistance and connect with the community on the [LynxHub Discord](https://discord.gg/e8rBzhtcnK). # API versions & changelog Source: https://docs.lynxhub.app/plugins/api-versions-3-5-6 Track the API versions for LynxHub modules and extensions at version 3.5.6. LynxHub uses API versioning to ensure compatibility between the host application and installed plugins (modules and extensions). *** ## Current API versions For the LynxHub client version `3.5.6`, the active API versions are: | Plugin type | Current API version | Target config field | | :------------- | :------------------ | :-------------------- | | **Modules** | `2.1.0` | `moduleApiVersion` | | **Extensions** | `2.2.0` | `extensionApiVersion` | *** ## Compatibility model Every plugin must specify its required API version inside its `metadata.json` configuration file: ```json theme={null} { "name": "my-plugin", "id": "my-plugin-id", "version": "1.0.0", "moduleApiVersion": "2.1.0" } ``` When loading plugins at startup, LynxHub performs the following compatibility checks: 1. **Exact match or minor version updates**: The host loads the plugin if its requested API version is equal to or lower than the host's supported version (within the same major version). 2. **Major version mismatch**: If the plugin requires a higher major API version than the host supports, the host disables the plugin and notifies the user with a compatibility warning. 3. **Missing version fields**: If the plugin configuration does not specify an API version, it defaults to legacy compatibility mode and may run with limited API features. *** ## Configuring semver ranges When publishing a plugin, you must declare compatible API ranges in your plugin's `versioning.json` registry file under the `engines` field. The host application checks this range to ensure compatibility. ### Easy semver guide Use these simple formats to control which host versions can run your plugin: * **Caret `^` (Recommended)**: Matches the major version. Allows safe updates. * *Example*: `"^2.0.0"` works with `2.0.0`, `2.1.0`, and all `2.x.x` versions. It will **not** work with `3.0.0`. * **Tilde `~` (Strict)**: Matches the major and minor version. Only allows patch fixes. * *Example*: `"~2.1.0"` works with `2.1.0` and `2.1.1`. It will **not** work with `2.2.0`. * **Exact version (Strict)**: Requires the exact version number. * *Example*: `"2.1.0"` only works with `2.1.0`. ### Example configuration Here is an example `versioning.json` snippet showcasing how to specify compatibility for a module and an extension: #### For a module: ```json theme={null} { "versions": [ { "version": "1.2.0", "commit": "abcdef1234567890abcdef1234567890abcdef12", "engines": { "moduleApi": "^2.1.0" }, "stage": "public", "platforms": ["win32", "linux", "darwin"] } ] } ``` #### For an extension: ```json theme={null} { "versions": [ { "version": "1.0.0", "commit": "1234567890abcdef1234567890abcdef12345678", "engines": { "extensionApi": "^2.0.0" }, "stage": "public", "platforms": ["win32"] } ] } ``` *** ## API changelogs The following changelogs detail API additions, modifications, and deprecations for each release. ### Version 3.5.6 #### Extensions (API 2.2.0) * Bumped `extensionApiVersion` to `2.2.0`. * Added support for **hidden pages** in `ExtensionPage` configuration by adding `position: 'hidden'` and making `icon` optional. * Introduced the `tabs` namespace to `ExtensionRendererApi` with the `setActivePage` method to programmatically set the active page for the current tab. * Added the `registerToolsCard` method to the `cards` namespace of `ExtensionRendererApi`, enabling extensions to register custom React components directly as tools cards. * Added support for triggering native toast notifications directly via the extension API. * Added a file IPC method to select multiple files via native system dialogs. * Updated card headers to support custom ReactNode icons or icon URL paths. * Added a build post-script (`fixExtension.js`) to clean up redundant files and folders after extension builds. #### Plugins system * Separated extensions and modules configurations into dedicated storage files. * Centralized development folder path resolution. *** ### Version 3.5.0 This major release introduced the V3 UI redesign, a unified IPC system, and major API version bumps. #### Modules (API 2.1.0) * Bumped `moduleApiVersion` to `2.1.0`. * Added the `supportCustomArguments` configuration flag to let modules explicitly enable or disable custom argument UI rendering. * Integrated **Alert** UI feedback support across most module execution steps. #### Extensions (API 2.0.0) * Bumped `extensionApiVersion` to `2.0.0`. * Enabled extensions to dynamically inject positional cards directly into the **Tools** and **Games** pages. * Enabled extensions to dynamically intercept and listen to any IPC event (running callbacks before and after calls) across both main and renderer processes. * Replaced the legacy IPC implementation with a unified IPC handler system, passed to both main and renderer entrypoints. * Added API support to trigger modals directly from card dropdown menus. # Extension API reference Source: https://docs.lynxhub.app/plugins/extensions/api-references Complete API reference guide for frontend, backend, and shared IPC namespaces. This page provides the full API specifications for building LynxHub extensions. *** ## Extension entrypoints An extension must define separate entry points for the backend (Electron Main) and frontend (Electron Renderer) processes. ### Backend entrypoint Your backend entrypoint file must build to `scripts/main/mainEntry.cjs` as a CommonJS module and export `initialExtension`: ```typescript theme={null} export async function initialExtension(lynxApi: ExtensionMainApi, utils: MainExtensionUtils, mainIpc: MainIpcApi): Promise; ``` ### Frontend entrypoint Your frontend entrypoint file must build to `scripts/renderer/rendererEntry.mjs` as an ES module and export `InitialExtensions`: ```typescript theme={null} export function InitialExtensions(lynxAPI: ExtensionRendererApi, rendererIpc: RendererIpcApi, extensionId: string): void; ``` *** ## Backend API reference The backend process handles Node.js integrations, local execution, and database persistence. ### ExtensionMainApi Use `lynxApi` to listen to system events, hook into IPC cycles, or insert system tray menu items. #### `listenForChannels(callback: () => void): void` Registers a callback to execute custom IPC channel listeners (`ipcMain.on` / `ipcMain.handle`). #### `onAppReady(callback: () => Promise): void` Registers an asynchronous callback executing when Electron is fully booted (`app.whenReady()`). #### `onReadyToShow(callback: () => void): void` Registers a callback executing when the main BrowserWindow is ready to show. #### `trayMenu_AddItem(callback: () => { item: MenuItemConstructorOptions; index: number }): void` Appends a menu item to the host's system tray dropdown. #### `ipcEvents` Accesses the main process IPC hook registry to intercept channel transactions. #### `initNodeSentry(dsn: string): Scope` Initializes Sentry error tracking for the backend Node process. Returns the active Sentry scope. *** ### MainExtensionUtils Use `utils` to resolve class singletons or spawn native pseudo-terminals. #### `getStorageManager(): Promise` Resolves to the application storage controller (see [StorageManager](#storagemanager) below). #### `getAppManager(): Promise` Resolves to the window manager controller (see [MainWindowManager](#mainwindowmanager) below). #### `getModuleManager(): Promise` Resolves to the card modules controller (see [ModuleManager](#modulemanager) below). #### `nodePty` Exposes the native `node-pty` module. Use this to spawn pseudo-terminal processes. *** ### StorageManager The persistent storage controller exposes methods to query settings or save custom settings. #### `getCustomData(id: string): any` Queries custom data values. Keys beginning with `moduleName::` (e.g. `python::envPath`) automatically redirect reads to the dedicated configuration file: `moduleName.config`. #### `setCustomData(id: string, data: any): void` Saves custom data values. Scoped keys using `::` are persisted to their respective config files. #### `getData(key: K): AppStorageData[K]` Queries core application configuration fields (e.g. `app`, `terminal`, `browser`, `performance`, `plugin`, `cards`, `cardsConfig`). #### `updateData(key: K, updateData: Partial): void` Updates core configuration fields partially. #### `write(): void` Commits current storage cache to disk. #### `decryptBrowserData(): void` Decrypts stored browser addresses and caches them in memory. #### `getBrowserDataSecurely(): BrowserHistoryData` Retrieves decrypted browser history, addresses, favorites, and favicons from the memory cache. #### `updateBrowserDataSecurely(data: Partial): void` Updates decrypted cache values. The manager automatically encrypts values on disk. #### `addInstalledCard(card: InstalledCard): void` Adds a card definition to the local card registry. #### `removeInstalledCard(id: string): void` Removes a card definition from the card registry. #### `addPinnedCard(cardId: string): void` Pins a card on the home page dashboard. #### `removePinnedCard(cardId: string): void` Unpins a card from the home page. #### `updateRecentlyUsedCards(id: string): void` Moves a card ID to the front of the recently used list. #### `setCardTerminalPreCommands(id: string, commands: string[]): void` Saves pre-commands executed in the terminal prior to card startups. #### `unassignCard(id: string, clearConfigs: boolean): void` Removes a card registry and optionally purges its associated configs. *** ### MainWindowManager Controls the main application window lifecycle. #### `getMainWindow(): BrowserWindow | undefined` Retrieves the main Electron `BrowserWindow` instance. #### `getWebContent(): WebContents | undefined` Retrieves the `WebContents` instance of the main window. #### `sendMessage(channel: string, ...args: any[]): void` Dispatches an IPC message directly to the renderer process. #### `restart(): void` Relaunches the host application. *** ### ModuleManager Controls the lifecycle of card modules. #### `getMethodsById(id: string): any` Queries public methods exported by a module. #### `uninstallCardByID(id: string): Promise` Triggers a module's custom uninstallation routine. #### `checkCardUpdate(card: InstalledCard, type: 'git' | 'stepper' | undefined): Promise` Checks if a card has updates available on its remote repository. *** ## Frontend API reference The frontend process handles page rendering, styles, and UI events. ### ExtensionRendererApi Use `lynxAPI` to inject custom components, manage client routes, or dispatch actions. #### `titleBar` * `addStart(component: FC): void` - Injects component to start of title bar. * `addCenter(component: FC): void` - Injects component to center of title bar. * `addEnd(component: FC): void` - Injects component to end of title bar. * `replaceCenter(component: FC): void` - Replaces center elements. * `replaceEnd(component: FC): void` - Replaces end elements. #### `statusBar` * `addStart(component: FC): void` - Injects component to start of status bar. * `addCenter(component: FC): void` - Injects component to center of status bar. * `addEnd(component: FC): void` - Injects component to end of status bar. * `replaceContainer(component: FC): void` - Replaces status bar container. #### `runningAI` * `container(component: FC): void` - Replaces entire running viewport. * `terminal(component: FC): void` - Replaces terminal panel. * `browser(component: FC): void` - Replaces browser panel. #### `router` * `add(routeObject: RouteObject[]): void` - Appends routes to the router. * `addPage(page: ExtensionPage): void` - Registers a custom page component to the router and sidebar navigation. * `replace` - Replaces default page views (`homePage`, `imageGenerationPage`, `textGenerationPage`, `audioGenerationPage`, `toolsPage`, `gamesPage`, `dashboardPage`, `modulesPage`, `extensionsPage`, `settingsPage`). ##### `ExtensionPage` properties: * `id` (string): Unique identifier. * `title` (string): Title of page. * `icon` (ReactNode, optional): Page icon elements. * `component` (FC): The page React component. * `position` (`'top' | 'bottom' | 'hidden'`, optional): Sidebar layout position. #### `tabs` * `setActivePage(pageID: string, title?: string, isTerminal?: boolean): void` - Sets the active page for the current tab. #### `navBar` * `replace.container(component: FC): void` - Replaces entire sidebar. * `replace.contentBar(component: FC): void` - Replaces upper menu list. * `replace.settingsBar(component: FC): void` - Replaces lower menu list. * `addButton.contentBar(component: FC): void` - Appends link to content menu. * `addButton.settingsBar(component: FC): void` - Appends link to settings menu. #### `addModal(component: FC): void` Appends a custom modal component to the overlay tree. #### `replaceModals` Replaces built-in host modals (`updateApp`, `launchConfig`, `cardExtensions`, `updatingNotification`, `cardInfo`, `installUi`, `uninstallCard`, `unassignCard`, `warning`, `cardReadme`, `gitManager`). #### `replaceMarkdownViewer(component: FC<{repoPath: string; rounded?: boolean}>): void` Replaces the default Markdown renderer. #### `addCustomHook(hook: FC): void` Mounts a React hook component at the tree root. #### `replaceBackground(component: FC): void` Replaces the window background component. #### `customizePages` * `home.replace.searchAndFilter(component: FC): void` * `home.replace.searchResult(component: FC<{searchValue: string}>): void` * `home.replace.categories(component: FC): void` * `home.add.top(component: FC): void` / `bottom` / `scrollTop` / `scrollBottom` * `home.add.pinCategory(component: FC): void` / `recentlyCategory` * `home.add.allCategory(component: FC): void` * `audio` | `image` | `text` | `agents` | `others` | `tools` | `games` - Sub-pages: * `add.top(component: FC): void` / `bottom` / `scrollTop` / `scrollBottom` / `cardsContainer` * `settings.add.navButton(component: FC): void` / `content` * `dashboard.add.navButton(component: FC): void` / `content` #### `addReducer(reducers: {name: string; reducer: Reducer}[]): void` Registers Redux slices to the central application store. #### `cards` * `replace(component: FC<{cards: LoadedCardData[]}>): void` - Replaces card grid. * `replaceComponent(component: FC): void` - Replaces single card layout. * `customize.header(component: FC): void` * `customize.body(component: FC): void` * `customize.footer(component: FC): void` * `customize.menu.replace(component: FC): void` * `customize.menu.addSection(sections: {index: number; components: FC[]}[]): void` * `customize.menu.addModal(modals: {key: string; component: FC}[]): void` * `registerToolsCard(card: ToolsCardConfig): void` - Registers a custom card directly inside the Tools page. ##### `ToolsCardConfig` properties: * `id` (string): Unique identifier. * `title` (string): Title of card. * `description` (string): Subtitle / description of card. * `icon` (string | ReactNode, optional): Custom icon element. * `onPress` (() => void, optional): Interactive click callback. * `component` (ComponentType, optional): The custom React component representing the card. * `where` (string, optional): Target category routing slot (e.g., `"tools_page"`). #### `setCards_TerminalPreCommands(id: string, preCommands: string[]): void` Defines commands running before launching terminal processes. #### `events` * `on(event: string, callback: (payload: any) => void): () => void` * `off(event: string, callback: (payload: any) => void): void` * `emit(event: string, payload: any): void` * `getListenerCount(event: string): number` #### `ipcEvents` Allows intercepting or hooking into all renderer IPC events. #### `modulesData` * `allModules: CardModules` * `allCards: CardData[]` * `useGetArgumentsByID(id: string): ArgumentsData | undefined` * `useGetCardsByPath(path: string): LoadedCardData[] | undefined` * `getCardMethod(cards: CardData[], id: string, method: string): Function | undefined` #### `toast` * `top(message: string, options?: any): void` * `bottom(message: string, options?: any): void` *** ## Shared IPC APIs Both process entry points receive IPC wrappers (`MainIpcApi` and `RendererIpcApi`) providing typed namespaces for communication. ### `files` (filesIpc) Handles native filesystem operations. * `openDlg(option: OpenDialogOptions): Promise` - Show file dialog. * `openDlgMany(option: OpenDialogOptions): Promise` - Show file dialog for multiple selections. * `openPath(dir: string): void` - Open path in system file explorer. * `saveToFile(content: string): Promise` - Show save dialog and save text. * `getAppDirectories(name: FolderNames): Promise` - Retrieve app folder paths. * `removeDir(dir: string): Promise` - Permanently delete directory. * `trashDir(dir: string): Promise` - Move directory to trash. * `listDir(dirPath: string, relatives: string[]): Promise` - List directory items. * `checkFilesExist(dir: string, fileNames: string[]): Promise` - Confirm file existence. * `calcFolderSize(dir: string): Promise` - Query folder size in bytes. * `getRelativePath(base: string, target: string): Promise` - Calculate relative path. * `getAbsolutePath(base: string, target: string): Promise` - Calculate absolute path. * `isEmptyDir(dir: string): Promise` - Check if folder is empty. * `isAbsolute(dir: string): Promise` - Check if path is absolute. *** ### `git` (gitIpc) Handles repository management operations. * `cloneShallow(options: ShallowCloneOptions): void` - Shallow clones repository. * `cloneShallowPromise(options: ShallowCloneOptions): Promise` - Clones repository asynchronously. * `getRepoInfo(dir: string): Promise` - Queries branch and remote URL. * `changeBranch(dir: string, branch: string): Promise` - Switch local branch. * `unShallow(dir: string): Promise` - Fetch full repository history. * `resetHard(dir: string): Promise` - Hard reset local changes. * `getCommits(dir: string, max?: number): Promise` - Retrieve git commit logs. * `validateGitDir(dir: string, url: string): Promise` - Confirm directory is valid repository matching URL. * `onProgress(callback: GitProgressCallback): () => void` - Listen for clone/pull progress. * `pull(repoDir: string, id: string): void` - Pull changes. * `stashDrop(dir: string): Promise` - Drop stash records. *** ### `pty` (ptyIpc) Handles pseudo-terminal process spawning. * `process(id: string, cardId: string): void` - Launch card shell process. * `customProcess(id: string, dir?: string, file?: string): void` - Spawn file executable in PTY. * `emptyProcess(id: string, dir?: string): void` - Spawn empty shell process. * `customCommands(id: string, commands?: string | string[], dir?: string): void` - Execute custom inputs. * `stop(id: string): void` - Kill terminal process. * `write(id: string, data: string): void` - Write string to stdin. * `clear(id: string): void` - Clear PTY buffer screen. * `resize(id: string, cols: number, rows: number): void` - Change PTY size. * `onData(callback: (id: string, data: string) => void): () => void` - Listen to stdout. * `onTitle(callback: (id: string, title: string) => void): () => void` - Listen to title changes. * `onExit(callback: (id: string) => void): () => void` - Listen to PTY process termination. *** ### `storage` (storageIpc) Direct database storage operations. * `getCustom(key: string): Promise` - Get custom settings value. * `setCustom(key: string, data: T): void` - Set custom settings value. * `get(key: K): Promise` - Get typed database field. * `getAll(): Promise` - Get full settings database. * `update(key: K, data: Partial): Promise` - Partally update database. * `clear(): Promise` - Full factory reset database. *** ### `utils` (utilsIpc) Exposes general helpers and image cache managers. * `updateAllExtensions(data: ExtensionsUpdateRequest): void` - Sequentially check/pull extensions. * `onUpdateAllExtensions(callback: (info: OnUpdatingExtensions) => void): () => void` - Listen to extension updates progress. * `getExtensionsDetails(dir: string): Promise` - List details of extensions in folder. * `getExtensionsUpdateStatus(dir: string): Promise` - Query updates status. * `disableExtension(disable: boolean, dir: string): Promise` - Toggle extension folder (prefixes folder with `.`). * `cancelExtensionsData(): void` - Stop querying extension summaries. * `downloadFile(url: string): void` - Trigger file download. * `cancelDownload(): void` - Terminate download process. * `onDownloadFile(callback: (prog: DownloadProgress) => void): () => void` - Listen to download status. * `decompressFile(filePath: string): Promise` - Extract archive to path. * `isResponseValid(url: string): Promise` - Confirm URL responds with status 200. * `getImageAsDataURL(url: string): Promise` - Fetch image and convert to data URI. * `getImageCacheStats(): Promise` - Retrieve size of image cache folders. * `clearImageCache(): Promise` - Purge cache. * `triggerImageCacheCleanup(): Promise` - Remove expired cache entries. # Extension architecture Source: https://docs.lynxhub.app/plugins/extensions/architecture Understand the architecture and runtime environment of LynxHub extensions. LynxHub extensions are dynamic plugins designed to integrate with the core application. They allow you to customize the shell UI, add routes, inject Redux reducers, listen to application events, and add system tray menu items. *** ## Process separation LynxHub operates on a multi-process architecture based on Electron. Your extension runs code in two separate processes: ```mermaid theme={null} graph TD subgraph Electron Main Process (Node.js) HostMain[Host Main Process] <-->|IPC Events| ExtensionMain["Extension Main (TypeScript Source)"] ExtensionMain -->|Compiled / Bundled| CjsBundle["scripts/main/mainEntry.cjs"] end subgraph Electron Renderer Process (Chromium) HostRenderer[Host React Shell] <-->|Module Federation| ExtensionRenderer["Extension Renderer (TypeScript Source)"] ExtensionRenderer -->|Compiled / Bundled| MjsBundle["scripts/renderer/rendererEntry.mjs"] end ``` ### Main process The main process runs in a Node.js environment. It has full access to operating system APIs. * **Entrypoint**: `src/main/lynxExtension.ts` (compiled and bundled to `scripts/main/mainEntry.cjs` as a CommonJS module). * **Responsibility**: Listens to system-level lifecycles, registers global IPC channels, adds tray menu items, and launches background processes (e.g., via `node-pty`). * **Signature**: ```typescript theme={null} import {ExtensionMainApi, MainExtensionUtils, MainIpcApi} from '@lynx_main/plugins/extensions/types'; export async function initialExtension(lynxApi: ExtensionMainApi, utils: MainExtensionUtils, mainIpc: MainIpcApi): Promise { // Register main process listeners and lifecycles } ``` ### Renderer process The renderer process displays the graphical interface and runs in a Chromium browser window. * **Entrypoint**: `src/renderer/Extension.tsx` (compiled and bundled to `scripts/renderer/rendererEntry.mjs` as an ES module). * **Responsibility**: Injects React UI components into predefined slots (Title Bar, Status Bar, Settings, Sidebar), adds custom pages/routes, and hooks into state management. * **Signature**: ```typescript theme={null} import {ExtensionRendererApi} from '@lynx/plugins/extensions/types/api'; import {RendererIpcApi} from '@lynx/plugins/extensions/types/ipcWrapper'; export function InitialExtensions(lynxAPI: ExtensionRendererApi, rendererIpc: RendererIpcApi, extensionId: string): void { // Inject React components, custom Redux reducers, or event listeners } ``` *** ## Boot and loading sequence LynxHub follows a structured sequence when loading extensions during application startup: ```mermaid theme={null} sequenceDiagram participant App as App Lifecycle participant EM as ExtensionManager participant EA as ExtensionApi participant Ext as Extension Code App->>EM: Initialize plugin managers Note over EM: Scan and validate plugin folders EM->>Ext: Import extension main module EM->>Ext: Invoke initialExtension(lynxApi, utils, mainIpc) Note over Ext: Register lifecycle callbacks App->>App: Complete app boot App->>EM: app.whenReady() fires EM->>EA: Trigger onAppReady() callbacks EA->>Ext: Execute registered onAppReady callbacks App->>EM: Register IPC listeners EM->>EA: Trigger listenForChannels() callbacks EA->>Ext: Execute registered listenForChannels callbacks App->>App: Show main window App->>EM: window ready-to-show fires EM->>EA: Trigger onReadyToShow() callbacks EA->>Ext: Execute registered onReadyToShow callbacks ``` ### 1. Discovery and validation During startup, the host scans the app `Plugins/` folder. It checks for a valid subdirectory containing `metadata.json` and the required entrypoint files. ### 2. Main process initialization The host imports the backend entrypoint of the extension. It calls the exported `initialExtension` function and passes the API wrappers. At this point, the extension registers callbacks for subsequent lifecycle events. ### 3. Application ready hook When Electron completes its initialization, the host invokes all registered `onAppReady` callbacks. ### 4. IPC channel setup The host registers core IPC handlers. It then triggers `listenForChannels` callbacks. This allows your extension to register custom IPC event handlers. ### 5. Main window display When the main application window is ready to be shown, the host triggers `onReadyToShow` callbacks. *** ## Module Federation in the renderer LynxHub uses **Module Federation** via `@originjs/vite-plugin-federation` to dynamically mount extension interfaces in production. This approach avoids rebuilding the host application. ```mermaid theme={null} sequenceDiagram participant Host as Host Renderer participant IPC as Main Process IPC participant ExtServer as Local Extension Server Host->>IPC: Get active plugin addresses IPC-->>Host: Return addresses (e.g., http://localhost:5001) Host->>Host: Register remote via __federation_method_setRemote() Host->>Host: Import Extension via __federation_method_getRemote() Host->>ExtServer: Fetch scripts/renderer/rendererEntry.mjs ExtServer-->>Host: Serve extension bundle Host->>Host: Run InitialExtensions() ``` ### Runtime loading flow 1. **Discovery**: The host requests the active plugin addresses from the main process over IPC. 2. **Registration**: For each active extension, the host registers its remote entry URL (`
/scripts/renderer/rendererEntry.mjs`) using the Virtual Module Federation API: ```javascript theme={null} import {__federation_method_setRemote} from '__federation__'; __federation_method_setRemote(extensionId, {format: 'esm', from: 'vite', url}); ``` 3. **Mounting**: The host dynamically loads the entry component using `__federation_method_getRemote(extensionId, 'Extension')` and calls the `InitialExtensions` initializer. ### Shared packages To prevent loading duplicate library instances and to ensure shared context is maintained, the host shares core packages with all extensions: * `react` and `react-dom` * `react-redux` * `@heroui/react` (HeroUI v3 React components) * `@heroui/styles` (HeroUI v3 styling tokens) * `react-aria` (React accessibility hooks) Any package listed above loads from the host shell. Your extension must treat these as peer dependencies. You must configure them as shared in your Vite configuration. *** ## Vite build configuration To build your extension correctly for LynxHub, configure `@originjs/vite-plugin-federation` and `electron-vite` in your `electron.vite.config.ts` configuration. Below is the standard configuration structure used by the template: ```typescript theme={null} import federation from '@originjs/vite-plugin-federation'; import tailwindcss from '@tailwindcss/vite'; import react from '@vitejs/plugin-react'; import {defineConfig} from 'electron-vite'; import {resolve} from 'path'; export default defineConfig({ main: { root: resolve('extension/src/main'), build: { externalizeDeps: {exclude: ['tree-kill']}, outDir: resolve('extension_out/main'), rolldownOptions: { input: resolve('extension/src/main/lynxExtension.ts'), output: {entryFileNames: 'mainEntry.cjs', format: 'cjs'}, }, }, resolve: { alias: { '@lynx_common': resolve(__dirname, '..', 'src/common'), '@lynx_main': resolve(__dirname, '..', 'src/main'), }, }, }, renderer: { root: resolve('extension/src/renderer'), plugins: [ react(), tailwindcss(), federation({ name: 'extension', filename: 'rendererEntry.mjs', exposes: { Extension: resolve('extension/src/renderer/Extension.tsx'), }, shared: { react: {generate: false}, 'react-dom': {generate: false}, 'react-redux': {generate: false}, '@heroui/react': {generate: false}, '@heroui/styles': {generate: false}, 'react-aria': {generate: false}, }, }), ], resolve: { alias: { '@lynx_module': resolve(__dirname, '..', 'module/src'), '@lynx_extension': resolve(__dirname, '..', 'extension/src'), '@lynx_common': resolve(__dirname, '..', 'src/common'), '@lynx': resolve(__dirname, '..', 'src/renderer/mainWindow'), '@lynx_shared': resolve(__dirname, '..', 'src/renderer/shared'), '@lynx_assets': resolve(__dirname, '..', 'src/renderer/shared/assets'), }, }, build: { outDir: resolve('extension_out/renderer'), rolldownOptions: { input: resolve('extension/src/renderer/index.html'), treeshake: {moduleSideEffects: false}, }, assetsDir: '', minify: false, target: 'esnext', cssCodeSplit: false, modulePreload: false, }, publicDir: resolve(__dirname, 'extension/src/renderer/Public'), }, }); ``` *** ## Development vs. production environments LynxHub provides two separate loading strategies to optimize the developer experience: | Feature | Development mode | Production mode | | :------------------------ | :------------------------------------------------ | :---------------------------------------------------- | | **Main entry import** | `extension/src/main/lynxExtension.ts` | `scripts/main/mainEntry.cjs` (CommonJS module) | | **Renderer entry import** | `@lynx_extension/renderer/Extension` (Vite alias) | `scripts/renderer/rendererEntry.mjs` (dynamic remote) | | **Loading mechanism** | Hot-imported from local source folder | Served by local servers via Module Federation | | **Updates** | Hot Module Replacement (HMR) instantly | Requires a build and app restart | | **Location** | Root `/extension` directory of the application | `/Plugins//` directory | ### Development mode To bypass Module Federation and speed up updates, the host process attempts to hot-import the extension directly from your local source directory using the Vite alias: * **Renderer**: `@lynx_extension/renderer/Extension` (mapped to `/extension/src/renderer`) * **Main**: `extension/src/main/lynxExtension` This allows you to benefit from Hot Module Replacement (HMR). You can see UI changes instantly without building or deploying. ### Production mode In production, LynxHub launches local servers for installed extensions. It loads their entry files from disk: * **Validation**: LynxHub verifies the directory structure under `/Plugins//`. It ensures `scripts/main/mainEntry.cjs` and `scripts/renderer/rendererEntry.mjs` exist. * **Protocol**: Static assets and entrypoints are served locally and registered as Module Federation remotes. # Building extensions Source: https://docs.lynxhub.app/plugins/extensions/build Compile, bundle, and install extensions for distribution. You must compile and package your extension to test it in a production environment or prepare it for publication. ## Compilation To build your extension, run the build script in the LynxHub root directory: ```bash theme={null} npm run build:extension ``` This command performs the following build steps: 1. Clears any previous builds by deleting the `extension_out/` directory. 2. Compiles your extension using the config file `extension/electron.vite.config.ts`. 3. Optimizes and bundles both your backend and frontend code. 4. Outputs the compiled assets into the `extension_out/` directory. ### Build output structure The build process generates two main directories inside `extension_out/`: * `main/` - Contains the compiled backend main-process bundle (`mainEntry.cjs`). * `renderer/` - Contains the compiled frontend assets, including the remote entry file (`rendererEntry.mjs`). *** ## Local installation and testing To test the production build of your extension locally, you must copy the compiled files to the LynxHub user plugins folder. ### 1. Locate the plugins directory The default application data directory is determined by your installation type: * **Installed**: `C:\Users\\Documents\LynxHub` (on Windows) * **Portable**: `./LynxHub_Data` (located in your application executable folder) Navigate to the `Plugins/` folder inside this directory. ### 2. Create the target folder Create a new subfolder named after your extension metadata ID. For example, if your extension ID is `custom-actions`, create a directory path like: ``` Documents/LynxHub/Plugins/custom-actions/ ``` ### 3. Copy the compiled files Create a `scripts/` folder inside your extension directory. Copy the compiled outputs from `extension_out/` as follows: 1. Copy the file `extension_out/main/mainEntry.cjs` to `Plugins//scripts/main/mainEntry.cjs`. 2. Copy all files from `extension_out/renderer/` to the folder `Plugins//scripts/renderer/`. 3. Copy your extension's static `metadata.json` file to the root of the extension folder (`Plugins//metadata.json`). ### 4. Verify directory structure Ensure your extension directory matches the following layout: ``` Documents/LynxHub/Plugins// ├── metadata.json └── scripts/ ├── main/ │ └── mainEntry.cjs └── renderer/ ├── rendererEntry.mjs └── [other bundled assets] ``` Restart LynxHub to load and verify the compiled extension. *** ## Production builds When you prepare your extension for publication, you must push the compiled build assets to the compiled branch (`main`). 1. Commit all raw source files to your `source` branch. 2. Run the compilation command `npm run build:extension`. 3. Switch to your `main` branch. 4. Copy the compiled `scripts/` directory to the root of the `main` branch. 5. Push the compiled branch to your remote repository. For more details on repository branches, see the [Publishing extensions](/plugins/extensions/publish) guide. # Extension configuration Source: https://docs.lynxhub.app/plugins/extensions/configuration Configure package settings and metadata for extensions. To publish and distribute your extension, you must place two registry files and an icon at the root of your repository's `metadata` branch. These files are `metadata.json`, `versioning.json`, and `icon.png`. *** ## metadata.json The `metadata.json` file contains the primary identity and descriptive information for your extension. ### Structure The file must follow this structure: * `id` (string): The unique identifier of your extension. It must use lowercase alphanumeric characters and hyphens. * `title` (string): The display name of your extension shown in the LynxHub registry. * `description` (string): A short sentence describing what your extension does. * `type` (string): Must be set to `"extension"`. ### Example Here is an example `metadata.json` file: ```json theme={null} { "$schema": "https://raw.githubusercontent.com/KindaBrazy/LynxHub-Statics/refs/heads/schema/plugins/metadata.schema.json", "id": "python-toolkit", "title": "Python Toolkit", "description": "Effortlessly manage your Python environments and dependencies.", "type": "extension" } ``` *** ## versioning.json The `versioning.json` file tracks your release history, compatible engine ranges, supported operating systems, and version changelogs. LynxHub reads this file to resolve target versions and download updates. ### Structure The file consists of two root arrays: #### versions An array of release version objects. Each version object contains: * `version` (string): The semantic version of the release (e.g., `"1.0.0"`). * `commit` (string): The exact Git commit hash of the release on your `main` branch. * `engines` (object): * `extensionApi` (string): The semver compatibility range of the LynxHub extension API (e.g., `"^2.0.0"`). * `stage` (string): The release distribution channel. Use `"public"`, `"early_access"`, or `"insider"`. * `platforms` (array): The target operating systems. Supported values are `"win32"`, `"linux"`, and `"darwin"`. #### changes An array of change logs for each release version. Each changelog object contains: * `version` (string): The semantic version this changelog applies to. * `date` (string): The release date formatted as `YYYY-MM-DD`. * `items` (array): A list of category objects containing change descriptions. Typical categories are `🚀 New Features`, `🛠️ Improvements`, and `🐛 Bug Fixes`. ### Example Here is an example `versioning.json` file: ```json theme={null} { "versions": [ { "version": "1.0.0", "commit": "abcdef1234567890abcdef1234567890abcdef12", "engines": { "extensionApi": "^2.0.0" }, "stage": "public", "platforms": ["win32", "linux", "darwin"] } ], "changes": [ { "version": "1.0.0", "date": "2026-06-19", "items": [ { "🚀 New Features": ["Initial release of the extension."] } ] } ] } ``` *** ## icon.png The `icon.png` file is the display icon for your extension in the LynxHub registry. ### Requirements * **Filename**: Must be named exactly `icon.png`. * **Location**: Place it at the root of your `metadata` branch. * **Format**: PNG format. * **Dimensions**: Use a square image (e.g., 512x512 pixels). # Extension Examples Source: https://docs.lynxhub.app/plugins/extensions/examples Explore example extensions and starter templates. Explore these real-world extension examples. You can inspect their codebases, clone them as templates, or install them in LynxHub. Manage Python installations, virtual environments, and package requirements inside your workspace. Display real-time CPU, GPU, and memory usage statistics in the LynxHub status bar. Create and run custom action cards to automate scripts, launch apps, and open websites. ## Python toolkit The [Python Toolkit](https://github.com/KindaBrazy/LynxHub-Python-Toolkit) extension manages Python environments and dependencies for AI modules. ### Key features * It automatically detects system Python installations, including Conda. * You can install new Python versions directly from the user interface. * You can create virtual environments and associate them with modules. * It manages package installations and validates requirements. *** ## Hardware monitor The [Hardware Monitor](https://github.com/KindaBrazy/LynxHub-Hardware-Monitor) extension provides real-time system resource tracking. ### Key features * It tracks CPU, GPU, and memory usage. * It displays system metrics directly in the status bar. * It uses a custom .NET 10.0 runtime helper to read hardware sensors. *** ## Custom actions The [Custom Actions](https://github.com/KindaBrazy/LynxHub-Custom-Actions) extension enables custom dashboard automations. ### Key features * You can design visual cards with custom titles, descriptions, and icons. * You can trigger shell scripts, run executables, or open links with a single click. * You can build custom workflows to control your applications. # IPC routing and storage manager Source: https://docs.lynxhub.app/plugins/extensions/main/ipc-storage Operate the host database and access core IPC routing interfaces. Extensions can access persistent databases and routing mechanisms. Use the `StorageManager` to query state, or the `mainIpc` parameter to invoke core app functions. *** ## Persistent storage manager The `StorageManager` singleton controls database get/set operations, migrations, and encryption. Retrieve the storage manager during initialization: ```typescript theme={null} const storageManager = await utils.getStorageManager(); ``` *** ### Custom data scopes If your extension requires custom persistent key-value configuration data, use `getCustomData` and `setCustomData`. If your key begins with a module identifier followed by double colons (e.g. `myModule::myKey`), the storage manager isolates this data in a dedicated configuration file: `myModule.config`. ```typescript theme={null} // Retrieve custom values const customVal = storageManager.getCustomData('myModule::api-key'); // Save and persist custom values storageManager.setCustomData('myModule::api-key', 'secure-token'); ``` *** ### Core database configuration You can read or update the host application's core settings (e.g., `app`, `terminal`, `browser`, `performance`, `plugin`, `cards`, `cardsConfig`): ```typescript theme={null} // Read settings const appSettings = storageManager.getData('app'); console.log('Is Dynamic Title Enabled:', appSettings.dynamicAppTitle); // Update settings partially storageManager.updateData('app', {dynamicAppTitle: false}); // Persist current settings state to disk storageManager.write(); ``` *** ### Encrypted browser history The application encrypts browser history entries on disk using Chromium's `safeStorage` API. You must decrypt browser records before reading them: ```typescript theme={null} // Decrypt and load data into memory cache storageManager.decryptBrowserData(); // Retrieve decrypted records cache const history = storageManager.getBrowserDataSecurely(); console.log('Recent URL addresses:', history.recentAddress); // Save updated records (the manager automatically encrypts values on disk) storageManager.updateBrowserDataSecurely({ recentAddress: [...history.recentAddress, 'https://lynxhub.app'], }); ``` *** ### Card lifecycle management Use the storage manager to update card registries, pinned lists, or terminal settings: ```typescript theme={null} // Add card definitions to the registry storageManager.addInstalledCard({id: 'my-card-id', dir: 'project-path'}); // Pin or unpin cards storageManager.addPinnedCard('my-card-id'); storageManager.removePinnedCard('my-card-id'); // Clear card configurations and unassign card storageManager.unassignCard('my-card-id', true); // Set terminal commands running prior to card executions storageManager.setCardTerminalPreCommands('my-card-id', ['echo "Setting env..."', 'conda activate env']); ``` *** ## Core IPC namespaces (`mainIpc`) The `mainIpc` parameter exposes direct wrappers to core application IPC channels. Use these namespaces to access background functionalities: * `lynxIpc`: Main app configuration, plugin loading, and logger utilities. * `application`: Window layout controls (minimize, maximize, close, relaunch). * `browser`: Controls browser tabs, download panels, and navigation. * `contextMenu`: Context menus and system tray options. * `dialogWindow`: System alert dialog prompts and file selection panels. * `downloadManager`: Queries download indicators and file sizes. * `files`: File operations (validate directories, list paths, delete/trash). * `git`: Git repository operations (shallow clone, pull, checkout branch). * `pty`: PTY process spawning, input writing, resizing, and exit hooks. * `storage`: Direct database operations. * `storageUtils`: Storage helper operations. * `user`: Retrives user profiles and options. * `utils`: Executes shell commands, activates Conda, and validates Python. # Lifecycle hooks and utilities Source: https://docs.lynxhub.app/plugins/extensions/main/lifecycle-utilities Manage main process lifecycle event hooks and access host managers. The backend registry allows you to execute operations during app startup, register custom tray menu triggers, and retrieve utility instances of major application managers. *** ## App lifecycle event registry The `lynxApi` parameter exposes methods to register callback hooks executing at various stages of the application lifecycle: ```typescript theme={null} // Triggers when Electron has finished booting (app.whenReady()) lynxApi.onAppReady(callback: () => Promise): void; // Triggers when the main BrowserWindow is ready to show lynxApi.onReadyToShow(callback: () => void): void; // Registers custom IPC channels (on/handle listeners) lynxApi.listenForChannels(callback: () => void): void; ``` *** ## System tray integration You can insert custom menu options directly into the LynxHub system tray context menu. ```typescript theme={null} import {MenuItemConstructorOptions} from 'electron'; lynxApi.trayMenu_AddItem(callback: () => { item: MenuItemConstructorOptions; index: number; }): void; ``` ### Example: Tray item clicks ```javascript theme={null} lynxApi.trayMenu_AddItem(() => { return { item: { label: 'Restart LynxHub Backend', click: async () => { const appManager = await utils.getAppManager(); appManager.restart(); }, }, index: 2, // Index position inside the dropdown menu list }; }); ``` *** ## Error tracking telemetry To track uncaught exceptions in the backend process, initialize a Sentry telemetry scope: ```typescript theme={null} const scope = lynxApi.initNodeSentry('https://your-sentry-dsn@sentry.io/project'); scope.setTag('backend_extension', 'active'); ``` *** ## Main process utilities (`utils`) The `utils` parameter contains references to core modules, terminal tools, and window managers: ### Native pseudo-terminals (`nodePty`) Exposes the `node-pty` module directly. You can use it to spawn shell terminals or Python executables: ```typescript theme={null} const ptyProcess = utils.nodePty.spawn('python', ['-m', 'venv', 'venv-dir'], { name: 'xterm-color', cols: 80, rows: 24, cwd: '/project-dir', env: process.env, }); ``` *** ### Window manager (`getAppManager()`) Resolves to the `MainWindowManager` class singleton. Use this to control the main window or send IPC messages directly to the frontend. ```typescript theme={null} const appManager = await utils.getAppManager(); // Retrieve window objects const win = appManager.getMainWindow(); // Returns BrowserWindow const web = appManager.getWebContent(); // Returns WebContents // Send raw IPC events to the frontend appManager.sendMessage('custom-channel', {status: 'updated'}); // Relaunch the application appManager.restart(); ``` *** ### Modules controller (`getModuleManager()`) Resolves to the `ModuleManager` class singleton. Use this to monitor or modify installed card modules. ```typescript theme={null} const moduleManager = await utils.getModuleManager(); // Retrieve methods registered by card modules const methods = moduleManager.getMethodsById(cardId); // Uninstall card modules await moduleManager.uninstallCardByID(cardId); ``` # Main backend overview Source: https://docs.lynxhub.app/plugins/extensions/main/overview Understand the environment and configuration of the LynxHub extension backend. Extensions run code inside the Electron Main process. This allows you to utilize full Node.js APIs, interface with local operating system tools, listen to app lifecycle hooks, and control database storage. *** ## Environment details The extension backend executes in a Node.js process managed by Electron. Since it runs in the main thread, it has unrestricted access to the local machine, filesystem, and native dependencies (such as spawning pseudo-terminals via `node-pty`). *** ## Entrypoint configuration Your backend main script must build into `scripts/main/mainEntry.cjs` as a CommonJS module. The host application imports it during startup. Your entrypoint file must export an `initialExtension` function: ```typescript theme={null} import {ExtensionMainApi, MainExtensionUtils} from '@lynx_main/plugins/extensions/types'; import {MainIpcApi} from '@lynx_main/plugins/extensions/ipcWrapper'; module.exports = { initialExtension: async (lynxApi: ExtensionMainApi, utils: MainExtensionUtils, mainIpc: MainIpcApi): Promise => { // Backend initialization logic here }, }; ``` ### Initialization parameters The initialization function receives three arguments: 1. `lynxApi`: The central registry to subscribe to application startup triggers, window readiness, or inject tray menu items. 2. `utils`: Utility getters to resolve major application class singletons (like storage, modules, and window managers) or access native `node-pty` methods. 3. `mainIpc`: A structured wrapper namespace granting direct access to Core application IPC handlers. # Publishing extensions Source: https://docs.lynxhub.app/plugins/extensions/publish Publish extensions to the LynxHub plugin repository. To share your extension with the community and make it installable directly within LynxHub, you must submit it to the official LynxHub plugin registry. Before publishing, ensure you complete local verification and configure your repository branches correctly. *** ## Prerequisites Before submitting your extension for publication, you must verify that all features work as expected. 1. **Read testing guidelines**: Familiarize yourself with the static validation and local testing flows in the [Testing extensions](/plugins/extensions/test) guide. 2. **Read building guidelines**: Learn how to compile and structure the compiled files in the [Building extensions](/plugins/extensions/build) guide. 3. **Verify locally**: Install the compiled production build of your extension into your local `Plugins/` folder. Ensure it boots and runs correctly without any runtime errors. *** ## Set up the compiled branch (`main`) The registry crawler accesses the `main` branch to retrieve the compiled production assets. You must keep this branch clean of raw source code. 1. Create a branch named `main` in your repository. 2. Copy the compiled `scripts/` directory to the root of the `main` branch. 3. Commit and push the compiled files to the `main` branch. *** ## Set up the `metadata` branch The LynxHub registry crawler scans extension repositories to fetch versioning and compatibility data. You must expose this metadata on a dedicated branch. 1. Create a branch named `metadata` in your extension repository. 2. Add the required `metadata.json` and `versioning.json` configuration files to the root of the branch. 3. Ensure the structure of these files matches the specifications in the [Extension configuration](/plugins/extensions/configuration) guide. 4. Ensure the `commit` hashes specified in `versioning.json` match the release commits on your `main` branch. *** ## Register your extension repository To register your extension in the global repository list, add it to the statics registry: 1. Visit the [LynxHub Statics repository](https://github.com/KindaBrazy/LynxHub-Statics). 2. Fork the repository. 3. Switch to the `source` branch in your fork. 4. Open the `plugins.json` file located at the root of the project. 5. Append your extension's repository base URL to the JSON array. For example: ```json theme={null} [ "https://github.com/KindaBrazy/LynxHub-Module-Offline-Container", "https://github.com/KindaBrazy/LynxHub-Python-Toolkit", "https://github.com/KindaBrazy/LynxHub-Hardware-Monitor", "https://github.com/KindaBrazy/LynxHub-Custom-Actions", "https://github.com/your-username/your-extension-repo" ] ``` 6. Commit the change and push to your fork. 7. Open a **Pull Request** targeting the `source` branch of `KindaBrazy/LynxHub-Statics`. Once the Pull Request is reviewed and merged, LynxHub's automated workflows will crawl your `metadata` branch, pull the release information, and register the extension in the application database. # Extension quick start Source: https://docs.lynxhub.app/plugins/extensions/quick-start Build and run a basic LynxHub extension in minutes. LynxHub extensions allow you to customize the application shell, add custom React routes, register Redux state reducers, and interact with Node.js APIs in the main process. Follow this guide to create and test a simple extension. *** ## Clone the starter template To start developing quickly, first clone the host LynxHub application repository and navigate to its root folder. Then, clone the `source` branch of the official [LynxHub Extension Template](https://github.com/KindaBrazy/LynxHub-Extension-Template) directly into the `extension` folder: ```bash theme={null} # Clone the host application git clone https://github.com/KindaBrazy/LynxHub cd LynxHub # Clone the extension template source branch into the /extension directory git clone -b source https://github.com/KindaBrazy/LynxHub-Extension-Template extension ``` This sets up the following folder structure: ``` extension/ ├── electron.vite.config.ts # Bundler and Module Federation configuration ├── package.json # Identity and Node.js module configuration └── src/ ├── main/ │ └── lynxExtension.ts # Main process background scripts └── renderer/ └── Extension.tsx # React component injected into host UI ``` *** ## Define package configuration Open the `package.json` file at the root of your `extension` folder and customize your extension's name, author information, and repository links. ```json theme={null} { "name": "my-custom-extension", "version": "1.0.0", "author": { "name": "Your Name", "url": "https://github.com/your-username" }, "homepage": "https://github.com/your-username/my-custom-extension", "repository": "https://github.com/your-username/my-custom-extension", "type": "module" } ``` Extension metadata (`metadata.json`) and release version history (`versioning.json`) must be placed in a dedicated `metadata` branch of your repository. They are not stored in the `source` branch. For more details, see the [Extension configuration](/plugins/extensions/configuration) and [Publishing extensions](/plugins/extensions/publish) guides. *** ## Implement the main process Create a `src/main/lynxExtension.ts` file. This handles background processes, system-tray menus, and Electron lifecycle hooks. ```typescript theme={null} import {ExtensionMainApi, MainExtensionUtils} from '@lynx_main/plugins/extensions/types'; export async function initialExtension(lynxApi: ExtensionMainApi, utils: MainExtensionUtils) { // Listen for the host application boot sequence lynxApi.onAppReady(async () => { console.log('Extension backend successfully initialized!'); }); } ``` *** ## Implement the renderer process Create a `src/renderer/Extension.tsx` file. This injects UI elements, styles, and custom menus into the frontend. ```tsx theme={null} import {ExtensionRendererApi} from '@lynx/plugins/extensions/types/api'; export function InitialExtensions(lynxAPI: ExtensionRendererApi) { // Inject a basic text widget into the end of the top title bar lynxAPI.titleBar.addEnd(() =>
Hello from Extension!
); } ``` *** ## Run and test locally Start the LynxHub host application in development mode: ```bash theme={null} npm run dev ``` The application automatically detects the `/extension` directory and boots your custom code. * Inspect your new Title Bar widget directly on the screen. * Press `F12` or `Ctrl+Shift+I` to open the developer tools and verify the backend logs in your terminal. * Run static validation and typecheck scripts to verify code health: ```bash theme={null} npm run validate:ext ``` # Renderer frontend overview Source: https://docs.lynxhub.app/plugins/extensions/renderer/overview Understand the environment and boot sequence of the LynxHub extension frontend. Extensions run inside the Electron renderer process. You can build rich user interfaces, listen to application events, inject custom page routes, and coordinate operations with the main process. *** ## Environment details The LynxHub frontend uses the following stack: * **UI library**: React * **Styling**: Tailwind CSS v4 * **Component library**: HeroUI v3 (`@heroui/react`) *** ## Entrypoint configuration Your extension frontend must build into `scripts/renderer/rendererEntry.mjs`. The host application loads this bundle dynamically during its boot sequence. Your entrypoint file must export an `InitialExtensions` function. The host executes this function synchronously before mounting the React application tree: ```typescript theme={null} import {ExtensionRendererApi} from '@lynx/plugins/extensions/types/api'; import {RendererIpcApi} from '@lynx/plugins/extensions/types/ipcWrapper'; export function InitialExtensions(lynxAPI: ExtensionRendererApi, rendererIpc: RendererIpcApi, extensionId: string): void { // Your frontend initialization logic here } ``` ### Initialization parameters The initialization function receives three arguments: 1. `lynxAPI`: The main interface to inject components, manage routes, and customize default views. 2. `rendererIpc`: A type-safe IPC wrapper to communicate with the extension's backend process. 3. `extensionId`: A unique, stable identifier assigned to your extension. *** ## Central state management If your extension requires shared global state, you can register Redux slice reducers. Call the `addReducer` method during initialization: ```typescript theme={null} import {createSlice} from '@reduxjs/toolkit'; const mySlice = createSlice({ name: 'myExtensionState', initialState: {count: 0}, reducers: { increment: state => { state.count += 1; }, }, }); export function InitialExtensions(lynxAPI) { lynxAPI.addReducer([ { name: 'myExtension', reducer: mySlice.reducer, }, ]); } ``` Components loaded in the application can consume this state using standard `useSelector` and `useDispatch` hooks. *** ## Global React hooks You can run global React logic by registering custom hook components. Call the `addCustomHook` method: ```typescript theme={null} import {useEffect} from 'react'; function MyCustomHook() { useEffect(() => { console.log('Extension custom hook mounted globally'); }, []); return null; } export function InitialExtensions(lynxAPI) { lynxAPI.addCustomHook(MyCustomHook); } ``` The host application mounts registered custom hooks at the root of the React tree. This makes them ideal for background listeners, telemetry, or window-level hotkey managers. *** ## Production error tracking You can initialize Sentry to capture frontend exceptions. Call `initBrowserSentry` with your DSN: ```typescript theme={null} export function InitialExtensions(lynxAPI) { const scope = lynxAPI.initBrowserSentry('https://your-sentry-dsn@sentry.io/project'); scope.setTag('extension_mode', 'production'); } ``` # Page and card customization Source: https://docs.lynxhub.app/plugins/extensions/renderer/page-customization Extend host views and inject components into default pages or card cards. You can customize existing pages and individual card layouts. The `customizePages` and `cards` namespaces provide layout slots for fine-grained modifications. *** ## Home page customization The main **Home** page of LynxHub can be customized by replacing core sections or adding components around scroll grids. ```typescript theme={null} // Replace default page sections lynxAPI.customizePages.home.replace.searchAndFilter(Component: FC): void; lynxAPI.customizePages.home.replace.searchResult(Component: FC<{searchValue: string}>): void; lynxAPI.customizePages.home.replace.categories(Component: FC): void; // Add elements around the main category grid lynxAPI.customizePages.home.add.top(Component: FC): void; lynxAPI.customizePages.home.add.bottom(Component: FC): void; lynxAPI.customizePages.home.add.scrollTop(Component: FC): void; lynxAPI.customizePages.home.add.scrollBottom(Component: FC): void; // Append elements to categories sections lynxAPI.customizePages.home.add.pinCategory(Component: FC): void; lynxAPI.customizePages.home.add.recentlyCategory(Component: FC): void; lynxAPI.customizePages.home.add.allCategory(Component: FC): void; ``` *** ## Category-specific pages You can inject components into standard category feeds (Audio, Image, Text, Agents, Others, Tools, Games). The slots are defined via a shared `PageAdd` structure: ```typescript theme={null} // Available for: audio | image | text | agents | others | tools | games lynxAPI.customizePages.tools.add.top(Component: FC): void; lynxAPI.customizePages.tools.add.bottom(Component: FC): void; lynxAPI.customizePages.tools.add.scrollTop(Component: FC): void; lynxAPI.customizePages.tools.add.scrollBottom(Component: FC): void; lynxAPI.customizePages.tools.add.cardsContainer(Component: FC): void; ``` ### Example: Inject settings panel on the tools page ```jsx theme={null} import ToolsPageSettings from './components/ToolsPageSettings'; export function InitialExtensions(lynxAPI) { // Adds a container settings block on the Tools category feed page lynxAPI.customizePages.tools.add.cardsContainer(ToolsPageSettings); } ``` *** ## Settings and dashboard pages You can append new settings tabs or dashboard modules: ```typescript theme={null} // Settings Page customization lynxAPI.customizePages.settings.add.navButton(Component: FC): void; lynxAPI.customizePages.settings.add.content(Component: FC): void; // Dashboard Page customization lynxAPI.customizePages.dashboard.add.navButton(Component: FC): void; lynxAPI.customizePages.dashboard.add.content(Component: FC): void; ``` *** ## Card layout customization You can customize the card components that display card metadata in the feed. ```typescript theme={null} import {CardData, LoadedCardData} from '@lynx_common/types/plugins/modules'; // Replace the entire cards grid list view lynxAPI.cards.replace(Component: FC<{cards: LoadedCardData[]}>): void; // Replace individual card layout cards type CardDataProps = { useCardStore: UseCardStoreType; useCardOverlayState: typeof useCardOverlayState; }; lynxAPI.cards.replaceComponent(Component: FC): void; // Replace layout sections of default cards lynxAPI.cards.customize.header(Component: FC): void; lynxAPI.cards.customize.body(Component: FC): void; lynxAPI.cards.customize.footer(Component: FC): void; ``` *** ## Card menus and custom modals You can append custom items to the options menu of each card, or link options to custom detail modals (e.g., verifying dependencies). ```typescript theme={null} // Replace the entire options dropdown menu lynxAPI.cards.customize.menu.replace(Component: FC): void; // Append a menu item section at a specific index type AddMenuType = { index: number; components: FC[]; }; lynxAPI.cards.customize.menu.addSection(sections: AddMenuType[]): void; // Register custom options modal handlers type AddModalType = { key: string; component: FC; }; lynxAPI.cards.customize.menu.addModal(modals: AddModalType[]): void; ``` ### Example: Card menu option triggers a modal ```jsx theme={null} import CardMenuOption from './components/CardMenuOption'; import CardMenuModal from './components/CardMenuModal'; const DEPS_MODAL_KEY = 'python-toolkit-deps-modal'; export function InitialExtensions(lynxAPI) { // Add menu button lynxAPI.cards.customize.menu.addSection([ { index: 1, components: [CardMenuOption], }, ]); // Link button modal action key with the modal component lynxAPI.cards.customize.menu.addModal([ { key: DEPS_MODAL_KEY, component: CardMenuModal, }, ]); } ``` *** ## Custom Tools Cards You can register custom cards directly into the **Tools** or **Games** sections of LynxHub using the `registerToolsCard` method: ```typescript theme={null} lynxAPI.cards.registerToolsCard?.(config: { id: string; title: string; description: string; icon?: string | ReactNode; onPress?: () => void; component?: ComponentType; where?: string; }): void; ``` ### Example: Registering a custom Tools card ```jsx theme={null} import PythonToolkitCard from './components/ToolsPage'; export function InitialExtensions(lynxAPI) { lynxAPI.cards.registerToolsCard?.({ id: 'python-toolkit', title: 'Python Toolkit', description: 'Manage Python versions, virtual environments, packages, requirements and more.', component: PythonToolkitCard, where: 'tools_page', }); } ``` # State, events, and routing Source: https://docs.lynxhub.app/plugins/extensions/renderer/state-events Hook into client routing, system event emitters, and communicate with the main process. Extensions can manage pages using client-side routers, communicate across processes with the IPC wrapper, and react to runtime lifecycle milestones through the system event emitter. *** ## Client-side routing and custom pages You can add custom page components, register new client-side routes, or replace default view pages: ```typescript theme={null} // Register a custom page to the router and sidebar navigation lynxAPI.router.addPage(page: { id: string; title: string; icon?: ReactNode; component: FC; position?: 'top' | 'bottom' | 'hidden'; }): void; // Register new client routes lynxAPI.router.add(routes: RouteObject[]): void; // Replace default pages entirely lynxAPI.router.replace.homePage(Component: FC): void; lynxAPI.router.replace.imageGenerationPage(Component: FC): void; lynxAPI.router.replace.textGenerationPage(Component: FC): void; lynxAPI.router.replace.audioGenerationPage(Component: FC): void; lynxAPI.router.replace.toolsPage(Component: FC): void; lynxAPI.router.replace.gamesPage(Component: FC): void; lynxAPI.router.replace.dashboardPage(Component: FC): void; lynxAPI.router.replace.modulesPage(Component: FC): void; lynxAPI.router.replace.extensionsPage(Component: FC): void; lynxAPI.router.replace.settingsPage(Component: FC): void; ``` *** ## Event bus (`events`) The Event Bus enables communication between the application host and active extensions. Use the `events` property on `lynxAPI`. ```typescript theme={null} // Register listener. Returns unsubscribe function. events.on(event: string, callback: (payload: any) => void): () => void; // Remove listener events.off(event: string, callback: (payload: any) => void): void; // Emit custom events events.emit(event: string, payload: any): void; // Get listener count events.getListenerCount(event: string): number; ``` ### Hookable system events The host emits events during critical application milestones: | Event Name | Payload Shape | Description | | :------------------------ | :------------------------------- | :--------------------------------------------------------- | | `before_card_start` | `{ id: string }` | Fires before launching a card module process. | | `before_card_install` | `{ id: string }` | Fires before launching a card installation wizard. | | `card_install_addStep` | `{ id: string, step: object }` | Fires when registering a step in the installation stepper. | | `card_collect_user_input` | `{ id: string, fields: object }` | Fires when collecting user configurations for a card. | *** ## Renderer IPC (`rendererIpc`) The `rendererIpc` object provides type-safe IPC wrappers to invoke methods or send events to your main process script. ```typescript theme={null} // Invoke a main process handle method (Returns Promise) rendererIpc.invoke(channel: string, ...args: any[]): Promise; // Send a fire-and-forget message to the main process rendererIpc.send(channel: string, ...args: any[]): void; // Listen for messages from the main process. Returns cleanup function. rendererIpc.on(channel: string, callback: (event: any, ...args: any[]) => void): () => void; ``` ### Example: Invoking custom backend IPC ```typescript theme={null} export function InitialExtensions(lynxAPI, rendererIpc) { rendererIpc.invoke('hardware:get-status').then(status => { console.log('CPU Load:', status.cpu); }); } ``` *** ## Read-only modules data The `modulesData` property grants access to loaded card modules information. ```typescript theme={null} type ModuleData = { allModules: CardModules; // All modules loaded by application allCards: CardData[]; // Flat array of all card definitions // React hook to retrieve custom arguments for a card ID useGetArgumentsByID: (id: string) => ArgumentsData | undefined; // React hook to retrieve cards list belonging to a specific page ID useGetCardsByPath: (path: AvailablePageIDs) => LoadedCardData[] | undefined; // Retrieve a specific method from a card renderer methods object getCardMethod: (cards: CardData[], id: string, method: string) => Function | undefined; }; ``` *** ## Toast indicators You can trigger visual toast notifications: ```typescript theme={null} lynxAPI.toast.top(message: string, options?: any): void; lynxAPI.toast.bottom(message: string, options?: any): void; ``` *** ## Tab navigation and control You can programmatically navigate tabs or set the active page for the current active tab: ```typescript theme={null} lynxAPI.tabs.setActivePage(pageID: string, title?: string, isTerminal?: boolean): void; ``` ### Example: Navigate to a custom page on click ```jsx theme={null} export function MySidebarWidget({lynxAPI}) { return ; } ``` # UI slots customization Source: https://docs.lynxhub.app/plugins/extensions/renderer/ui-slots Inject custom React components into specific slots in the LynxHub user interface. You can customize the layout structure of the host application. The `lynxAPI` exposes registration methods to inject or replace React components in multiple layout slots. *** ## Title bar customization The top title bar is divided into three sections: **Start** (left), **Center**, and **End** (right). ```typescript theme={null} // Add widgets to specific slots lynxAPI.titleBar.addStart(Component: FC): void; lynxAPI.titleBar.addCenter(Component: FC): void; lynxAPI.titleBar.addEnd(Component: FC): void; // Replace entire default slot sections lynxAPI.titleBar.replaceCenter(Component: FC): void; lynxAPI.titleBar.replaceEnd(Component: FC): void; ``` ### Example: Add a sync button ```jsx theme={null} import {Button} from '@heroui/react'; function SyncButton() { return ( ); } export function InitialExtensions(lynxAPI) { lynxAPI.titleBar.addEnd(SyncButton); } ``` *** ## Status bar customization The bottom status bar lists state indicators and quick toggles. ```typescript theme={null} // Add widgets to specific slots lynxAPI.statusBar.addStart(Component: FC): void; lynxAPI.statusBar.addCenter(Component: FC): void; lynxAPI.statusBar.addEnd(Component: FC): void; // Replace the entire status bar container lynxAPI.statusBar.replaceContainer(Component: FC): void; ``` ### Example: Hardware monitor integration If you want to replace the default status bar with custom metrics, use `replaceContainer`: ```jsx theme={null} import HardwareStatusBar from './components/HardwareStatusBar'; export function InitialExtensions(lynxAPI) { // Replace the default bar with your custom component lynxAPI.statusBar.replaceContainer(HardwareStatusBar); } ``` *** ## Navigation bar customization The sidebar navigation panel houses page links and settings routes. ```typescript theme={null} // Replace default elements lynxAPI.navBar.replace.container(Component: FC): void; // Entire sidebar lynxAPI.navBar.replace.contentBar(Component: FC): void; // Upper navigation list lynxAPI.navBar.replace.settingsBar(Component: FC): void; // Lower settings list // Add navigation buttons to lists lynxAPI.navBar.addButton.contentBar(Component: FC): void; lynxAPI.navBar.addButton.settingsBar(Component: FC): void; ``` *** ## Running AI panel customization The split screen view shown when executing a card is customizable. You can replace individual panels or the full viewport. ```typescript theme={null} lynxAPI.runningAI.container(Component: FC): void; // Replaces entire running view lynxAPI.runningAI.terminal(Component: FC): void; // Replaces terminal pane only lynxAPI.runningAI.browser(Component: FC): void; // Replaces browser webview pane only ``` *** ## Modals customization You can register custom modal overlays or override the host's default dialog windows. ### Add custom modals To register a custom modal in the application overlay tree: ```typescript theme={null} lynxAPI.addModal(Component: FC): void; ``` ### Replace built-in modals To replace a default host modal, assign your replacement component to the appropriate key in `replaceModals`: ```typescript theme={null} lynxAPI.replaceModals.updateApp(Component: FC): void; lynxAPI.replaceModals.launchConfig(Component: FC): void; lynxAPI.replaceModals.cardExtensions(Component: FC): void; lynxAPI.replaceModals.updatingNotification(Component: FC): void; lynxAPI.replaceModals.cardInfo(Component: FC): void; lynxAPI.replaceModals.installUi(Component: FC): void; lynxAPI.replaceModals.uninstallCard(Component: FC): void; lynxAPI.replaceModals.unassignCard(Component: FC): void; lynxAPI.replaceModals.warning(Component: FC): void; lynxAPI.replaceModals.cardReadme(Component: FC): void; lynxAPI.replaceModals.gitManager(Component: FC): void; ``` *** ## Miscellaneous slots ### Markdown viewer replacement You can supply a custom Markdown component to display project readme files: ```typescript theme={null} type ReplaceMdProps = {repoPath: string; rounded?: boolean}; lynxAPI.replaceMarkdownViewer(Component: FC): void; ``` ### Background replacement You can override the default window background layer: ```typescript theme={null} lynxAPI.replaceBackground(Component: FC): void; ``` # Testing extensions Source: https://docs.lynxhub.app/plugins/extensions/test Run and test your custom extensions in development and production environments. You can test LynxHub extensions during development or after compiling them for production. ## Development mode testing LynxHub automatically boots your extension in development mode when the codebase exists in the project root. 1. Place your extension project directory in the LynxHub repository root. 2. Name the folder `extension`. 3. Run the development server from the LynxHub root directory: ```bash theme={null} npm run dev ``` The application detects the `/extension` directory. It automatically loads your extension in development mode. The main process dynamically imports your code from `extension/src/main/lynxExtension.ts`. The renderer process imports your code from `@lynx_extension/renderer/Extension`. ### Inspector and logs You can inspect components and debug frontend behavior using the Electron developer tools. * Press `Ctrl+Shift+I` or `F12` to open the developer tools. * You can inspect the DOM structure, verify styles, and run frontend console commands. * The main process prints backend logs directly to your terminal window. * Vite applies styling changes instantly using Hot Module Replacement (HMR). * You must restart the application if you modify backend files or entry point definitions. *** ## Static validation You should run validation scripts to check code quality and typescript compiler correctness. To test your extension code for errors, run the validation script in the LynxHub root directory: ```bash theme={null} npm run validate:ext ``` This command executes the following tasks: * Formats your extension code using Prettier. * Checks your extension files for syntax and stylistic errors using ESLint. * Validates that your TypeScript files compile without type errors. *** ## Production testing You should test that your compiled extension loads correctly in production mode before distributing it. 1. Build your extension for production. 2. Manually copy the compiled files to your local `Plugins` directory. 3. Launch LynxHub to verify that the extension initializes without errors. 4. Read the [Building extensions](/plugins/extensions/build) guide for detailed installation steps. # Module API reference Source: https://docs.lynxhub.app/plugins/modules/api-references Reference guide for the LynxHub module APIs. LynxHub modules (or cards) represent standalone AI applications or tools. Each module integrates with the host by exporting backend methods and configuring renderer behaviors. Unlike extensions, modules are purely configuration and API calling wrappers. The host application handles all user interface rendering, layouts, themes, and global state tracking. *** ## Entry points A module must provide main process logic and renderer process configuration. ### Backend entry point Your backend main script must export a default function that initializes your modules. ```typescript theme={null} import {MainModules, MainModuleUtils} from '../../src/common/types/plugins/modules'; export default async function initialModule(utils: MainModuleUtils): Promise; ``` This function returns an array of `MainModules` objects mapping each module ID to its main process methods. ```typescript theme={null} export type MainModules = { id: string; methods: () => CardMainMethods; }; ``` ### Frontend entry point Your frontend renderer script must export the `CardModules` configuration list as its default export. ```typescript theme={null} import {CardModules} from '../../src/common/types/plugins/modules'; const rendererModules: CardModules = [ // ... your PagesData pages ]; export default rendererModules; ``` *** ## Backend API reference The backend entry point receives the `utils` helper object. Use it to access directories, manage processes, and handle persistent storage. ### MainModuleUtils Use this object to interact with the host backend environment. #### `storage` Simple key-value database storage helper. * `get(key: string): T` - Retrieves custom data synchronously. * `set(key: string, data: T): void` - Saves custom data. #### `ipc` IPC communications helper. * `handle(channel: string, listener: (event: any, ...args: any[]) => any): void` - Registers an IPC invocation handler. * `on(channel: string, listener: (event: any, ...args: any[]) => void): void` - Listens to IPC messages. * `send(channel: string, ...args: any[]): void` - Emits an IPC message to the renderer. #### `pty` Exposes the `node-pty` module. Use it to spawn pseudo-terminals for running interactive CLI workflows. #### `isPullAvailable(dir: string): Promise` Checks if Git pull is available in the target directory. #### `trashDir(dir: string): Promise` Moves the specified directory to the system trash. #### `removeDir(dir: string): Promise` Deletes the specified directory permanently. #### `getInstallDir(id: string): string | undefined` Returns the absolute installation directory for the card module. #### `getConfigDir(): string | undefined` Returns the user data configuration directory for LynxHub. #### `pullDir(dir: string, showTaskbarProgress?: boolean): Promise` Performs a Git pull operation in the specified directory. #### `getExtensions_TerminalPreCommands(id: string): string[]` Retrieves pre-launch commands defined by extensions for the specified card. ### CardMainMethods These are the backend methods you implement for your card modules. #### `getRunCommands(): Promise` **Required**. Returns the CLI commands needed to launch the module's server or process. #### `readArgs(): Promise` *Optional*. Reads saved launch arguments from a configuration file. #### `saveArgs(args: ChosenArgument[]): Promise` *Optional*. Saves launch arguments chosen by the user. #### `mainIpc(): void` *Optional*. Sets up backend IPC handlers. Executed during module initialization. #### `updateAvailable(): Promise` *Optional*. Returns whether an update is available for this module. #### `isInstalled(): Promise` *Optional*. Returns whether the module is currently installed. #### `uninstall(): Promise` *Optional*. Clears folders and files when the user uninstalls the card. *** ## Frontend API reference The frontend configuration defines pages, cards, metadata, and custom installation/updater wizards. ### CardRendererMethods These methods define renderer behaviors for your card module. #### `catchAddress(line: string): string | undefined` *Optional*. Inspects each terminal output line during startup. Returns the URL of the running web interface once detected (e.g., `http://127.0.0.1:7860`). #### `fetchExtensionList(): Promise` *Optional*. Fetches a list of extensions available for the card. #### `parseArgsToString(args: ChosenArgument[]): string` *Optional*. Serializes the arguments array into a single CLI parameters string. #### `parseStringToArgs(args: string): ChosenArgument[]` *Optional*. Parses a CLI string back into an arguments array. #### `cardInfo(api: CardInfoApi, callback: CardInfoCallback): void` *Optional*. Populates details inside the card information modal. ##### `CardInfoApi` * `installationFolder?: string` - Absolute installation path of the card. * `storage`: `{ get: (key: string) => Promise; set: (key: string, data: T) => void }` - Asynchronous storage get/set interface. * `ipc`: `RendererIpcTypes` - Inter-process communication handler. * `getFolderSize(dir: string): Promise` - Queries absolute size on disk of the folder. * `getFolderCreationTime(dir: string): Promise` - Queries folder creation date. * `getLastPulledDate(dir: string): Promise` - Queries last Git pull execution date. * `getCurrentReleaseTag(dir: string): Promise` - Queries current active Git release tag. ##### `CardInfoCallback` * `setDescription(descriptions: CardInfoDescriptions): void` - Pushes custom list sections of name-result labels to render in the card modal. * `setOpenFolders(dir: string[] | undefined): void` - Pushes a list of folder paths to be displayed as clickable buttons that open in the native explorer. #### `manager` *Optional*. Defines the installation and update workflows: * `startInstall(stepper: InstallationStepper): void` - Triggers the installation wizard. * `updater` - Defines updates: * `updateType: 'git' | 'stepper'` - Git-based pull or stepper-based update workflow. * `startUpdate(stepper: InstallationStepper, dir?: string): void` - Triggers the updater wizard. ### InstallationStepper The installation stepper manages the setup UI wizard. #### `initialSteps(stepTitles: InitialSteps): void` Registers the list of steps to display on the progress timeline. ##### `InitialSteps` ```typescript theme={null} type InitialSteps = InitialStep[]; type InitialStep = | string | { title: string; alerts: CustomAlertParams[]; }; type CustomAlertParams = { type?: 'default' | 'note' | 'warning' | 'danger'; title: string; description?: string; urls?: {title: string; url: string}[]; }; ``` #### `nextStep(): Promise` Advances the installer to the next step. #### `starterStep(options?: StarterStepOptions): Promise` Displays the initial installer step. Resolves to the user's choice: install clean or locate an existing folder. ##### `StarterStepOptions` ```typescript theme={null} type StarterStepOptions = { disableSelectDir?: boolean; }; ``` ##### `InstallationMethod` ```typescript theme={null} type InstallationMethod = { chosen: 'install' | 'locate'; targetDirectory?: string; }; ``` #### `cloneRepository(url: string): Promise` Clones a Git repository to the target directory. Returns the path of the cloned repository. #### `runTerminalScript(workingDirectory: string, scriptFileName: string): Promise` Executes a terminal script file. Resolves when the script finishes and the user clicks **Next**. #### `executeTerminalCommands(commands: string | string[], workingDirectory?: string): Promise` Executes commands in the specified directory. Resolves when execution completes. #### `downloadFileFromUrl(fileUrl: string): Promise` Downloads a file to the user's downloads folder. Returns the downloaded file's path. #### `progressBar(isIndeterminate: boolean, title?: string, percentage?: number, description?: { label: string, value: string }[]): void` Configures a progress bar overlay. Use this to represent custom tasks. #### `setInstalled(dir?: string): void` Saves the target installation directory and flags the module as installed. #### `setUpdated(): void` Flags the module as updated. #### `collectUserInput(inputFields: UserInputField[], title?: string): Promise` Displays a dialog requesting user inputs (checkboxes, text inputs, selectors). Resolves with the inputs. ##### `UserInputField` ```typescript theme={null} type UserInputField = { id: string; label: string; type: 'checkbox' | 'text-input' | 'select' | 'directory' | 'file'; selectOptions?: string[]; defaultValue?: string | boolean; isRequired?: boolean; }; ``` ##### `UserInputResult` ```typescript theme={null} type UserInputResult = { id: string; result: string | boolean; }; ``` #### `showFinalStep(resultType: 'success' | 'error', resultTitle: string, resultDescription?: string): void` Displays the final completion step of the installer wizard. #### `ipc` IPC communications helper. * `invoke(channel: string, ...args: any[]): Promise` - Invokes a backend handler. * `on(channel: string, listener: (event: any, ...args: any[]) => void): () => void` - Registers a listener. Returns a cleanup unsubscribe function. * `send(channel: string, ...args: any[]): void` - Sends an IPC message. #### `storage` * `get(key: string): Promise` - Retrieves stored data asynchronously. * `set(key: string, data: T): void` - Saves stored data. #### `postInstall` Post-installation automated tasks. * `installExtensions(extensionURLs: string[], extensionsDir: string): Promise` - Downloads a set of Git extensions for the module. * `config(configs: ModuleConfigOptions): void` - Automatically configures auto-updates, custom arguments presets, and pre-launch hooks. ##### `ModuleConfigOptions` ```typescript theme={null} type ModuleConfigOptions = { /** If true, automatically updates all extensions when launching WebUI */ autoUpdateExtensions?: boolean; /** If true, automatically updates the WebUI itself */ autoUpdateCard?: boolean; /** Pre-defined arguments to pass to the WebUI */ customArguments?: { /** Name of the preset configuration */ presetName: string; /** Array of custom arguments */ customArguments: { /** Name of the argument like --use-cpu */ name: string; /** Value of the argument (set empty string '' if it is a boolean or checkbox) */ value: string; }[]; }; /** Custom commands to execute instead of the actual command to launch WebUI */ customCommands?: string[]; /** Defines the behavior of the browser and terminal when launching */ launchBehavior?: Omit; /** Actions to perform before launching WebUI */ preLaunch?: { /** Commands to execute before launch */ preCommands: string[]; /** Paths to open before launch */ openPath: {path: string; type: 'folder' | 'file'}[]; }; }; ``` #### `utils` Helper operations that do not affect the wizard UI. * `decompressFile(compressedFilePath: string): Promise` * `validateGitRepository(localDirectory: string, repositoryUrl: string): Promise` * `verifyFilesExist(directory: string, itemNames: string[]): Promise` * `openFileOrFolder(itemPath: string): void` #### `topToast(message: string): void` Displays a toast at the top of the window. #### `bottomToast(message: string): void` Displays a toast at the bottom of the window. # Module architecture Source: https://docs.lynxhub.app/plugins/modules/architecture Understand the architecture and integration model of LynxHub modules. LynxHub modules represent standalone features or AI engines, represented as **cards** in the interface. Unlike extensions (which dynamically hook into the main shell's React tree), modules are isolated packages running self-contained tools, backing terminal processes, and configuration menus. *** ## Process separation Modules divide execution between the Electron Main process and the Renderer process to ensure backend reliability and interface responsiveness: ```mermaid theme={null} graph TD subgraph Electron Main Process (Node.js) HostMain[Host Main Process] <-->|IPC Channels| ModuleMain["Module Main (TypeScript Source)"] ModuleMain -->|Compiled / Bundled| MainBundle["scripts/main.mjs"] end subgraph Electron Renderer Process (Chromium) HostRenderer[Host React Shell] <-->|lynxplugin:// Protocol| ModuleRenderer["Module Renderer (TypeScript Source)"] ModuleRenderer -->|Compiled / Bundled| RendererBundle["scripts/renderer.mjs"] end ``` ### Main process The main process coordinates the installation, update checks, uninstallation, and execution of backing terminal commands (such as executing Python scripts or starting a local model binary via `node-pty`). * **Entrypoint**: `src/main.ts` (compiled and bundled to `scripts/main.mjs` as an ES module). * **Responsibility**: Exposes backend lifecycle hooks and commands for execution. * **Signature**: ```typescript theme={null} import {MainModules, MainModuleUtils} from '../../src/common/types/plugins/modules'; export default async function initialModule(utils: MainModuleUtils): Promise { return [ { id: 'my-card-id', methods: () => ({ getRunCommands: async () => { return 'python main.py'; }, // Other optional methods readArgs: async () => { ... }, saveArgs: async (args) => { ... }, isInstalled: async () => { ... }, uninstall: async () => { ... }, mainIpc: () => { ... } }), }, ]; } ``` ### Renderer process The renderer process handles card visualization, settings/argument configuration forms, and installation wizard steppers. * **Entrypoint**: `src/renderer.ts` (compiled and bundled to `scripts/renderer.mjs` as an ES module). * **Responsibility**: Registers card UI layouts, defines category mappings, and specifies custom argument inputs. * **Signature**: ```typescript theme={null} import {CardModules} from '../../src/common/types/plugins/modules'; const rendererModules: CardModules = [ { routePath: 'imageGen_page', // placement target (e.g. 'imageGen_page', 'textGen_page', etc.) cards: [ { id: 'my-card-id', title: 'My Card', description: 'Description...', repoUrl: 'https://github.com/my-username/my-card-repo', type: 'image', supportCustomArguments: true, arguments: comfyuiArguments, methods: { catchAddress: (line: string) => { ... }, parseArgsToString: (args: ChosenArgument[]) => { ... }, parseStringToArgs: (args: string) => { ... }, cardInfo: (api, callback) => { ... }, manager: { startInstall: (stepper: InstallationStepper) => { ... }, updater: { updateType: 'git', }, }, }, installationType: 'git', }, ], }, ]; export default rendererModules; ``` *** ## Boot and loading sequence LynxHub performs the following steps to load modules during application startup: ```mermaid theme={null} sequenceDiagram participant App as App Lifecycle participant MM as ModuleManager participant Disk as AppData Plugins Folder participant Main as Module Main (main.mjs) participant Rend as Module Renderer (renderer.mjs) App->>MM: Initialize module manager MM->>Disk: Scan and validate plugin folders Note over MM: Filters out disabled cards MM->>Main: Import module main script MM->>Main: Call initialModule(utils) Note over Main: Cache returned methods App->>MM: Main window ready-to-show MM->>Main: Invoke mainIpc() handlers App->>Rend: Import renderer.mjs via lynxplugin:// Note over Rend: Aggregate cards for React router routes ``` ### 1. Discovery and validation During startup, the host scans folders inside the `Plugins/` directory. It ensures that required files exist before loading a module. ### 2. Import and aggregation The host imports the backend entrypoint `scripts/main.mjs`. It calls `initialModule` and registers card-specific backend operations. The frontend then dynamically loads `scripts/renderer.mjs` using the custom protocol to display card components. *** ## Custom plugin protocol (lynxplugin://) Modern browsers and Chromium engines prevent loading assets or scripts directly from the local file system (`file://` URLs) due to security restrictions. To resolve this safely, LynxHub registers a custom protocol handler at boot: * **Protocol**: `lynxplugin://` * **Hostname mapping**: The hostname corresponds to the plugin folder ID. * **Path mapping**: The path maps directly to files under `/Plugins//`. *** ## Host card state management When card modules run, the host application manages their states internally. Module developers do not need to write state management code. The host aggregates card states using its internal stores: ```mermaid theme={null} graph LR Redux[Host Redux: cards.runningCard] -->|Coordinates| HostShell[Host Shell UI] HostShell -->|Mounts| CardInstance[Card Instance] CardInstance <-->|Reads/Writes| Zustand[Host Zustand Store: store.ts] ``` ### Host Zustand store (local) The host maintains a Zustand store for each mounted card instance to handle interface updates: * Tracking selected arguments. * Monitoring installation and compiler progress. * Streaming active terminal outputs. * Displaying loading states. ### Host Redux store (global) The host updates its central Redux store when cards launch or close, allowing navigation bars and panels to track active processes. *** ## Rollup build configuration To compile and package your module correctly, configure Rollup to output ES modules. Below is the standard Rollup configuration (`rollup.config.mjs`) used by the module template: ```typescript theme={null} import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; import nodeResolve from '@rollup/plugin-node-resolve'; import typescript from '@rollup/plugin-typescript'; /** @type {import('rollup').RollupOptions} */ const config = { input: ['module/src/main.ts', 'module/src/renderer.ts'], output: { dir: 'module_out/scripts', format: 'es', entryFileNames: '[name].mjs', chunkFileNames: '[name]_[hash:6].mjs', }, external: ['electron'], plugins: [json(), typescript({tsconfig: 'module/tsconfig.json'}), nodeResolve(), commonjs()], }; export default config; ``` Make sure to externalize any Node.js dependencies (such as `electron`) in your build config for the main script so they are not bundled. *** ## Development vs. production environments LynxHub provides two separate loading strategies to optimize the developer experience: | Feature | Development mode | Production mode | | :------------------------ | :------------------------------------------ | :----------------------------------------- | | **Main entry import** | `module/src/main` (TypeScript Source) | `scripts/main.mjs` (ES module) | | **Renderer entry import** | `@lynx_module/renderer` (Vite alias) | `scripts/renderer.mjs` (custom protocol) | | **Loading mechanism** | Hot-imported from local source folder | Served via `lynxplugin://` protocol | | **Updates** | Hot Module Replacement (HMR) instantly | Requires a build and app restart | | **Location** | Root `/module` directory of the application | `/Plugins//` directory | ### Development mode During development, the host redirects module loading to local aliases to support instant code updates: * **Renderer**: `@lynx_module/renderer` (pointing to `/module/src/renderer`) * **Main**: `module/src/main` ### Production mode In production: * **Validation**: LynxHub scans folders inside the `Plugins` directory. It ensures that `scripts/main.mjs` and `scripts/renderer.mjs` exist before loading. * **Filtering**: Disabled cards (`plugin.disabledCards`) are filtered out early in both processes so they are never loaded into memory. # Building modules Source: https://docs.lynxhub.app/plugins/modules/build Compile and install card modules for production. You must compile and package your module to test it in a production environment or prepare it for publication. ## Compilation To build your module, run the build script in the LynxHub root directory: ```bash theme={null} npm run build:module ``` This command performs the following build steps: 1. Clears any previous builds by deleting the `module_out/` directory. 2. Compiles your module using Rollup with the config file `module/rollup.config.mjs`. 3. Optimizes and bundles both your backend and frontend code. 4. Outputs the compiled assets into the `module_out/` directory. ### Build output structure The build process generates compiled scripts inside the `module_out/scripts/` directory: * `main.mjs` - Contains the compiled backend main-process bundle. * `renderer.mjs` - Contains the compiled frontend configuration bundle. *** ## Local installation and testing To test the production build of your module locally, you must copy the compiled files to the LynxHub user plugins folder. ### 1. Locate the plugins directory The default application data directory is determined by your installation type: * **Installed**: `C:\Users\\Documents\LynxHub` (on Windows) * **Portable**: `./LynxHub_Data` (located in your application executable folder) Navigate to the `Plugins/` folder inside this directory. ### 2. Create the target folder Create a new subfolder named after your module metadata ID. For example, if your module ID is `offline-container`, create a directory path like: ``` Documents/LynxHub/Plugins/offline-container/ ``` ### 3. Copy the compiled files Create a `scripts/` folder inside your module directory. Copy the compiled outputs from `module_out/` as follows: 1. Copy all compiled files from `module_out/scripts/` directly into the `Plugins//scripts/` folder. Both `main.mjs` and `renderer.mjs` must reside in the root of the `scripts/` directory. 2. Copy your module's static `metadata.json` file to the root of the module folder (`Plugins//metadata.json`). ### 4. Verify directory structure Ensure your module directory matches the following layout: ``` Documents/LynxHub/Plugins// ├── metadata.json └── scripts/ ├── main.mjs └── renderer.mjs ``` Restart LynxHub to load and verify the compiled module. *** ## Production builds When you prepare your module for publication, you must push the compiled build assets to the compiled branch (`main`). 1. Commit all raw source files to your `source` branch. 2. Run the compilation command `npm run build:module`. 3. Switch to your `main` branch. 4. Copy the compiled `scripts/` directory to the root of the `main` branch. 5. Push the compiled branch to your remote repository. For more details on repository branches, see the [Publishing modules](/plugins/modules/publish) guide. # Module configuration Source: https://docs.lynxhub.app/plugins/modules/configuration Configure metadata and settings for modules. To publish and distribute your card module, you must place two registry files and an icon at the root of your repository's `metadata` branch. These files are `metadata.json`, `versioning.json`, and `icon.png`. *** ## metadata.json The `metadata.json` file contains the primary identity and descriptive information for your card module. ### Structure The file must follow this structure: * `id` (string): The unique identifier of your module. It must use lowercase alphanumeric characters and hyphens. * `title` (string): The display name of your module shown in the LynxHub registry. * `description` (string): A short sentence describing what your module does. * `type` (string): Must be set to `"module"`. ### Example Here is an example `metadata.json` file: ```json theme={null} { "$schema": "https://raw.githubusercontent.com/KindaBrazy/LynxHub-Statics/refs/heads/schema/plugins/metadata.schema.json", "id": "offline-container", "title": "Offline Container", "description": "A collection of local AI tools with full support for arguments, configurations, and extensions.", "type": "module" } ``` *** ## versioning.json The `versioning.json` file tracks your release history, compatible engine ranges, supported operating systems, and version changelogs. LynxHub reads this file to resolve target versions and download updates. ### Structure The file consists of two root arrays: #### versions An array of release version objects. Each version object contains: * `version` (string): The semantic version of the release (e.g., `"14.2.0"`). * `commit` (string): The exact Git commit hash of the release on your `main` branch. * `engines` (object): * `moduleApi` (string): The semver compatibility range of the LynxHub module API (e.g., `"^2.1.0"`). * `stage` (string): The release distribution channel. Use `"public"`, `"early_access"`, or `"insider"`. * `platforms` (array): The target operating systems. Supported values are `"win32"`, `"linux"`, and `"darwin"`. #### changes An array of change logs for each release version. Each changelog object contains: * `version` (string): The semantic version this changelog applies to. * `date` (string): The release date formatted as `YYYY-MM-DD`. * `items` (array): A list of category objects containing change descriptions. Typical categories are `🚀 New Features`, `🛠️ Improvements`, and `🐛 Bug Fixes`. ### Example Here is an example `versioning.json` file: ```json theme={null} { "versions": [ { "version": "14.2.0", "commit": "abcdef1234567890abcdef1234567890abcdef12", "engines": { "moduleApi": "^2.1.0" }, "stage": "public", "platforms": ["win32", "linux", "darwin"] } ], "changes": [ { "version": "14.2.0", "date": "2026-06-19", "items": [ { "🚀 New Features": ["Initial release of the card module."] } ] } ] } ``` *** ## icon.png The `icon.png` file is the display icon for your card module in the LynxHub registry. ### Requirements * **Filename**: Must be named exactly `icon.png`. * **Location**: Place it at the root of your `metadata` branch. * **Format**: PNG format. * **Dimensions**: Use a square image (e.g., 512x512 pixels). # Module Examples Source: https://docs.lynxhub.app/plugins/modules/examples Reference implementation and code samples for modules. The Offline Container repository serves as the reference implementation for building and integrating modules. Explore the Offline Container codebase to see how modules are structured, registered, and executed. ## Reference implementation The [Offline Container](https://github.com/KindaBrazy/LynxHub-Module-Offline-Container) repository contains the integration code for local AI tools such as image generators, text generation LLMs, and audio synthesis tools. ### Core structure * **Entry point**: The `src/main.ts` file acts as the main entry point. It registers all available modules with LynxHub. * **Renderer script**: The `src/renderer.ts` file handles the frontend module scripts. * **Module configuration**: The `package.json` file configures dependencies and build configurations using Rollup. ### Module registration Each module registers an identifier and its lifecycle methods. The following code snippet shows how modules are registered in `src/main.ts`: ```typescript theme={null} import {MainModules, MainModuleUtils} from '../../src/common/types/plugins/modules'; import Comfy_MM from './Container/Image/ComfyUI (comfyanonymous)/MainMethods'; export default async function initialModule(utils: MainModuleUtils): Promise { return [ { id: 'comfyui', methods: () => Comfy_MM(utils), }, ]; } ``` ### Key utilities The `utils` parameter provides helpers to interact with the host application: * `utils.logger`: Logs messages to the central console. * `utils.processManager`: Manages the lifecycle of child processes. * `utils.settings`: Accesses user-defined module configurations. # Card main methods Source: https://docs.lynxhub.app/plugins/modules/main/card-methods Implement backend methods to handle execution commands, cache arguments, and clean up files. Backend card methods control how LynxHub runs your CLI processes, reads saved arguments from disk files, and handles uninstallation cleanups. *** ## Execution commands * **Method**: `getRunCommands(): Promise` * **Responsibility**: Invoked when the user clicks the card launch button. It must return a terminal command string (or array of strings) to execute in the pseudo-terminal. ### Example ```javascript theme={null} async () => { const installDir = utils.getInstallDir('my-card-id'); const preCommands = utils.getExtensions_TerminalPreCommands('my-card-id'); // Read saved configurations let argsString = ''; try { const savedArgs = (await utils.storage.get('my_arguments_key')) || []; argsString = savedArgs.map(arg => `${arg.name} ${arg.value}`).join(' '); } catch { // Fallback if key does not exist } return [`cd /d "${installDir}"`, ...preCommands, `python main.py --host 127.0.0.1 ${argsString}`]; }; ``` *** ## Arguments cache If you prefer to cache argument configurations in a local config file rather than standard database storage, implement the argument cache methods: * **Methods**: `readArgs(): Promise` and `saveArgs(args: ChosenArgument[]): Promise` * **Responsibility**: Reads configuration parameters from disk files, and saves chosen settings back to the configuration files. ### Example ```javascript theme={null} const methods = { readArgs: async () => { const configDir = utils.getConfigDir(); // Read and parse options from settings.json inside configDir return []; }, saveArgs: async args => { const configDir = utils.getConfigDir(); // Write the args array to disk inside configDir }, }; ``` *** ## Custom IPC listeners * **Method**: `mainIpc(): void` * **Responsibility**: Invoked during application IPC configuration. Use this to bind message handlers between frontend pages and backend modules. ### Example ```javascript theme={null} () => { utils.ipc.handle('module:get-status', async () => { return {status: 'idle'}; }); }; ``` *** ## Lifecycle events Implement lifecycle state checks to tell the host app if card files exist, if updates are available, or to clean up files on removal: * `isInstalled(): Promise` - Check if files are located in the installation folder. * `updateAvailable(): Promise` - Checks if a Git pull is available in the installation folder. * `uninstall(): Promise` - Runs file removals, deletes dependencies, and clears cache on card deletion. ### Example ```javascript theme={null} const lifecycle = { isInstalled: async () => { const runFolder = utils.getInstallDir('my-card-id'); return runFolder !== undefined; }, updateAvailable: async () => { const runFolder = utils.getInstallDir('my-card-id'); if (!runFolder) return false; return utils.isPullAvailable(runFolder); }, uninstall: async () => { const runFolder = utils.getInstallDir('my-card-id'); if (runFolder) { await utils.trashDir(runFolder); } }, }; ``` # Main backend overview Source: https://docs.lynxhub.app/plugins/modules/main/overview Understand the environment and configuration of the LynxHub module backend. Module backends run code in the Electron Main process. This allows you to utilize Node.js API suites, interact with local operating systems, and manage subprocess terminals. *** ## Environment details The extension backend runs inside a Node.js process managed directly by Electron. Because it runs in the main thread, it has unrestricted access to the local machine: * Run filesystem operations (`fs-extra`). * Intercept and manage host IPC channels. * Spawn pseudo-terminals (`node-pty`) for CLI tools. *** ## Entrypoint configuration Your backend script must build into `scripts/main.mjs` as an ES module. The host application imports it during startup. Your entrypoint file must export a default async function that initializes your cards: ```typescript theme={null} import {MainModules, MainModuleUtils} from '../../src/common/types/plugins/modules'; export default async function initialModule(utils: MainModuleUtils): Promise { return [ { id: 'my-card-id', methods: () => ({ // Backend card methods }), }, ]; } ``` ### Module registration array The returned array contains `MainModules` objects mapping each module ID to its main process methods: ```typescript theme={null} export type MainModules = { id: string; methods: () => CardMainMethods; }; ``` # Main process utilities Source: https://docs.lynxhub.app/plugins/modules/main/utilities Access host managers, database storage, and shell terminal helpers. The `utils` parameter contains references to configuration managers, database layers, system trays, and terminal controllers. *** ## Storage database (`storage`) A simple key-value database wrapper that reads and writes custom settings data: * `get(key: string): T` - Retrieves custom data values. * `set(key: string, data: T): void` - Saves custom configuration values. ```typescript theme={null} // Read custom configuration const apiKey = utils.storage.get('my-api-key'); // Save configuration updates utils.storage.set('my-api-key', 'secure-key-string'); ``` *** ## Inter-process communication (`ipc`) A simplified IPC utility to register listeners or emit events to the renderer process: * `handle(channel: string, listener: (event: any, ...args: any[]) => any): void` - Registers an IPC invocation handler (equivalent to `ipcMain.handle`). * `on(channel: string, listener: (event: any, ...args: any[]) => void): void` - Listens to events (equivalent to `ipcMain.on`). * `send(channel: string, ...args: any[]): void` - Dispatches events directly to the frontend window. ```typescript theme={null} // Handle frontend invocations utils.ipc.handle('module:check-gpu', async () => { return {gpuAvailable: true}; }); ``` *** ## Pseudo-terminals (`pty`) The `utils.pty` property exposes the native `node-pty` package directly. Use it to spawn sub-shells or python executors inside interactive terminal terminals: ```typescript theme={null} const ptyProcess = utils.pty.spawn('python', ['app.py'], { name: 'xterm-color', cols: 80, rows: 24, cwd: '/project-dir', }); ``` *** ## Directory resolution * `getInstallDir(id: string): string | undefined` - Returns the absolute installation path of the card. * `getConfigDir(): string | undefined` - Returns the main application configurations folder path. *** ## File and Git management * `trashDir(dir: string): Promise` - Moves a directory to the operating system recycle bin. * `removeDir(dir: string): Promise` - Deletes a directory permanently. * `isPullAvailable(dir: string): Promise` - Checks if a directory contains a valid Git repository with pull updates. * `pullDir(dir: string, showTaskbarProgress?: boolean): Promise` - Triggers a Git pull inside the target directory. *** ## Extension pre-commands compatibility * `getExtensions_TerminalPreCommands(id: string): string[]` - Gathers startup shell commands (such as Conda activations or path configurations) registered by active extensions for the target card ID. Always prepend these commands to your execution array. # Publishing modules Source: https://docs.lynxhub.app/plugins/modules/publish Publish card modules to the LynxHub plugin repository. To share your card module with the community and make it installable directly within LynxHub, you must submit it to the official LynxHub plugin registry. Before publishing, ensure you complete local verification and configure your repository branches correctly. *** ## Prerequisites Before submitting your module for publication, you must verify that all features work as expected. 1. **Read testing guidelines**: Familiarize yourself with the local testing flows in the [Testing modules](/plugins/modules/test) guide. 2. **Read building guidelines**: Learn how to compile and structure the compiled files in the [Building modules](/plugins/modules/build) guide. 3. **Verify locally**: Install the compiled production build of your module into your local `Plugins/` folder. Ensure it boots and runs correctly without any runtime errors. *** ## Set up the compiled branch (`main`) The registry crawler accesses the `main` branch to retrieve the compiled production assets. You must keep this branch clean of raw source code. 1. Create a branch named `main` in your repository. 2. Copy the compiled `scripts/` directory to the root of the `main` branch. 3. Commit and push the compiled files to the `main` branch. *** ## Set up the `metadata` branch The LynxHub registry crawler scans module repositories to fetch versioning and compatibility data. You must expose this metadata on a dedicated branch. 1. Create a branch named `metadata` in your module repository. 2. Add the required `metadata.json` and `versioning.json` configuration files to the root of the branch. 3. Ensure the structure of these files matches the specifications in the [Module configuration](/plugins/modules/configuration) guide. 4. Ensure the `commit` hashes specified in `versioning.json` match the release commits on your `main` branch. *** ## Register your module repository To register your module in the global repository list, add it to the statics registry: 1. Visit the [LynxHub Statics repository](https://github.com/KindaBrazy/LynxHub-Statics). 2. Fork the repository. 3. Switch to the `source` branch in your fork. 4. Open the `plugins.json` file located at the root of the project. 5. Append your module's repository base URL to the JSON array. For example: ```json theme={null} [ "https://github.com/KindaBrazy/LynxHub-Module-Offline-Container", "https://github.com/KindaBrazy/LynxHub-Python-Toolkit", "https://github.com/KindaBrazy/LynxHub-Hardware-Monitor", "https://github.com/KindaBrazy/LynxHub-Custom-Actions", "https://github.com/your-username/your-module-repo" ] ``` 6. Commit the change and push to your fork. 7. Open a **Pull Request** targeting the `source` branch of `KindaBrazy/LynxHub-Statics`. Once the Pull Request is reviewed and merged, LynxHub's automated workflows will crawl your `metadata` branch, pull the release information, and register the module in the application database. # Module quick start Source: https://docs.lynxhub.app/plugins/modules/quick-start Build and run a basic LynxHub card module in minutes. LynxHub card modules represent standalone features or tools shown as **cards** on the dashboard. Unlike extensions, modules run isolated CLI scripts, backend commands, and custom installation wizards. Follow this guide to create and test a simple card module. *** ## Clone the starter template To start developing quickly, first clone the host LynxHub application repository and navigate to its root folder. Then, clone the `source` branch of the official [LynxHub Module Template](https://github.com/KindaBrazy/LynxHub-Module-Template) directly into the `module` folder: ```bash theme={null} # Clone the host application git clone https://github.com/KindaBrazy/LynxHub cd LynxHub # Clone the card module template source branch into the /module directory git clone -b source https://github.com/KindaBrazy/LynxHub-Module-Template module ``` This sets up the following folder structure: ``` module/ ├── rollup.config.mjs # Bundler configuration ├── tsconfig.json # TypeScript compiler configuration ├── package.json # Identity and Node.js module configuration └── src/ ├── main.ts # Main process backend entrypoint ├── renderer.ts # Renderer process UI entrypoint ├── Constants.ts # Shared constants (such as card ID) ├── ComfyUI/ # ComfyUI-specific implementation (arguments, methods) └── Utils/ # Useful starter helper utilities ``` The template provides a fully working implementation of a **ComfyUI** card module. Under the `src/Utils/` folder, you will find starter utilities for process checks, Python/environment discovery, NPM package installation, and renderer installation/updating steppers. *** ## Define package configuration Open the `package.json` file at the root of your `module` folder and customize your card module's name, author information, and repository links. ```json theme={null} { "name": "lynxhub-module-template", "version": "1.0.0", "author": { "name": "Your Name", "url": "https://github.com/your-username" }, "homepage": "https://github.com/your-username/lynxhub-module-template", "repository": "https://github.com/your-username/lynxhub-module-template", "type": "module", "dependencies": { "which": "^7.0.0" }, "devDependencies": { "@rollup/plugin-commonjs": "^29.0.3", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-typescript": "^12.3.0", "@types/which": "^3.0.4", "rimraf": "^6.1.3", "rollup": "^4.62.2", "tslib": "^2.8.1" } } ``` Card module metadata (`metadata.json`) and release version history (`versioning.json`) must be placed in a dedicated `metadata` branch of your repository. They are not stored in the `source` branch. For more details, see the [Module configuration](/plugins/modules/configuration) and [Publishing modules](/plugins/modules/publish) guides. *** ## Implement the main process Open the `src/main.ts` file. This file registers the backend lifecycle methods of your module. The template uses a helper module `Comfy_MM` to specify commands and checks. ```typescript theme={null} import {MainModules, MainModuleUtils} from '../../src/common/types/plugins/modules'; import Comfy_MM from './ComfyUI/MainMethods'; import {COMFYUI_ID} from './Constants'; export default async function initialModule(utils: MainModuleUtils): Promise { return [{id: COMFYUI_ID, methods: () => Comfy_MM(utils)}]; } ``` *** ## Implement the renderer process Open the `src/renderer.ts` file. This file defines the metadata, settings, arguments, and installation wizards for the user interface. ```typescript theme={null} import {CardModules} from '../../src/common/types/plugins/modules'; import comfyuiArguments from './ComfyUI/Arguments'; import COMFYUI_RM from './ComfyUI/RendererMethods'; import {COMFYUI_ID} from './Constants'; const rendererModules: CardModules = [ { routePath: 'imageGen_page', cards: [ { id: COMFYUI_ID, title: 'ComfyUI', description: 'This ui will let you design and execute advanced stable diffusion pipelines' + ' using a graph/nodes/flowchart based interface.', repoUrl: 'https://github.com/comfyanonymous/ComfyUI', type: 'image', supportCustomArguments: true, arguments: comfyuiArguments, methods: COMFYUI_RM, installationType: 'git', }, ], }, ]; export default rendererModules; ``` *** ## Run and test locally Start the LynxHub host application in development mode: ```bash theme={null} npm run dev ``` The host application automatically detects the `/module` directory and imports your card definitions. In this template, the ComfyUI card is registered under the **Image Generation** category. * Locate the **ComfyUI** card on the dashboard. * Click the launch button to test execution. * A terminal tab will boot up automatically and run your configured run commands. * Run typecheck validation scripts to verify code health: ```bash theme={null} npm run typecheck ``` # Card renderer methods Source: https://docs.lynxhub.app/plugins/modules/renderer/card-methods Implement frontend methods to manage addresses, arguments, and card details. Frontend card methods handle user configuration changes, parse terminal feeds for server URLs, and populate description fields in card details modals. *** ## Server URL detection LynxHub launches a companion browser tab automatically when a card starts its server process. To enable this, your card must inspect the console stream and locate the active address. * **Method**: `catchAddress(line: string): string | undefined` * **Responsibility**: Checks each incoming line of terminal output. Returns the web address (URL) if discovered. ### Example ```javascript theme={null} line => { const match = line.match(/Running on local URL:\s+(http:\/\/127\.0\.0\.1:\d+)/); return match ? match[1] : undefined; }; ``` *** ## Arguments serialization If your card supports custom parameters, you must implement serialization methods. These serialize argument states into command parameters and reconstruct argument views from saved commands. * **Methods**: `parseArgsToString(args: ChosenArgument[]): string` and `parseStringToArgs(args: string): ChosenArgument[]` * **Responsibility**: Converts the React UI arguments state array into a single execution string, and parses configuration strings back into the UI state. ### Example ```javascript theme={null} const methods = { parseArgsToString: args => { return args.map(arg => `${arg.name} ${arg.value}`).join(' '); }, parseStringToArgs: str => { // Reconstruct arguments array from saved string return []; }, }; ``` *** ## Card details modal You can populate metadata and status details dynamically when users open a card's info dialog. * **Method**: `cardInfo(api: CardInfoApi, callback: CardInfoCallback): void` * **Responsibility**: Queries the local environment using the helper `api` parameter and pushes formatted fields to the view via the `callback` interface. ### Example ```javascript theme={null} async (api, callback) => { const folderSize = await api.getFolderSize(api.installationFolder); const gitTag = await api.getCurrentReleaseTag(api.installationFolder); callback.setDescription([ { title: 'Storage Details', items: [ {label: 'Size on Disk', result: `${(folderSize / (1024 * 1024)).toFixed(2)} MB`}, {label: 'Release Version', result: gitTag}, ], }, ]); }; ``` # Installation stepper wizard Source: https://docs.lynxhub.app/plugins/modules/renderer/installation-stepper Build installation and update workflows using the host stepper wizard. If your card requires repository downloading, virtual environment creation, or manual setup, define a custom wizard flow. The host runs this flow inside the central installation modal. *** ## Stepper lifecycle To attach a wizard to a card, register the `manager` property in your renderer card definition: ```typescript theme={null} const card = { id: 'my-card', // ... other properties methods: { manager: { startInstall: async (stepper: InstallationStepper) => { // Wizard flow logic }, updater: { updateType: 'stepper', // 'git' | 'stepper' startUpdate: async (stepper: InstallationStepper, dir?: string) => { // Wizard update logic }, }, }, }, }; ``` *** ## InstallationStepper API The `stepper` parameter exposes UI actions to step through setup stages, request user choices, run commands, and update progress bars. ### Progress and wizard navigation * `initialSteps(stepTitles: string[]): void` - Configures the titles of your installation steps on the timeline. * `nextStep(): Promise` - Advances the wizard to the next stage in the timeline. * `progressBar(isIndeterminate: boolean, title?: string, percentage?: number, description?: { label: string; value: string }[]): void` - Customizes the current progress bar message. * `showFinalStep(resultType: 'success' | 'error', title: string, description?: string): void` - Ends the installer flow with a final status window. ### User interaction * `starterStep(options?: StarterStepOptions): Promise` - Displays the default setup page. Resolves to the user's choice: `{ chosen: 'install' | 'locate', targetDirectory: string }`. * `collectUserInput(inputFields: UserInputField[], title?: string): Promise` - Renders a form requesting text inputs, dropdown selections, or checkmarks. ### Filesystem and terminal execution * `cloneRepository(url: string): Promise` - Clones a Git repository. Returns the destination folder path. * `runTerminalScript(dir: string, script: string): Promise` - Executes a script file (e.g. `.bat`, `.sh`) inside a separate terminal overlay. Resolves when the script finishes. * `executeTerminalCommands(commands: string | string[], dir?: string): Promise` - Runs inline CLI commands inside a terminal overlay. Resolves when commands finish. * `downloadFileFromUrl(url: string): Promise` - Downloads a file to the user's downloads folder. Returns the local path. * `setInstalled(dir?: string): void` - Saves the target installation path and registers the card as installed. * `setUpdated(): void` - Flags the card update as complete. *** ## Step-by-step example Below is a complete installation flow that configures stages, downloads a repository, prompts for options, runs an environment compiler, and completes the setup: ```javascript theme={null} async stepper => { try { // 1. Register step names stepper.initialSteps(['Choose Folder', 'Download Files', 'Setup Environment', 'Finish']); // 2. Locate or select the installation path const setup = await stepper.starterStep(); await stepper.nextStep(); // 3. Clone repository files stepper.progressBar(true, 'Downloading codebase...'); const targetDir = await stepper.cloneRepository('https://github.com/example/model-repo'); await stepper.nextStep(); // 4. Prompt the user for graphics settings const inputs = await stepper.collectUserInput( [{id: 'cuda', label: 'Enable GPU (CUDA) acceleration?', type: 'checkbox', defaultValue: true}], 'Hardware Configuration', ); // 5. Run installation terminal script stepper.progressBar(true, 'Configuring environments...'); const useCuda = inputs.find(i => i.id === 'cuda')?.result; const installScript = useCuda ? 'install_gpu.bat' : 'install_cpu.bat'; await stepper.runTerminalScript(targetDir, installScript); await stepper.nextStep(); // 6. Complete installation stepper.setInstalled(targetDir); stepper.showFinalStep('success', 'Installation Completed!', 'Your card is ready to run.'); } catch (err) { stepper.showFinalStep('error', 'Installation Failed', err.message); } }; ``` # Renderer frontend overview Source: https://docs.lynxhub.app/plugins/modules/renderer/overview Understand the configuration and API environment of the LynxHub module frontend. Modules define card visuals, launch configurations, and installer wizards. They run in the Chromium renderer process. Unlike extensions (which allow you to write custom React views and styling), modules are purely configuration-driven. The host application manages all layout, visual styling, rendering, and global state (using its internal React, Tailwind, HeroUI, Redux, and Zustand architectures). You do **not** write React components, handle styling, or interact with state stores directly. Instead, you declare card definitions, argument schemas, and lifecycle steps, and invoke host-provided APIs to coordinate wizard views or update settings. *** ## Entrypoint configuration Your module frontend must build into `scripts/renderer.mjs` as an ES module. The host application imports it during startup using a custom local protocol. Your entrypoint file must export a default array matching the `CardModules` structure: ```typescript theme={null} import {CardModules} from '../../src/common/types/plugins/modules'; const rendererModules: CardModules = [ // Page configuration and card definitions ]; export default rendererModules; ``` *** ## Page routing placement The default export array groups cards by their destination dashboard page route. Set `routePath` to one of the following available page IDs to specify where the card appears: | Page ID (`routePath`) | Destination dashboard section | | :-------------------- | :---------------------------- | | `home_page` | **Home** dashboard | | `imageGen_page` | **Image Generation** category | | `textGen_page` | **Text Generation** category | | `audioGen_page` | **Audio Generation** category | | `tools_page` | **Tools** category | | `games_page` | **Games** category | | `agents_page` | **Agents** category | | `others_page` | **Others** category | | `dashboard_page` | **Dashboard** | | `plugins_page` | **Plugins** management page | | `settings_page` | **Settings** panel | # Post-installation configuration Source: https://docs.lynxhub.app/plugins/modules/renderer/post-install Automate extensions installation and configure launch parameters after setup. Once you flag a card as successfully installed, you can execute post-setup procedures. These include downloading supplementary plugins, enabling automatic updates, setting startup argument presets, or defining launch behaviors. Access these configurations using the `stepper.postInstall` namespace. Call them after invoking `stepper.setInstalled(targetDir)` in your installation stepper script. *** ## Install supplementary extensions If your card supports extension directories (e.g. ComfyUI or Stable Diffusion WebUI), you can automate cloning a list of extensions during the installation wizard. * **Method**: `installExtensions(extensionURLs: string[], extensionsDir: string): Promise` * **Responsibility**: Clones an array of Git repository URLs to the specified target directory. ### Example ```javascript theme={null} // Flag card as installed first stepper.setInstalled(targetDir); // Define target extension folder and source URLs const extensionsFolder = `${targetDir}/custom_nodes`; const repositories = ['https://github.com/ltdrdata/ComfyUI-Manager', 'https://github.com/pythongosssss/ComfyUI-Custom-Scripts']; stepper.progressBar(true, 'Installing extensions...'); await stepper.postInstall.installExtensions(repositories, extensionsFolder); ``` *** ## Configure WebUI settings Configure auto-update policies, pre-launch dependencies, execution arguments, and browser parameters. * **Method**: `config(configs: ModuleConfigOptions): void` * **Responsibility**: Saves launch and configuration presets for the card. ### Enable automatic updates Configure whether the card or its installed extensions should check for and apply updates automatically on startup. ```javascript theme={null} stepper.postInstall.config({ autoUpdateCard: true, // Pull updates for the WebUI itself autoUpdateExtensions: true, // Pull updates for all installed extensions }); ``` ### Pre-defined arguments presets You can register custom CLI argument configurations for your card. ```javascript theme={null} stepper.postInstall.config({ customArguments: { presetName: 'Low VRAM GPU', customArguments: [ {name: '--lowvram', value: ''}, // Empty string for boolean parameters {name: '--precision', value: 'full'}, ], }, }); ``` ### Custom launch commands If you need to execute a custom CLI sequence when running the card, define `customCommands` to override the default runner commands. ```javascript theme={null} stepper.postInstall.config({ customCommands: ['call .venv\\Scripts\\activate.bat', 'python main.py --listen 0.0.0.0 --port 8188'], }); ``` ### Pre-launch commands and path opening Execute setup shell commands or open folder locations in the OS file explorer before booting the card process. ```javascript theme={null} stepper.postInstall.config({ preLaunch: { preCommands: ['echo "Booting server process..."', 'conda activate base'], openPath: [{path: targetDir, type: 'folder'}], }, }); ``` # Testing modules Source: https://docs.lynxhub.app/plugins/modules/test Run and test your custom modules in development and production environments. You can test LynxHub card modules during development or after compiling them for production. ## Development mode testing LynxHub automatically boots your module in development mode when the codebase exists in the project root. 1. Place your module project directory in the LynxHub repository root. 2. Name the folder `module`. 3. Run the development server from the LynxHub root directory: ```bash theme={null} npm run dev ``` The application detects the `/module` directory. It automatically loads your module in development mode. The main process dynamically imports your code from `module/src/main.ts`. The renderer process imports your code from `@lynx_module/renderer`. ### Inspector and logs You can inspect components and debug card behavior using the Electron developer tools. * Press `Ctrl+Shift+I` or `F12` to open the developer tools. * You can inspect card DOM elements, verify visual rendering, and debug frontend behavior. * The main process prints backend logs directly to your terminal window. * Vite applies styling changes instantly using Hot Module Replacement (HMR). * You must restart the application if you modify backend files or card API definitions. *** ## Static validation You should compile the project to check for typescript errors. To run typescript type checking across the project including your module, run the check script in the LynxHub root directory: ```bash theme={null} npm run typecheck ``` This command validates that your TypeScript files compile without type errors. *** ## Production testing You should test that your compiled module loads correctly in production mode before distributing it. 1. Build your module for production. 2. Manually copy the compiled files to your local `Plugins` directory. 3. Launch LynxHub to verify that the module cards load without errors. 4. Read the [Building modules](/plugins/modules/build) guide for detailed installation steps. # Plugin system overview Source: https://docs.lynxhub.app/plugins/overview Understand LynxHub plugin capabilities and learn when to build an extension or a module. LynxHub provides a powerful plugin system that allows you to extend application capabilities. You can build plugins to integrate new AI engines, customize the user interface, or automate developer workflows. The plugin system supports two types of integrations: **modules** and **extensions**. *** ## Core concepts ### What is a module? Modules represent standalone features or AI engines. They are displayed as interactive **cards** inside the LynxHub interface. Examples include Stable Diffusion, ComfyUI, or local llama.cpp models. * **Purpose**: Integrate external tools, scripts, or local binaries that require dedicated configuration and execution. * **UI style**: Appears as a self-contained card under specific tabs (such as image generation or text generation). It features argument inputs, installation wizards, and terminal status monitors. * **Process architecture**: Modules run backend processes (such as Python scripts) in the background via a terminal shell. The host process controls execution and handles status tracking. ### What is an extension? Extensions are dynamic plugins designed to integrate deeply with the LynxHub core client shell. * **Purpose**: Add custom pages, modify the layout, listen to global events, inject state management, or add system tray widgets. * **UI style**: Injects React components directly into predefined host layout slots (such as the Title Bar, Status Bar, Settings page, or Sidebar navigation). * **Process architecture**: Runs continuously in both the main process (Node.js) and the renderer process (Chromium). The renderer elements load dynamically at runtime using Webpack Module Federation. *** ## Comparative matrix Use this table to understand the key differences between modules and extensions: | Feature | Modules | Extensions | | :----------------------- | :------------------------------------------ | :-------------------------------------------------- | | **Primary Focus** | Standalone AI tool or script execution | Deep shell UI integration and customization | | **UI Integration** | Standardized card in category routes | Predefined slots, custom routes, tray, or title bar | | **Core Technology** | Rollup + ES modules | Vite + Webpack Module Federation | | **Lifecycle** | Started and stopped by the user | Runs continuously in the host background | | **State Management** | Handled by host Zustand and Redux stores | Can inject custom Redux reducers and middleware | | **Terminal Integration** | Built-in stream output to host terminal tab | Manages background tasks or sub-processes manually | *** ## Choosing the right plugin type Select the integration model that best fits your requirements: ### Choose a module if: * You want to bring an existing AI model, tool, or console script (such as ComfyUI or a custom Python script) into LynxHub. * You want LynxHub to automatically manage installation (such as `git clone` or `pip install`), updates, and process execution. * Your tool fits nicely into a standardized card interface with configuration parameters. ### Choose an extension if: * You need to add a brand-new top-level tab or custom route outside the standard card-style pages. * You need to modify the main frame layout, such as putting a status widget in the title bar or adding custom menu items to the tray. * You want to hook into global application lifecycle events, state changes, or listen to all window events. *** ## Next steps Ready to start building? Refer to the quick start guides: Create user interface elements and widgets to enhance the LynxHub workspace. Build backend integrations and connect custom services to your client.