{"id":46506394,"url":"https://github.com/Michael-Matta1/zsh-edit-select","last_synced_at":"2026-03-08T00:01:19.794Z","repository":{"id":324977967,"uuid":"1098968597","full_name":"Michael-Matta1/zsh-edit-select","owner":"Michael-Matta1","description":"Edit your Zsh command line like a text editor, with Shift/Shift+Ctrl selection, mouse support, type-to-replace editing, clipboard-aware copy/cut/paste, and fully customizable keybindings.","archived":false,"fork":false,"pushed_at":"2026-03-02T15:09:47.000Z","size":17705,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-02T17:52:42.976Z","etag":null,"topics":["edit-selected-cells","editor-plugin","editor-theme","kitty","linux","linux-shell","mouse-tracking","shell","shift-select","terminal","zsh","zsh-configuration","zsh-plugin","zsh-plugins","zsh-widget","zshell"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Michael-Matta1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-18T11:33:05.000Z","updated_at":"2026-03-02T15:13:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Michael-Matta1/zsh-edit-select","commit_stats":null,"previous_names":["michael-matta1/zsh-edit-select"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/Michael-Matta1/zsh-edit-select","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Michael-Matta1%2Fzsh-edit-select","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Michael-Matta1%2Fzsh-edit-select/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Michael-Matta1%2Fzsh-edit-select/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Michael-Matta1%2Fzsh-edit-select/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Michael-Matta1","download_url":"https://codeload.github.com/Michael-Matta1/zsh-edit-select/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Michael-Matta1%2Fzsh-edit-select/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30238065,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T23:52:25.683Z","status":"ssl_error","status_checked_at":"2026-03-07T23:52:25.373Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["edit-selected-cells","editor-plugin","editor-theme","kitty","linux","linux-shell","mouse-tracking","shell","shift-select","terminal","zsh","zsh-configuration","zsh-plugin","zsh-plugins","zsh-widget","zshell"],"created_at":"2026-03-06T15:00:39.314Z","updated_at":"2026-03-08T00:01:19.774Z","avatar_url":"https://github.com/Michael-Matta1.png","language":"Shell","funding_links":[],"categories":["Plugins"],"sub_categories":["ZSH on Windows"],"readme":"# Zsh Edit-Select\n\nZsh plugin that lets you edit your command line like a text editor. Select text with Shift + Arrow keys or the\nmouse, type or paste to replace selections, use standard editing shortcuts (copy, cut, paste, undo, redo,\nselect all), and customize keybindings through an interactive wizard — with full X11 and Wayland clipboard\nsupport.\n\n[demo video](https://github.com/user-attachments/assets/a024e609-1de1-4608-a7c3-e17264162904)\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eIf the demo video is unavailable, click here to view the GIF.\u003c/b\u003e\u003c/summary\u003e\n\n![Demo](assets/demo.gif)\n\n\u003c/details\u003e\n\n---\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Features](#features)\n- [Auto Installation](#auto-installation)\n- [Manual Installation](#manual-installation)\n- [Configuration Wizard](#configuration-wizard)\n- [Terminal Setup](#terminal-setup)\n  - [Step 1: Configure Copy Shortcut](#step-1-configure-copy-shortcut)\n  - [Step 2: Configure Undo and Redo Shortcut](#step-2-configure-undo-and-redo-shortcut)\n  - [Step 3: Enable Shift Selection Keys](#step-3-enable-shift-selection-keys)\n- [Wayland Support](#wayland-support)\n- [Default Key Bindings Reference](#default-key-bindings-reference)\n- [Troubleshooting](#troubleshooting)\n- [Platform Compatibility](#platform-compatibility)\n- [Performance-Optimized Architecture](#performance-optimized-architecture)\n- [Contributing](#contributing)\n- [License](#license)\n- [Acknowledgments](#acknowledgments)\n- [References](#references)\n\n---\n\n## Overview\n\n**Zsh Edit-Select** brings familiar text editor behaviors to your Zsh command line:\n\n- ✅ **Shift selection** — Select text using Shift + Arrow keys\n- ✅ **Type-to-replace** — Type over selected text to replace it\n- ✅ **Paste-to-replace** — Paste clipboard content over selections\n- ✅ **Mouse integration** — Works with text selected by mouse\n- ✅ **Clipboard integration** — Works with X11 and Wayland\n- ✅ **Standard shortcuts** — Ctrl+A (select all), Ctrl+C (copy), Ctrl+X (cut), Ctrl+V (paste), Ctrl+Z (undo),\n  Ctrl+Shift+Z (redo)\n\n\u003e **Customization:** The plugin works after installation with editor-like defaults. Use the command\n\u003e `edit-select config` to customize mouse behavior and keybindings.\n\n---\n\n## Features\n\n### Keyboard Selection\n\nSelect text using familiar keyboard shortcuts:\n\n| Shortcut                    | Action                                          |\n| --------------------------- | ----------------------------------------------- |\n| **Shift + ←/→**             | Select character by character                   |\n| **Shift + ↑/↓**             | Select line by line                             |\n| **Shift + Home/End**        | Select to line start/end                        |\n| **Shift + Ctrl + ←/→**      | Select word by word                             |\n| **Shift + Ctrl + Home/End** | Select to buffer start/end                      |\n| **Ctrl + A**                | Select all text (including multi-line commands) |\n\n### Mouse Selection Integration\n\nThe plugin intelligently integrates mouse selections:\n\n**When Mouse Replacement is Enabled (default):**\n\n- ✅ Copy mouse selections with Ctrl+C (or Ctrl+Shift+C if configured)\n- ✅ Cut mouse selections with Ctrl+X\n- ✅ Type to replace mouse selections\n- ✅ Delete mouse selections with Backspace/Delete\n- ✅ Paste over mouse selections with Ctrl+V\n\n**When Mouse Replacement is Disabled:**\n\n- ✅ Copy mouse selections with Ctrl+C (or Ctrl+Shift+C if configured)\n- ✅ Replacement/Deletion work with keyboard selections\n\n\u003e **Note:** Configure mouse behavior with the command `edit-select config` → Option 1\n\n### Type-to-Replace and Paste-to-Replace\n\nType or paste while text is selected to replace it automatically.\n\nWorks with both keyboard and mouse selections (when mouse replacement is enabled).\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e⚠️ Mouse Replacement Note (Safeguard Prompt)\u003c/b\u003e\u003c/summary\u003e\n\nIf you see the message **\"Duplicate text: place cursor inside the occurrence you want to modify\"**, the plugin\nhas detected multiple identical occurrences of the selected text in your command buffer.\n\n**When does this appear?** This message only appears when:\n\n- The selection was made with the mouse, AND\n- The exact same text occurs more than once in the buffer, AND\n- You try to replace the selected text by either typing or pasting\n\n**Why does this happen?** This is a protective safeguard for the plugin's mouse-selection workaround. Since\nmouse replacement is not enabled by default, the implemented workaround cannot automatically distinguish\nbetween multiple occurrences of identical text. This prompt prevents accidental edits to the wrong occurrence\nwhen using mouse-based selection.\n\n**What should you do?** When prompted, place the cursor inside the specific occurrence you want to edit, then\nretry the operation (select it and type or paste to replace).\n\n**Note:** This safeguard is only for mouse selections. Using `Shift+Arrow keys` doesn't require caret\nreplacement and works directly without ambiguity or extra prompting.\n\n\u003c/details\u003e\n\n### Copy, Cut, and Paste\n\nStandard editing shortcuts:\n\n- **Ctrl + C** (or Ctrl+Shift+C if configured): Copy selected text\n- **Ctrl + X**: Cut selected text\n- **Ctrl + V**: Paste (replaces selection if any)\n\n\u003e **Clipboard Managers Compatibility Note:** The plugin is fully compatible with clipboard history managers\n\u003e like **CopyQ**, **GPaste**, and others. Since it uses standard X11 and Wayland clipboard protocols, all\n\u003e copied text is automatically captured by your clipboard manager.\n\n### Undo and Redo\n\nNavigate through your command line editing history:\n\n- **Ctrl + Z**: Undo last edit\n- **Ctrl + Shift + Z**: Redo last undone edit\n\n\u003e **Note:** The Ctrl+Z keybinding works seamlessly alongside the traditional suspend process functionality\n\u003e (Ctrl+Z suspends a running foreground process to background). The plugin intelligently handles undo\n\u003e operations for command line editing while preserving the ability to suspend processes when needed.\n\n\u003e **Note:** The Copy and the Redo keybinding (Ctrl+Shift+Z) requires terminal configuration to send the\n\u003e correct escape sequence. See [Terminal Setup](#terminal-setup) for manual configuration instructions, or use\n\u003e the [Auto Installation](#auto-installation) script to configure this automatically.\n\n### Clipboard Integration\n\nThe plugin includes purpose-built clipboard agents that replace external tools entirely:\n\n**Clipboard Integration Agents:** Small compiled programs built specifically for this plugin handle all\nclipboard and selection operations:\n\n| Display Server | Agent                     | Protocol                                                                                                | Performance vs. External Tool |\n| -------------- | ------------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------- |\n| **X11**        | `zes-x11-selection-agent` | XFixes extension + CLIPBOARD atom                                                                       | **44.6% faster than xclip**   |\n| **Wayland**    | `zes-wl-selection-agent`  | `zwp_primary_selection_unstable_v1` + `ext_data_control_v1` / `zwlr_data_control_v1` / `wl_data_device` | **96.4% faster than wl-copy** |\n| **XWayland**   | `zes-xwayland-agent`      | X11 XFixes through XWayland                                                                             | XWayland compatibility layer  |\n\n**External Tools (Fallback Only):**\n\n| Display Server | Tool                   | When Used                 |\n| -------------- | ---------------------- | ------------------------- |\n| **X11**        | `xclip`                | Only if agent unavailable |\n| **Wayland**    | `wl-copy` / `wl-paste` | Only if agent unavailable |\n\n\u003e The agents handle copy, paste, and clipboard operations directly through native protocols—no external tools\n\u003e needed. They run as background processes and communicate with the plugin through a fast in-memory cache,\n\u003e giving you instant clipboard response with zero subprocess overhead.\n\n\u003e See [Performance-Optimized Architecture](#performance-optimized-architecture) for benchmarks and implementation details.\n\n---\n\n## Auto Installation\n\n\u003e **Recommendation:** If you are comfortable editing dotfiles and prefer full control over your system\n\u003e configuration, [Manual Installation](#manual-installation) is the recommended approach.\n\nInstallation consists of three straightforward steps:\n\n1. install dependencies\n2. plugin to your plugin manager\n3. configure your terminal\n\nEach documented with exact commands and copy-paste configurations.\n\n- Completing all three steps should take no longer than **8 minutes** on a first install.\n- All instructions are organized in collapsed sections so you can expand only what applies to your specific\n  setup and platform.\n\nThe auto-installer is provided as a convenience for users who are less comfortable with terminal configuration\nor who prefer a fully guided, hands-off setup. It detects your environment (X11, Wayland, or XWayland),\ninstalls dependencies, sets up the plugin, and configures your terminal in a single run. It has been tested\nacross multiple distributions using Docker containers and virtual machines, and handles the most common\nconfigurations — but not every edge case can be guaranteed. If you encounter an issue, please\n[report it](https://github.com/Michael-Matta1/zsh-edit-select/issues) so it can be addressed.\n\nTo use the auto-installer, simply run:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/Michael-Matta1/zsh-edit-select/main/assets/auto-install.sh -o install.sh \u0026\u0026 chmod +x install.sh \u0026\u0026 bash install.sh\n```\n\n### Key Features\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eClick to expand\u003c/b\u003e\u003c/summary\u003e\n\nThe installer is designed for reliability and system safety:\n\n- **Idempotency**: The script checks your configuration files before making changes. It can be run multiple\n  times without creating duplicate entries or corrupting files.\n- **System Safety**: Creates timestamped backups of every file before modification. Implements standard signal\n  trapping (INT, TERM, EXIT) to ensure clean rollbacks even if interrupted.\n- **Universal Compatibility**: Supports 11 different package managers (including `apt`, `dnf`, `pacman`,\n  `zypper`, `apk`, and `nix`) across X11, Wayland, and XWayland environments.\n- **Robust Pre-flight Checks**: Validates **network connectivity**, **disk space**, and **package manager\n  health** before starting. Also proactively detects and reports broken repositories (e.g., problematic apt\n  sources) to prevent installation failures.\n\n### Automated Capabilities\n\nThe script handles the end-to-end setup process:\n\n| Category           | Automated Actions                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n| :----------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Dependencies**   | - Installs system packages (`git`, `zsh`, `gcc`, `make`, `xclip`/`wl-clipboard`)\u003cbr\u003e- Detects your OS (Debian, Fedora, Arch, etc.) and uses the correct package manager (`apt`, `dnf`, `pacman`)                                                                                                                                                                                                                                                                        |\n| **Plugin Manager** | - **Detects** your existing manager (Oh My Zsh, Zinit, Antigen, Sheldon, etc.)\u003cbr\u003e- **Offers to install Oh My Zsh** if you don't have a plugin manager. You can refuse if you prefer manual installation\u003cbr\u003e- _Note: The installer detects and installs the plugin for other managers such as Zinit or Antigen, but it does not install those managers themselves. If you prefer using them instead of OMZ, make sure they are installed before running the installer._ |\n| **Terminal Setup** | - Configures **Kitty**, **Alacritty**, **WezTerm**, **Foot**, and **VS Code** to support keybindings\u003cbr\u003e- Backs up existing config files before making changes                                                                                                                                                                                                                                                                                                          |\n| **Safeguards**     | - Checks for conflicting keybindings in your `.zshrc` and terminal configuration files (Kitty, Alacritty, WezTerm, Foot, VS Code)\u003cbr\u003e- Verifies the installation with a self-test suite                                                                                                                                                                                                                                                                                 |\n\n\u003c/details\u003e\n\n### Interactive Menu\n\nWhen run without arguments, the installer provides an interactive menu with the following options:\n\n1. **Full Installation (Recommended)**: The complete setup process. **Required for first-time installations.**\n2. **Configure Terminals Only**: Only detects and configures your terminal emulators.\n3. **Check for Conflicts Only**: Scans your configuration files for conflicting keybindings.\n4. **Update Plugin**: Pulls the latest changes from the repository.\n5. **Build Agents Only**: Rebuild clipboard agents for your display server.\n6. **Uninstall**: Remove the plugin, configuration entries, and agents.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eAdvanced Usage \u0026 Options\u003c/b\u003e\u003c/summary\u003e\n\nYou can customize the installation behavior with command-line flags. To use them, download the script first or\npass them to bash:\n\n| Option              | Description                                                                  |\n| :------------------ | :--------------------------------------------------------------------------- |\n| `--non-interactive` | Run in headless mode without user prompts (accepts all defaults)             |\n| `--skip-deps`       | Skip installing system dependencies (useful if you manage packages manually) |\n| `--skip-conflicts`  | Skip the configuration conflict detection phase                              |\n| `--skip-verify`     | Skip the post-installation verification tests                                |\n| `--test-mode`       | Allow running as root (for testing only)                                     |\n| `--help`            | Show the help message and exit                                               |\n\n**Example: Non-interactive installation (CI/CD friendly)**\n\n```bash\nbash auto-install.sh --non-interactive\n```\n\n\u003c/details\u003e\n\n### Installation Output\n\nThe script provides detailed, color-coded feedback for every step:\n\n- **✅ Success**: Step completed successfully\n- **⚠️ Warning**: Non-critical issue (e.g., optional component missing)\n- **❌ Error**: Critical failure that requires attention\n\nAt the end, you'll receive a **Summary Report** listing all installed components and any manual steps\nrequired. A detailed log is also saved to `~/.zsh-edit-select-install.log`.\n\n\u003e **Troubleshooting / Manual Preference:** If the automated installation fails or if you prefer to configure\n\u003e everything yourself, you can follow the comprehensive [Manual Installation](#manual-installation) and\n\u003e [Terminal Setup](#terminal-setup) guides below.\n\n---\n\n## Manual Installation\n\nManual installation is the recommended approach if you are comfortable with dotfiles and want complete\nvisibility and control over every change made to your system. The process consists of three steps:\n\n1. **Install build dependencies** — A one-line command for your distribution.\n2. **Install the plugin** — Clone the repository with your plugin manager and add one line to your `.zshrc`.\n3. **Configure your terminal** — Add a few keybinding entries to your terminal's config file.\n\nAll steps are fully documented with exact commands and copy-paste configuration snippets. The instructions are\norganized in collapsed sections labeled by distribution and terminal — expand only what applies to your setup.\n\n\u003e If you prefer an automated setup, the [Auto Installation](#auto-installation) script can handle all of these\n\u003e steps for you. If you run into any difficulty at any step, please\n\u003e [open an issue](https://github.com/Michael-Matta1/zsh-edit-select/issues) and it will be addressed.\n\n### 1. Prerequisites (Build Dependencies)\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eHow to check if you're using X11 or Wayland\u003c/b\u003e\u003c/summary\u003e\n\nRun this command in your terminal:\n\n```bash\necho $XDG_SESSION_TYPE\n```\n\n- If it returns `x11` → You're using X11\n- If it returns `wayland` → You're using Wayland\n\n\u003e **Note:** The plugin automatically detects your display server and loads the appropriate implementation.\n\n\u003c/details\u003e\n\nThe plugin automatically compiles native agents on first use. Install the required build tools and libraries\nfor your platform:\n\n### For X11 Users\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eDebian/Ubuntu\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nsudo apt install build-essential libx11-dev libxfixes-dev pkg-config xclip\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eArch Linux\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nsudo pacman -S --needed base-devel libx11 libxfixes pkgconf xclip\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eFedora\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nsudo dnf install gcc make libX11-devel libXfixes-devel pkgconfig xclip\n```\n\n\u003c/details\u003e\n\n### For Wayland Users\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eDebian/Ubuntu\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nsudo apt install build-essential libx11-dev libxfixes-dev libwayland-dev wayland-protocols pkg-config wl-clipboard\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eArch Linux\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nsudo pacman -S --needed base-devel libx11 libxfixes wayland wayland-protocols pkgconf wl-clipboard\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eFedora\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nsudo dnf install gcc make libX11-devel libXfixes-devel wayland-devel wayland-protocols-devel pkgconfig wl-clipboard\n```\n\n\u003c/details\u003e\n\n---\n\n### 2. Install the Plugin\n\n\u003e **Important:** Before installing, ensure you have the required\n\u003e [Build Dependencies](#1-prerequisites-build-dependencies) installed.\n\u003e\n\u003e You may use the [Auto Installation](#auto-installation) script to perform this step automatically, or\n\u003e [open an issue](https://github.com/Michael-Matta1/zsh-edit-select/issues) if you run into any difficulty.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eOh My Zsh\u003c/b\u003e\u003c/summary\u003e\n\nExpand your plugin manager:\n\n```bash\ngit clone https://github.com/Michael-Matta1/zsh-edit-select.git \\\n  ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-edit-select\n```\n\nAdd to your `.zshrc`:\n\n```bash\nplugins=(... zsh-edit-select)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003ezgenom\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nzgenom load Michael-Matta1/zsh-edit-select\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003ezinit\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nzinit light Michael-Matta1/zsh-edit-select\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003ezplug\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nzplug \"Michael-Matta1/zsh-edit-select\"\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eantigen\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nantigen bundle Michael-Matta1/zsh-edit-select\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eantibody\u003c/b\u003e \u003csub\u003e(deprecated)\u003c/sub\u003e\u003c/summary\u003e\n\n\u003e **Note:** antibody has been archived since May 2022. Consider migrating to\n\u003e [antidote](https://github.com/mattmc3/antidote), a drop-in replacement.\n\n```bash\nantibody bundle Michael-Matta1/zsh-edit-select\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003ezgen\u003c/b\u003e \u003csub\u003e(unmaintained)\u003c/sub\u003e\u003c/summary\u003e\n\n\u003e **Note:** zgen is no longer maintained. Consider migrating to [zgenom](https://github.com/jandamm/zgenom),\n\u003e its maintained successor.\n\n```bash\nzgen load Michael-Matta1/zsh-edit-select\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003esheldon\u003c/b\u003e\u003c/summary\u003e\n\n```bash\nsheldon add zsh-edit-select --github Michael-Matta1/zsh-edit-select\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eManual Installation\u003c/b\u003e\u003c/summary\u003e\n\n```bash\ngit clone https://github.com/Michael-Matta1/zsh-edit-select.git \\\n  ~/.local/share/zsh/plugins/zsh-edit-select\n\n# Add to ~/.zshrc:\nsource ~/.local/share/zsh/plugins/zsh-edit-select/zsh-edit-select.plugin.zsh\n```\n\n\u003c/details\u003e\n\n### 3. Configure Your Terminal\n\nSome terminals need configuration to support Shift selection. See [Terminal Setup](#terminal-setup) for\ndetails.\n\n### 4. Restart Your Shell\n\n```bash\nsource ~/.zshrc\n```\n\n\u003e **Important:** You may need to fully close and reopen your terminal (not just source ~/.zshrc) for all\n\u003e features to work correctly, especially in some terminal emulators.\n\n**You're ready!** Try selecting text with Shift + Arrow keys.\n\n### 5. (Optional) Customize Settings\n\nThe plugin works immediately with sensible defaults, but you can customize:\n\n- Mouse replacement behavior\n- Keybindings (Ctrl+A, Ctrl+V, Ctrl+X, Ctrl+Z, Ctrl+Shift+Z)\n\nRun the interactive configuration wizard:\n\n```bash\nedit-select config\n```\n\n---\n\n## Configuration Wizard\n\nLaunch the interactive configuration wizard:\n\n```bash\nedit-select config\n```\n\nThe wizard provides:\n\n1. **Mouse Replacement** — Enable/disable mouse selection integration\n2. **Key Bindings** — Customize Copy, Cut, Paste, Select All, Undo, Redo, and Word Navigation shortcuts\n3. **View Full Configuration** — See current settings\n4. **Reset to Defaults** — Restore factory settings\n5. **Exit Wizard** — Close the wizard\n\nAll changes are saved to `~/.config/zsh-edit-select/config` and persist across sessions.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e Mouse Replacement Modes \u003c/b\u003e\u003c/summary\u003e\n\nConfigure how the plugin handles mouse selections:\n\n**Enabled (default):**\n\n- Full integration: type, paste, cut, and delete work with mouse selections\n- Best for users who want seamless mouse+keyboard workflow\n\n**Disabled:**\n\n- Mouse selections can be copied with Ctrl+C (or Ctrl+Shift+C if configured)\n- Typing, pasting, cutting, and deleting only work with keyboard selections\n- Best for users who prefer strict keyboard-only editing\n\nChange the mode:\n\n```bash\nedit-select config  # → Option 1: Mouse Replacement\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e Keybinding Customization \u003c/b\u003e\u003c/summary\u003e\n\nCustomize the main editing shortcuts:\n\n```bash\nedit-select config  # → Option 2: Key Bindings\n```\n\n**Default bindings:**\n\n- **Ctrl + A** — Select all\n- **Ctrl + V** — Paste\n- **Ctrl + X** — Cut\n- **Ctrl + Shift + C** — Copy\n- **Ctrl + Z** — Undo\n- **Ctrl + Shift + Z** — Redo\n- **Ctrl + ←** — Word left\n- **Ctrl + →** — Word right\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eCustom Keybinding Notes (Terminal Configuration)\u003c/b\u003e\u003c/summary\u003e\n\n\u003e **⚠️ Important:** When using custom keybindings (especially with Shift modifiers), you may need to configure\n\u003e your terminal emulator to send the correct escape sequences.\n\n**To find the escape sequence for any key combination:**\n\n1. Run `cat` (without arguments) in your terminal\n2. Press the key combination\n3. The terminal will display the escape sequence\n4. Use this sequence in your configuration\n\nFor example, if you want to use `Ctrl + Shift + X` for cut add the following to your terminal dotfile:\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eKitty\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `kitty.conf`:\n\n```conf\nmap ctrl+shift+x send_text all \\x1b[88;6u\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eWezTerm\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `wezterm.lua`:\n\n```lua\nreturn {\n  keys = {\n    {\n      key = 'X',\n      mods = 'CTRL|SHIFT',\n      action = wezterm.action.SendString '\\x1b[88;6u',\n    },\n  },\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eAlacritty\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `alacritty.toml`:\n\n```toml\n[[keyboard.bindings]]\nkey = \"X\"\nmods = \"Control|Shift\"\nchars = \"\\u001b[88;6u\"\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ci\u003eLegacy YAML format (deprecated since Alacritty v0.13)\u003c/i\u003e\u003c/summary\u003e\n\n```yaml\nkey_bindings:\n  - { key: X, mods: Control|Shift, chars: \"\\x1b[88;6u\" }\n```\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eVS Code Terminal\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `keybindings.json` (usually at `~/.config/Code/User/`):\n\n```json\n[\n  {\n    \"key\": \"ctrl+shift+x\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[88;6u\" },\n    \"when\": \"terminalFocus\"\n  }\n]\n```\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eDisplay Server Override\u003c/b\u003e\u003c/summary\u003e\n\n\u003e **Note:** The plugin automatically uses the correct implementation (X11 or Wayland) based on your system.\n\n### Environment Variables\n\n```bash\n# Force a specific implementation (overrides auto-detection)\nexport ZES_FORCE_IMPL=x11    # Force X11 implementation\nexport ZES_FORCE_IMPL=wayland # Force Wayland implementation\n```\n\n\u003e **Use case:** Force a specific implementation if auto-detection fails or if you want to use a different\n\u003e display server intentionally.\n\n\u003c/details\u003e\n\n---\n\n## Terminal Setup\n\n\u003e The [Auto Installation](#auto-installation) script can configure supported terminals (Kitty, WezTerm,\n\u003e Alacritty, Foot, VS Code) automatically. If you prefer to configure manually follow the steps below.\n\u003e [Open an issue](https://github.com/Michael-Matta1/zsh-edit-select/issues) if you need help with a terminal\n\u003e that is not covered.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eHow to Find Escape Sequences\u003c/b\u003e\u003c/summary\u003e\n\n**To find the escape sequence for any key combination:**\n\n1. Run `cat` (without arguments) in your terminal\n2. Press the key combination\n3. The terminal will display the escape sequence\n4. Use this sequence in your configuration\n\n\u003c/details\u003e\n\n### Step 1: Configure Copy Shortcut\n\n\u003e **⚠️ CRITICAL:** Before adding these mappings, you **MUST** remove or comment out any existing\n\u003e `ctrl+shift+c` mappings in your terminal config (such as `map ctrl+shift+c copy_to_clipboard` in Kitty).\n\u003e These will conflict and prevent the plugin from working correctly.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eKitty\u003c/b\u003e\u003c/summary\u003e\n\n### Using Ctrl+Shift+C (Default)\n\nTo use Ctrl+Shift+C for copying, add the following to `kitty.conf`:\n\n```conf\nmap ctrl+shift+c send_text all \\x1b[67;6u\n```\n\n### Using Ctrl+C for Copying (Reversed)\n\nIf you prefer to use Ctrl+C for copying (like in GUI applications) and Ctrl+Shift+C for interrupt:\n\n```conf\n# Ctrl+C sends the escape sequence for copying\nmap ctrl+c send_text all \\x1b[67;6u\n\n# Ctrl+Shift+C sends interrupt (default behavior)\nmap ctrl+shift+c send_text all \\x03\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eWezTerm\u003c/b\u003e\u003c/summary\u003e\n\n### Using Ctrl+Shift+C (Default)\n\nTo use Ctrl+Shift+C for copying, add the following to `wezterm.lua`:\n\n```lua\nreturn {\n  keys = {\n    {\n      key = 'C',\n      mods = 'CTRL|SHIFT',\n      action = wezterm.action.SendString '\\x1b[67;6u',\n    },\n  },\n}\n```\n\n### Using Ctrl+C for Copying (Reversed)\n\nIf you prefer to use Ctrl+C for copying and Ctrl+Shift+C for interrupt:\n\n```lua\nreturn {\n  keys = {\n    {\n      -- Ctrl+C sends the escape sequence for copying\n      key = 'c',\n      mods = 'CTRL',\n      action = wezterm.action.SendString '\\x1b[67;6u',\n    },\n    {\n      -- Ctrl+Shift+C sends interrupt signal\n      key = 'C',\n      mods = 'CTRL|SHIFT',\n      action = wezterm.action.SendString '\\x03',\n    },\n  },\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eAlacritty\u003c/b\u003e\u003c/summary\u003e\n\n### Using Ctrl+Shift+C (Default)\n\nAdd to `alacritty.toml`:\n\n```toml\n[[keyboard.bindings]]\nkey = \"C\"\nmods = \"Control|Shift\"\nchars = \"\\u001b[67;6u\"\n```\n\n### Using Ctrl+C for Copying (Reversed)\n\nIf you prefer to use Ctrl+C for copying and Ctrl+Shift+C for interrupt:\n\n```toml\n# Ctrl+C sends the escape sequence for copying\n[[keyboard.bindings]]\nkey = \"C\"\nmods = \"Control\"\nchars = \"\\u001b[67;6u\"\n\n# Ctrl+Shift+C sends interrupt signal\n[[keyboard.bindings]]\nkey = \"C\"\nmods = \"Control|Shift\"\nchars = \"\\u0003\"\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ci\u003eLegacy YAML format (deprecated since Alacritty v0.13)\u003c/i\u003e\u003c/summary\u003e\n\n**Default (Ctrl+Shift+C):**\n\n```yaml\nkey_bindings:\n  - { key: C, mods: Control|Shift, chars: \"\\x1b[67;6u\" }\n```\n\n**Reversed (Ctrl+C for copy):**\n\n```yaml\nkey_bindings:\n  - { key: C, mods: Control, chars: \"\\x1b[67;6u\" }\n  - { key: C, mods: Control|Shift, chars: \"\\x03\" }\n```\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eVS Code Terminal\u003c/b\u003e\u003c/summary\u003e\n\n### Using Ctrl+Shift+C (Default)\n\nTo use Ctrl+Shift+C for copying, add the following to `keybindings.json` (usually at `~/.config/Code/User/`):\n\n```json\n[\n  {\n    \"key\": \"ctrl+shift+c\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[67;6u\" },\n    \"when\": \"terminalFocus\"\n  }\n]\n```\n\n### Using Ctrl+C for Copying (Reversed)\n\nIf you prefer to use Ctrl+C for copying and Ctrl+Shift+C for interrupt:\n\n```json\n[\n  {\n    // Ctrl+C sends copy sequence to terminal\n    \"key\": \"ctrl+c\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[67;6u\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    // Ctrl+Shift+C sends interrupt signal\n    \"key\": \"ctrl+shift+c\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u0003\" },\n    \"when\": \"terminalFocus\"\n  }\n]\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eFoot\u003c/b\u003e\u003c/summary\u003e\n\n### Using Ctrl+Shift+C (Default)\n\nAdd the following to `foot.ini`. The default `clipboard-copy` binding must be unbound first so the escape\nsequence reaches the shell:\n\n```ini\n[key-bindings]\nclipboard-copy=none\n\n[text-bindings]\n\\x1b[67;6u = Control+Shift+c\n```\n\n### Using Ctrl+C for Copying (Reversed)\n\nIf you prefer to use Ctrl+C for copying and Ctrl+Shift+C for interrupt:\n\n```ini\n[key-bindings]\nclipboard-copy=none\n\n[text-bindings]\n\\x1b[67;6u = Control+c\n\\x03 = Control+Shift+c\n```\n\n\u003e **Note:** Foot uses the `[text-bindings]` section to send custom escape sequences to the shell. The default\n\u003e `clipboard-copy=Control+Shift+c` must be unbound first, otherwise Foot intercepts the key for its own\n\u003e clipboard action and the plugin never receives it. If you follow both Step 1 and Step 2, merge the\n\u003e `[key-bindings]` and `[text-bindings]` entries into single sections.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eAlternative: Without Terminal Remapping\u003c/b\u003e\u003c/summary\u003e\n\nIf your terminal doesn't support key remapping, you can add the following to your `~/.zshrc` to use **Ctrl +\n/** for copying:\n\n```sh\nx-copy-selection () {\n  if [[ $MARK -ne $CURSOR ]]; then\n    local start=$(( MARK \u003c CURSOR ? MARK : CURSOR ))\n    local length=$(( MARK \u003e CURSOR ? MARK - CURSOR : CURSOR - MARK ))\n    local selected=\"${BUFFER:$start:$length}\"\n    print -rn \"$selected\" | xclip -selection clipboard\n  fi\n}\nzle -N x-copy-selection\nbindkey '^_' x-copy-selection\n```\n\nYou can change the keybinding to any key you prefer. For example, to use **Ctrl + K**:\n\n```sh\nbindkey '^K' x-copy-selection\n```\n\n\u003e **Note:** The `^_` sequence represents Ctrl + / (Ctrl + Slash), and `^K` represents Ctrl + K. You can find\n\u003e other key sequences by running `cat` in your terminal and pressing the desired key combination.\n\n\u003e **Bonus Feature:** If no text is selected, this manual keybinding will copy the entire current line to the\n\u003e clipboard.\n\n\u003c/details\u003e\n\n---\n\n### Step 2: Configure Undo and Redo Shortcut\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eKitty\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `kitty.conf`:\n\n```conf\nmap ctrl+shift+z send_text all \\x1b[90;6u\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eWezTerm\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `wezterm.lua`:\n\n```lua\nreturn {\n  keys = {\n    {\n      key = 'Z',\n      mods = 'CTRL|SHIFT',\n      action = wezterm.action.SendString '\\x1b[90;6u',\n    },\n  },\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eAlacritty\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `alacritty.toml`:\n\n```toml\n[[keyboard.bindings]]\nkey = \"Z\"\nmods = \"Control|Shift\"\nchars = \"\\u001b[90;6u\"\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ci\u003eLegacy YAML format (deprecated since Alacritty v0.13)\u003c/i\u003e\u003c/summary\u003e\n\n```yaml\nkey_bindings:\n  - { key: Z, mods: Control|Shift, chars: \"\\x1b[90;6u\" }\n```\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eVS Code Terminal\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `keybindings.json` (usually at `~/.config/Code/User/`):\n\n```json\n[\n  {\n    \"key\": \"ctrl+z\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001a\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"ctrl+shift+z\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[90;6u\" },\n    \"when\": \"terminalFocus\"\n  }\n]\n```\n\n\u003e **Note:** The Ctrl+Z keybinding works seamlessly alongside the traditional suspend process functionality\n\u003e (Ctrl+Z suspends a running foreground process to background). The plugin intelligently handles undo\n\u003e operations for command line editing while preserving the ability to suspend processes when needed.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eFoot\u003c/b\u003e\u003c/summary\u003e\n\nAdd the following to `foot.ini`. The default `prompt-prev` binding must be unbound first because Foot maps\nCtrl+Shift+Z to prompt navigation by default:\n\n```ini\n[key-bindings]\nprompt-prev=none\n\n[text-bindings]\n\\x1b[90;6u = Control+Shift+z\n```\n\n\u003e **Note:** If you already configured Foot for Step 1 (Copy), merge these entries into the existing\n\u003e `[key-bindings]` and `[text-bindings]` sections rather than creating duplicates.\n\n\u003c/details\u003e\n\n---\n\n### Step 3: Enable Shift Selection Keys\n\nSome terminals intercept Shift key combinations by default. Here's how to configure popular terminals:\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eKitty\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `kitty.conf`:\n\n```conf\n# Pass Shift and Ctrl+Shift keys through to Zsh for selection\n# (overrides any default or custom Kitty mappings on these keys)\nmap shift+left        no_op\nmap shift+right       no_op\nmap shift+up          no_op\nmap shift+down        no_op\nmap shift+home        no_op\nmap shift+end         no_op\n# Ctrl+Shift+Left/Right default to previous_tab/next_tab in Kitty\nmap ctrl+shift+left   no_op\nmap ctrl+shift+right  no_op\n# Ctrl+Shift+Home/End default to scroll_home/scroll_end in Kitty\nmap ctrl+shift+home   no_op\nmap ctrl+shift+end    no_op\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eWezTerm\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `wezterm.lua`:\n\n```lua\nreturn {\n  keys = {\n    { key = 'LeftArrow', mods = 'CTRL|SHIFT', action = wezterm.action.DisableDefaultAssignment },\n    { key = 'RightArrow', mods = 'CTRL|SHIFT', action = wezterm.action.DisableDefaultAssignment },\n    { key = 'Home', mods = 'CTRL|SHIFT', action = wezterm.action.DisableDefaultAssignment },\n    { key = 'End', mods = 'CTRL|SHIFT', action = wezterm.action.DisableDefaultAssignment },\n  },\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eVS Code Terminal\u003c/b\u003e\u003c/summary\u003e\n\nAdd to `keybindings.json` (usually at `~/.config/Code/User/`):\n\n```json\n[\n  {\n    \"key\": \"shift+left\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;2D\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"shift+right\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;2C\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"shift+up\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;2A\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"shift+down\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;2B\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"shift+home\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;2H\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"shift+end\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;2F\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"ctrl+shift+left\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;6D\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"ctrl+shift+right\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;6C\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"ctrl+shift+home\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;6H\" },\n    \"when\": \"terminalFocus\"\n  },\n  {\n    \"key\": \"ctrl+shift+end\",\n    \"command\": \"workbench.action.terminal.sendSequence\",\n    \"args\": { \"text\": \"\\u001b[1;6F\" },\n    \"when\": \"terminalFocus\"\n  }\n]\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eAlacritty\u003c/b\u003e\u003c/summary\u003e\n\nAlacritty's defaults intercept **Shift+Home** (`ScrollToTop`) and **Shift+End** (`ScrollToBottom`). Add to\nyour Alacritty configuration to pass them through for selection:\n\n\u003cdetails\u003e\n\u003csummary\u003eTOML (\u003ccode\u003ealacritty.toml\u003c/code\u003e)\u003c/summary\u003e\n\n```toml\n# Pass Shift+Home/End through for selection\n# (overrides Alacritty defaults: ScrollToTop / ScrollToBottom)\n[[keyboard.bindings]]\nkey = \"Home\"\nmods = \"Shift\"\naction = \"ReceiveChar\"\n\n[[keyboard.bindings]]\nkey = \"End\"\nmods = \"Shift\"\naction = \"ReceiveChar\"\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eLegacy YAML format (\u003ccode\u003ealacritty.yml\u003c/code\u003e, deprecated since Alacritty v0.13)\u003c/summary\u003e\n\n```yaml\nkey_bindings:\n  # Pass Shift+Home/End through for selection\n  - { key: Home, mods: Shift, action: ReceiveChar }\n  - { key: End, mods: Shift, action: ReceiveChar }\n```\n\n\u003c/details\u003e\n\nAll other Shift / Ctrl+Shift arrow keys pass through to Zsh natively.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eFoot\u003c/b\u003e\u003c/summary\u003e\n\nFoot passes Shift+Arrow keys through to the terminal natively — no additional configuration is needed for\nShift selection.\n\n\u003c/details\u003e\n\n\u003e **Configurations in practice:** The [dev-dotfiles](https://github.com/Michael-Matta1/dev-dotfiles)\n\u003e repository includes working setups for **Kitty** (`kitty.conf`) and **VS Code** (`keybindings.json`) that\n\u003e demonstrate that this plugin can be seamlessly integrated alongside other tools and configurations.\n\n---\n\n## Wayland Support\n\n\u003e The [Auto Installation](#auto-installation) script automatically detects your display server and selects the\n\u003e correct agent. For manual setup, follow the Wayland-specific instructions in\n\u003e [Manual Installation](#manual-installation).\n\nWayland is fully supported with native protocol implementation. The plugin automatically detects your Wayland\nsetup and uses the optimal clipboard agent:\n\n**Clipboard Agent Priority (automatically selected):**\n\n1. **`zes-wl-selection-agent` (Native Wayland)** — Clipboard integration with direct Wayland protocol support\n   - Handles PRIMARY selection and CLIPBOARD using native Wayland protocols\n   - Works on all compositors with protocol support (Sway, Hyprland, KDE Plasma, River, Wayfire)\n   - Full mouse selection replacement — no external tools needed\n   - Sub-2.2ms clipboard latency (96.4% faster than `wl-copy`)\n\n2. **`zes-xwayland-agent` (XWayland)** — XWayland compatibility layer (used when `DISPLAY` is available)\n   - Uses X11 XFixes via XWayland for clipboard integration\n   - Seamless support for mixed X11/Wayland environments\n   - Complements the native Wayland agent for maximum compatibility\n\n\u003e The native Wayland implementation connects directly to Wayland protocols, eliminating reliance on\n\u003e `wl-copy`/`wl-paste`. All clipboard operations happen within the persistent agent process — zero subprocess\n\u003e overhead.\n\u003e\n\u003e **Architecture:** The clipboard agents (`zes-wl-selection-agent`, `zes-xwayland-agent`,\n\u003e `zes-x11-selection-agent`) are lightweight background processes that integrate with display server clipboard\n\u003e protocols. Updates are written to a fast in-memory cache (typically on `XDG_RUNTIME_DIR` or `/dev/shm`). The\n\u003e shell reads this cache via a single `stat()` call per keypress — no forks, no pipes, no latency.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eNative Wayland Protocol Support (Fully Implemented)\u003c/b\u003e\u003c/summary\u003e\n\n`zes-wl-selection-agent` provides complete clipboard and selection support on all Wayland compositors with\nprotocol implementation:\n\n**Supported Compositors:**\n\n- ✅ **wlroots-based compositors** — Sway, Hyprland, River, Wayfire (full PRIMARY support)\n- ✅ **KDE Plasma Wayland** — Full PRIMARY selection via `zwp_primary_selection_unstable_v1`\n- ✅ **GNOME Wayland (Mutter)** — Native Wayland implementation provides PRIMARY selection support\\*\n\n\\*Note: GNOME's PRIMARY selection support depends on compositor configuration. If unavailable, XWayland bridge\nprovides seamless fallback.\n\n**Performance Advantage:** Direct Wayland protocol access means:\n\n- No subprocess spawning for clipboard operations\n- No `wl-copy`/`wl-paste` process overhead\n- Native event-driven architecture\n- Instant response to selection changes\n- Zero typing lag even with frequent selections\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eXWayland Bridge (Recommended, for Enhanced Compatibility)\u003c/b\u003e\u003c/summary\u003e\n\n`zes-xwayland-agent` uses XWayland (if available) for an extra X11 compatibility layer for clipboard\nintegration. XWayland provides:\n\n- Seamless fallback for hybrid X11/Wayland environments\n- Support for legacy applications running under XWayland\n- Alternative PRIMARY selection detection when native Wayland protocols unavailable\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eEnabling XWayland (Recommended)\u003c/b\u003e\u003c/summary\u003e\n\n`zes-xwayland-agent` uses XWayland for clipboard integration — it requires XWayland to be available. XWayland\nprovides an X11 compatibility layer on top of Wayland, allowing the agent to use X11's XFixes extension —\ncompletely invisible: no windows, no dock entries, no compositor artifacts.\n\n**Check if XWayland is already running:**\n\n```bash\necho $DISPLAY\n```\n\nIf this prints something like `:0` or `:1`, XWayland is already available.\n\n**If `DISPLAY` is empty, enable XWayland on your compositor:**\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eGNOME (Mutter)\u003c/b\u003e\u003c/summary\u003e\n\nXWayland is enabled by default on GNOME. If it was disabled:\n\n```bash\n# Re-enable XWayland (requires logout/login)\ngsettings reset org.gnome.mutter experimental-features\n```\n\nOr add `Xwayland` to the experimental features if using a custom list. On GNOME 47+, XWayland starts on demand\nwhen any X11 app connects.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eKDE Plasma\u003c/b\u003e\u003c/summary\u003e\n\nXWayland is enabled by default. If disabled, re-enable it in:\n\n**System Settings → Display and Monitor → Compositor → Allow XWayland applications**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eSway\u003c/b\u003e\u003c/summary\u003e\n\nXWayland is enabled by default. If disabled, add to your Sway config:\n\n```\nxwayland enable\n```\n\nThen reload Sway (`$mod+Shift+C`).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eHyprland\u003c/b\u003e\u003c/summary\u003e\n\nAdd to your Hyprland config:\n\n```\nxwayland {\n  force_zero_scaling = true\n}\n```\n\nXWayland is enabled by default in Hyprland.\n\n\u003c/details\u003e\n\n**Without XWayland:** The plugin uses `zes-wl-selection-agent` directly, which uses\n`zwp_primary_selection_v1`. This works on wlroots-based compositors (Sway, Hyprland) and KDE Plasma, but may\nshow a small surface in the dock/taskbar on GNOME/Mutter.\n\n\u003c/details\u003e\n\n---\n\n## Default Key Bindings Reference\n\n### Navigation Keys\n\n| Key Combination | Action                     |\n| --------------- | -------------------------- |\n| **Ctrl + ←**    | Move cursor one word left  |\n| **Ctrl + →**    | Move cursor one word right |\n\n### Selection Keys\n\n| Key Combination         | Action                     |\n| ----------------------- | -------------------------- |\n| **Shift + ←**           | Select one character left  |\n| **Shift + →**           | Select one character right |\n| **Shift + ↑**           | Select one line up         |\n| **Shift + ↓**           | Select one line down       |\n| **Shift + Home**        | Select to line start       |\n| **Shift + End**         | Select to line end         |\n| **Shift + Ctrl + ←**    | Select to word start       |\n| **Shift + Ctrl + →**    | Select to word end         |\n| **Shift + Ctrl + Home** | Select to buffer start     |\n| **Shift + Ctrl + End**  | Select to buffer end       |\n| **Ctrl + A**            | Select all text            |\n\n### Editing Keys\n\n| Key Combination      | Action                            |\n| -------------------- | --------------------------------- |\n| **Ctrl + C**         | Copy selected text                |\n| **Ctrl + X**         | Cut selected text                 |\n| **Ctrl + V**         | Paste (replaces selection if any) |\n| **Ctrl + Z**         | Undo last edit                    |\n| **Ctrl + Shift + Z** | Redo last undone edit             |\n| **Delete/Backspace** | Delete selected text              |\n| **Any character**    | Replace selected text if any      |\n\n---\n\n## Troubleshooting\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eShift selection doesn't work\u003c/b\u003e\u003c/summary\u003e\n\n**Solution:** Configure your terminal to pass Shift key sequences. See [Terminal Setup](#terminal-setup).\n\n**Verify:** Run `cat` and press Shift+Left. You should see an escape sequence like `^[[1;2D`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eClipboard operations don't work\u003c/b\u003e\u003c/summary\u003e\n\n**Solution:** Install the required clipboard tool:\n\n- Wayland: `wl-clipboard`\n- X11: `xclip`\n\n**Verify:** Run `wl-copy \u003c\u003c\u003c \"test\"` or `xclip -i \u003c\u003c\u003c \"test\"` to check if the tool works.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMouse replacement not working\u003c/b\u003e\u003c/summary\u003e\n\n**Solution:**\n\n1. Check if mouse replacement is enabled: `edit-select config` → View Configuration\n2. Ensure your terminal supports mouse selection (most do)\n3. Try selecting text with your mouse, then typing—it should replace the selection\n\nIf this does not work for you, it is often due to platform limitations or compatibility issues with the\nPRIMARY selection. See [Platform Compatibility](#platform-compatibility) for more details.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eCtrl+C doesn't copy\u003c/b\u003e\u003c/summary\u003e\n\n**Solution:** Configure your terminal to remap Ctrl+C. See\n[Step 1: Configure Copy Shortcut](#step-1-configure-copy-shortcut) at the [Terminal Setup](#terminal-setup)\nsection.\n\n**Alternative:** Use Ctrl+Shift+C for copying, or configure a custom keybinding with `edit-select config`, or\nuse the 'Without Terminal Remapping' method if your terminal doesn't support key remapping.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eConfiguration wizard doesn't launch\u003c/b\u003e\u003c/summary\u003e\n\n**Symptoms:** Running `edit-select config` shows \"file not found\" error\n\n**Solution:**\n\n1. Check the plugin was installed correctly\n2. Verify the wizard file exists in the plugin directory (`edit-select-wizard-x11.zsh` on X11, or\n   `edit-select-wizard-wayland.zsh` on Wayland)\n3. Ensure the file has read permissions:\n   ```bash\n   # X11:\n   chmod +r ~/.oh-my-zsh/custom/plugins/zsh-edit-select/impl-x11/edit-select-wizard-x11.zsh\n   # Wayland:\n   chmod +r ~/.oh-my-zsh/custom/plugins/zsh-edit-select/impl-wayland/edit-select-wizard-wayland.zsh\n   ```\n4. Try sourcing your `.zshrc` again: `source ~/.zshrc`\n5. Fully close and reopen your terminal\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eDelete key not removing mouse-selected text\u003c/b\u003e\u003c/summary\u003e\n\nIf the `Delete` key does not remove mouse-selected text, ensure your `~/.zshrc` does not contain a line that\nforces the Delete key to the default handler such as:\n\n```bash\nbindkey '^[[3~' delete-char\n```\n\nThat line will override the plugin's binding for the Delete key and prevent `zsh-edit-select` from handling\nmouse selections correctly.\n\n**Solution:** Remove or comment out that line and reload your shell:\n\n```bash\nsource ~/.zshrc\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMouse selection replaces text in a different pane (tmux users)\u003c/b\u003e\u003c/summary\u003e\n\n**Symptoms:** When using tmux with multiple panes, selecting text with the mouse in one pane and then\nswitching to another pane causes typed text to unexpectedly replace the previously selected text from the\nother pane.\n\n**Solution:** Enable focus events in tmux. The plugin uses terminal focus reporting (DECSET 1004) to\ndistinguish between selections made in the active pane versus other panes.\n\nAdd this line to your `~/.tmux.conf`:\n\n```bash\nset-option -g focus-events on\n```\n\nThen reload your tmux configuration:\n\n```bash\ntmux source-file ~/.tmux.conf\n```\n\n\u003e **Note:** `focus-events on` has been the default since tmux 3.3a (released April 2023). If you're running an\n\u003e older version of tmux, either upgrade or add the line above to your configuration.\n\n**Alternative:** If you cannot enable focus events, you can disable mouse replacement entirely with\n`edit-select config` → Option 1 → Disable. This will preserve keyboard selection functionality while\npreventing cross-pane mouse selection issues.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eManual Build (Optional)\u003c/b\u003e\u003c/summary\u003e\n\nThe plugin compiles agents automatically on first use. To manually build them, first locate your plugin\ndirectory — this depends on your plugin manager:\n\n```bash\n# Common locations (adjust to wherever you installed the plugin):\n#   Oh My Zsh:  ~/.oh-my-zsh/custom/plugins/zsh-edit-select\n#   Zinit:      ~/.local/share/zinit/plugins/Michael-Matta1---zsh-edit-select\n#   Sheldon:    ~/.local/share/sheldon/repos/github.com/Michael-Matta1/zsh-edit-select\n#   Manual:     wherever you ran: git clone https://github.com/Michael-Matta1/zsh-edit-select\nPLUGIN_DIR=~/.oh-my-zsh/custom/plugins/zsh-edit-select  # ← change this to your path\n```\n\n**X11 Agent:**\n\n```bash\ncd \"$PLUGIN_DIR/impl-x11/backends/x11\"\nmake\n```\n\n**Wayland Agent:**\n\n```bash\ncd \"$PLUGIN_DIR/impl-wayland/backends/wayland\"\nmake\n```\n\n**XWayland Agent:**\n\n```bash\ncd \"$PLUGIN_DIR/impl-wayland/backends/xwayland\"\nmake\n```\n\nTo clean/rebuild:\n\n```bash\nmake clean \u0026\u0026 make\n```\n\n\u003e **Note:** The plugin will automatically detect your display server and compile the appropriate agent on\n\u003e first shell startup. The Makefiles require:\n\u003e\n\u003e - **X11 builds:** `libX11` and `libXfixes` development headers/libraries\n\u003e - **Wayland builds:** `wayland-client` development headers, `wayland-scanner`, and `wayland-protocols`\n\u003e\n\u003e If compilation fails, the plugin will fall back to using external clipboard tools (`xclip` for X11,\n\u003e `wl-clipboard` for Wayland).\n\n**Build Optimization:**\n\nThe default Makefiles compile with aggressive optimization flags for maximum runtime performance:\n\n| Flag                                  | Purpose                                              |\n| ------------------------------------- | ---------------------------------------------------- |\n| `-O3`                                 | Maximum compiler optimization level                  |\n| `-march=native`                       | CPU-specific instruction set (SSE, AVX, etc.)        |\n| `-mtune=native`                       | CPU-specific scheduling optimizations                |\n| `-flto`                               | Link-time optimization across compilation units      |\n| `-ffunction-sections -fdata-sections` | Granular dead code elimination                       |\n| `-Wl,--gc-sections`                   | Remove unused functions/data at link time            |\n| `-Wl,--as-needed`                     | Skip linking unused shared libraries                 |\n| `-Wl,-z,now`                          | Resolve all symbols at load time (security + perf)   |\n| `-Wl,-z,relro`                        | Read-only relocations after startup                  |\n| `-Wl,-z,noexecstack`                  | Mark stack non-executable (security hardening)       |\n| `-Wl,-O1`                             | Linker-level optimization pass                       |\n| `-Wl,--hash-style=gnu`                | Faster symbol lookup with GNU hash tables            |\n| `-Wl,--build-id=none`                 | Omit build-id section for smaller binaries           |\n| `-fomit-frame-pointer`                | Free up a register for better performance            |\n| `-fno-plt`                            | Eliminate PLT indirection for faster library calls   |\n| `-fno-semantic-interposition`         | Enable inlining across translation units             |\n| `-fno-strict-aliasing`                | Permit type-punning casts in X11 agents (X11 only)   |\n| `-fno-asynchronous-unwind-tables`     | Omit async unwind info not needed by signal handlers |\n| `-fno-unwind-tables`                  | Omit synchronous unwind tables (no C++ exceptions)   |\n| `-fmerge-all-constants`               | Deduplicate identical constants across units         |\n| `-fipa-pta`                           | Interprocedural pointer analysis for better inlining |\n| `-fno-ident`                          | Omit compiler identification string from binary      |\n| `-fno-stack-protector`                | Remove stack-canary overhead (local-only agents)     |\n| `-DNDEBUG`                            | Disable assertions in release builds                 |\n| `-funroll-loops`                      | Unroll small loops for throughput                    |\n| `-s`                                  | Strip symbols for smaller production binaries        |\n\n\u003e **Important:** `-march=native` produces binaries optimized for the CPU you're building on. These binaries\n\u003e may not run correctly on different CPU architectures. For distributed builds, replace\n\u003e `-march=native -mtune=native` with a portable baseline like `-march=x86-64-v2`.\n\n\u003c/details\u003e\n\n\n---\n\n## Platform Compatibility\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMouse Selection Replacement Feature\u003c/b\u003e\u003c/summary\u003e\n\nThe **Mouse Selection Replacement** feature (automatically detecting and replacing mouse-selected text) has\ncomprehensive support across platforms via our custom agent implementations:\n\n### ✅ Fully Supported\n\n**X11 \u0026 XWayland:**\n\n- **X11** - Complete PRIMARY selection support via XFixes extension\n- **XWayland bridge** - Full compatibility layer for mixed environments\n\n**Native Wayland (Direct Protocol Implementation):**\n\n- **wlroots-based compositors** — Sway, Hyprland, River, Wayfire with `zwp_primary_selection_unstable_v1`\n- **KDE Plasma Wayland** - Full PRIMARY selection support via native protocols\n- **GNOME Wayland (Mutter)** - Native Wayland implementation provides PRIMARY selection support where\n  available\n- **Other Wayland compositors** - Full support for any compositor implementing PRIMARY selection protocols\n\n### Performance on Wayland\n\nThe native Wayland agent (`zes-wl-selection-agent`) provides:\n\n- ✅ Direct protocol access (no `wl-copy`/`wl-paste` subprocess overhead)\n- ✅ Zero typing lag with instant selection detection\n- ✅ Event-driven architecture\n- ✅ Superior responsiveness compared to standard clipboard tools\n\n### If Selection Replacement Doesn't Work\n\n1. Verify native Wayland or XWayland support is available\n2. Check that your compositor supports PRIMARY selection protocols\n3. Disable mouse replacement if needed: `edit-select config` → Option 1\n4. Report issues with your compositor on [GitHub](https://github.com/Michael-Matta1/zsh-edit-select/issues)\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eTesting Coverage\u003c/b\u003e\u003c/summary\u003e\n\nThis plugin has been thoroughly and heavily tested on **Kitty Terminal** and briefly on other popular\nterminals.\n\nIf you encounter issues on other terminals or platforms, please\n[open an issue](https://github.com/Michael-Matta1/zsh-edit-select/issues) with your terminal name, OS, and\ndisplay server.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eCore Features (Universal)\u003c/b\u003e\u003c/summary\u003e\n\nThese features work universally on X11, Wayland, and XWayland:\n\n- ✅ Shift+Arrow keys for text selection\n- ✅ Ctrl+A to select all\n- ✅ Ctrl+Shift+C to copy (or Ctrl+C in reversed mode)\n- ✅ Ctrl+X to cut keyboard selection\n- ✅ Ctrl+V to paste\n- ✅ Ctrl+Z to undo\n- ✅ Ctrl+Shift+Z to redo\n- ✅ Delete/Backspace to remove keyboard selection\n- ✅ Type or paste to replace keyboard selection\n- ✅ Mouse selection replacement (where PRIMARY selection available)\n\n\n\u003c/details\u003e\n\n---\n\n## Performance-Optimized Architecture\n\nThe plugin architecture is built around compiled native C agents that run as persistent background processes.\nEach agent tracks selection changes via display server events, writes updates to a RAM-backed cache, and the\nshell reads that cache using a single `zstat` call per keypress — zero process forks during normal typing.\nBackend detection, agent startup, and configuration loading occur once at plugin load; all subsequent\noperations use the cached results directly.\n\nCore architectural properties:\n\n- **Single-pass initialization** — Backend detection, agent startup, and configuration loading occur at plugin\n  load time. The results are cached in shell variables and reused for the entire session.\n- **Event-driven selection tracking** — X11 XFixes events and Wayland compositor events drive cache updates;\n  all agents sleep in `poll()` between events, consuming no CPU while idle.\n- **Compiled C agents** — Direct system calls compiled with aggressive optimization flags\n  (`-O3 -march=native -flto -fipa-pta` and link-time dead code elimination); no interpreter overhead.\n- **RAM-backed cache** — Cache files reside in `XDG_RUNTIME_DIR` (tmpfs on most Linux distributions),\n  with `TMPDIR` or `/tmp` as fallback. On standard systemd-based systems, all cache I/O remains in memory.\n- **Graceful fallback** — If the compiled agents are unavailable, the plugin falls back to standard clipboard\n  tools (`xclip`, `wl-paste`/`wl-copy`) transparently. No functionality is lost.\n\n### Optimization Techniques\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eStartup \u0026 Initialization\u003c/b\u003e\u003c/summary\u003e\n\n**Backend Detection**\n\n- Platform detection runs once at plugin load time by inspecting `ZES_FORCE_IMPL`, `XDG_SESSION_TYPE`,\n  `WAYLAND_DISPLAY`, and `DISPLAY` in priority order\n- The detected backend (`x11` or `wayland`) is stored in read-only shell variables (`ZES_ACTIVE_IMPL`,\n  `ZES_DETECTION_REASON`, `ZES_IMPL_PATH`) and reused for the entire session\n- A double-load guard (`_ZES_LOADER_LOADED`) prevents re-execution when `.zshrc` is re-sourced mid-session\n\n**Lazy Backend Loading**\n\n- Only the implementation matching the detected display server (X11 or Wayland) is sourced\n- The other implementation is never loaded into memory, reducing both startup time and memory footprint\n- The configuration wizard is also lazy-loaded — its file is only sourced when the user explicitly runs\n  `edit-select config`\n\n**Zsh Bytecode Compilation**\n\n- Plugin files and all backend `.zsh` files are compiled to `.zwc` (Zsh wordcode bytecode) on first load via\n  `zcompile`\n- The bytecode is reused on subsequent sessions, bypassing source parsing entirely\n- A file-existence guard (`[[ ! -f \"${file}.zwc\" ]]`) prevents redundant recompilation\n\n**Agent Auto-Compilation**\n\n- If the compiled agent binary is missing but its `Makefile` is present, the loader runs `make` automatically\n  in a subshell\n- Build errors produce stderr diagnostics naming the required `-dev` packages for the user's distribution\n\n**Configuration Loading**\n\n- The configuration file (`~/.config/zsh-edit-select/config`) is read once at startup and its values are\n  stored in shell variables\n- No configuration file I/O occurs during individual plugin operations\n\n**Configuration Wizard**\n\nThe wizard file is lazy-loaded — sourced only when the user explicitly invokes `edit-select config`,\nadding zero overhead to normal shell sessions. All wizard operations are implemented entirely as Zsh\nbuilt-in operations with no subprocess spawning:\n\n- Config reads use `while IFS= read -r` loops; config writes use `print -r --` with Zsh array filtering\n  (`${(@)array:#KEY=*}`) — no `sed` or `grep` forks at any point in the config I/O path\n- Screen redraws use inline ANSI escape sequences (`printf '\\033[2J\\033[3J\\033[H'`) instead of the\n  `clear` command; this also clears the scrollback buffer in a single `write()` call rather than a fork\n- The color gradient used in the wizard UI is computed once and cached in `$_ZESW_GRADIENT_CACHE` on\n  first invocation; subsequent calls within the same shell session reuse the cached values directly\n- Keybinding changes applied through the wizard take effect immediately in the current shell session via\n  direct `bindkey` calls — no shell restart or `.zshrc` re-source is required\n\n**Agent Startup \u0026 Readiness**\n\n- Before launching a new agent instance, the backend removes any leftover `seq` and `primary` cache files\n  from a previous session (`rm -f \"$_EDIT_SELECT_SEQ_FILE\" \"$_EDIT_SELECT_PRIMARY_FILE\"`). This prevents the\n  shell from treating stale data written by the previous daemon as a new selection event immediately after\n  startup.\n- The agent is launched inside a subshell using the pattern `( agent_binary \"$cache_dir\" \u0026\u003e/dev/null \u0026 ; disown )`.\n  The wrapping subshell isolates job control: the agent process does not appear in the shell's `jobs` list,\n  does not receive `SIGHUP` when the terminal closes, and does not trigger Zsh background-job notifications.\n- The plugin polls for the agent's readiness signal (the `seq` cache file appearing) with a maximum wait of\n  1 second (40 × 25 ms intervals), rather than using a fixed sleep — the poll exits as soon as the file\n  appears, so startup overhead matches actual agent initialization time.\n- If a running agent is already present (PID file exists and `kill -0` succeeds), it is reused without\n  restart.\n- After the readiness poll completes, the plugin reads the initial `seq` file mtime and sets\n  `_EDIT_SELECT_EVENT_FIRED_FOR_MTIME=1`. This marks the startup mtime as already-seen, preventing the first\n  observed value from being treated as a new selection event on the first ZLE callback.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eRuntime Execution\u003c/b\u003e\u003c/summary\u003e\n\n**mtime-Based Selection Detection**\n\nThe typing hot path is designed around a single `stat()` syscall per keypress:\n\n1. The background agent writes selection content to a `primary` cache file, then updates a `seq` file\n2. The shell detects changes by reading the `seq` file's modification time via `zstat` (the Zsh builtin,\n   which performs a direct `stat()` syscall — no process fork)\n3. If the mtime matches the cached value, the function returns immediately with no further work\n4. If the mtime has changed, the `primary` file content is read via `$(\u003cfile)` (Zsh builtin read — also\n   zero forks) and stored in a shell variable\n\nUnder normal typing conditions with no selection changes, the entire detection path costs one `stat()` syscall\nand an integer comparison per keypress.\n\n**Write-Ordering Guarantee**\n\nThe agent always writes the `primary` content file before updating the `seq` file. Since the shell uses the\n`seq` file's mtime as its change signal, this ordering guarantees the shell never reads a half-written\n`primary` file.\n\n**In-Memory State Caching**\n\n- The last-known selection state is held in shell variables (`_EDIT_SELECT_LAST_PRIMARY`,\n  `_EDIT_SELECT_LAST_MTIME`)\n- `_zes_sync_selection_state()` returns immediately if the cache file mtime is unchanged\n- An event-fired gate (`_EDIT_SELECT_EVENT_FIRED_FOR_MTIME`) prevents the same mtime from triggering\n  redundant processing across multiple ZLE callbacks within the same redraw cycle\n- Keyboard selections bypass the mouse-detection path entirely\n- State is invalidated only when the agent writes a new cache entry\n- Widget handlers call `zle -c` (flush pending typeahead) rather than `zle -Rc` (flush + force full\n  redraw); this avoids an unnecessary redraw cycle on every keypress that does not modify the display\n- After each paste or cut operation, `_zes_sync_after_paste()` re-reads the current `seq` file mtime and\n  `primary` file content directly from the daemon cache and updates `_EDIT_SELECT_LAST_MTIME` and\n  `_EDIT_SELECT_LAST_PRIMARY`. This resets the detection baseline to the post-operation state, preventing the\n  mtime written during the operation from being re-detected as a new selection event on the next ZLE callback.\n\n**Direct Buffer Manipulation**\n\nPaste and replace-selection operations compute the selection bounds and splice `BUFFER` directly using Zsh\nstring indexing (`${BUFFER:0:$start}${replacement}${BUFFER:$((start+len))}`), bypassing `zle kill-region`.\nThis prevents these operations from writing to ZLE's kill buffer, which would interfere with subsequent yank\n(`Ctrl+Y`) operations. Mouse-selection deletion widgets use the same direct-splice approach. Cut operations\n(`Ctrl+X`) intentionally retain `kill-region` so the deleted text remains available for yank.\n\n**Cut Operation Ordering**\n\nCut copies the selected text to the clipboard before deleting it from the buffer. By performing the copy\nfirst, the clipboard server begins serving the content to other applications immediately while the subsequent\nbuffer deletion completes — a single in-memory string splice with no external I/O.\n\n**Agent Health Monitoring**\n\n- Agent liveness is checked via `kill(pid, 0)` at 30-second intervals (amortized via `EPOCHSECONDS`\n  comparison)\n- If the agent process has exited, it is restarted transparently\n- Health checks are not issued on individual keypress operations\n\n**Event-Driven Detection**\n\n- **X11 / XWayland**: The agent subscribes to XFixes `XFixesSetSelectionOwnerNotifyMask` events; it wakes only\n  on selection owner changes. The main loop uses `poll()` with a 1-second timeout used solely for clean\n  `SIGTERM` shutdown — no periodic work is performed on timeout\n- **Wayland**: The compositor delivers primary selection events on owner change via\n  `zwp_primary_selection_unstable_v1`. A 50 ms `poll()` timeout provides a secondary detection path for\n  content changes within the same selection owner (e.g., the user extending a terminal text selection without\n  releasing the mouse button — which changes content without changing the selection owner)\n- All agents sleep in `poll()` between events, consuming no CPU during idle periods\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eC Agent Internals\u003c/b\u003e\u003c/summary\u003e\n\n**Compilation \u0026 Binary Optimization**\n\nAgents are compiled with aggressive optimization flags to minimize binary size and maximize runtime\nperformance:\n\n- `-O3 -march=native -mtune=native` — Full optimization with CPU-specific instruction scheduling\n- `-flto` (Link-Time Optimization) — Whole-program optimization across all translation units\n- `-fipa-pta` — Interprocedural pointer analysis for better alias resolution\n- `-fomit-frame-pointer` — Frees a general-purpose register by omitting the frame pointer\n- `-funroll-loops` — Unrolls loops to reduce branch overhead in tight event-handling paths\n- `-fmerge-all-constants` — Merges identical constants across translation units, reducing `.rodata` size\n- `-ffunction-sections -fdata-sections` + `-Wl,--gc-sections` — Dead code elimination: each function and data\n  object is placed in its own section; the linker discards unreferenced sections\n- `-fno-plt -fno-semantic-interposition` — Direct function calls without PLT indirection; allows the compiler\n  to inline across translation units without interposition checks\n- `-fno-asynchronous-unwind-tables -fno-unwind-tables` — Removes `.eh_frame` exception unwind sections\n  (unnecessary for C agents that do not use C++ exceptions), reducing binary size\n- `-DNDEBUG` — Disables all `assert()` checks in release builds, removing debug overhead\n- `-Wl,--as-needed` — Only links libraries that are actually referenced\n- `-Wl,-O1` — Linker optimization pass for symbol resolution and relocation processing\n- `-Wl,-z,now -Wl,-z,relro` — Full RELRO: the Global Offset Table is resolved and marked read-only at load\n  time\n- `-Wl,-z,noexecstack` — Non-executable stack\n- `-Wl,--hash-style=gnu` — GNU hash table for faster dynamic symbol lookup\n- `-s -Wl,--build-id=none -fno-ident` — Strips all symbols, build-id, and compiler version strings from the\n  binary\n- `-fno-strict-aliasing` _(X11 and XWayland agents only)_ — Permits the type-punning pointer casts required\n  by Xlib's event structures without aliasing-rule violations; not applied to the Wayland agent, which does\n  not cast between unrelated pointer types\n- `-fno-stack-protector` — Removes stack-canary instrumentation overhead; the agents run locally as\n  unprivileged user daemons with no network-facing attack surface\n- System libraries (`libwayland-client`, `libX11`, `libXfixes`) are the only runtime dependencies\n\n**Operation Modes**\n\nEach agent binary supports five operation modes within a single executable, eliminating the need for separate\nper-mode binaries:\n\n| Mode | CLI Flag | Behavior |\n| --- | --- | --- |\n| **Daemon** | _(default)_ | Persistent PRIMARY selection monitoring with event-driven cache updates |\n| **Oneshot** | `--oneshot` | Print current PRIMARY selection to stdout and exit |\n| **Get clipboard** | `--get-clipboard` | Print current CLIPBOARD contents to stdout and exit |\n| **Copy clipboard** | `--copy-clipboard` | Read stdin, take clipboard ownership, fork a background server |\n| **Clear primary** | `--clear-primary` | Clear the PRIMARY selection and exit |\n\n**Persistent File Descriptor Architecture**\n\nAll three agents open the cache file descriptors (`fd_primary`, `fd_seq`) once at daemon startup and hold them\nopen for the entire agent lifetime. Cache writes use `pwrite()` (atomic positional write — no preceding\n`lseek()`) followed by `ftruncate()` to trim the file to the exact written length, preventing stale trailing\nbytes from longer previous entries. This reduces each cache update to 2 syscalls per file, compared to the\n`open()`/`write()`/`fsync()`/`close()` pattern (4 syscalls per file) used by conventional approaches.\n\n**Content Deduplication** _(Wayland agent)_\n\nThe Wayland agent's `check_and_update_primary()` and `ps_device_handle_selection()` compare incoming selection\ncontent against a cached copy (`last_known_content`) using `memcmp()` before writing. When the content is\nunchanged — common during static selections or repeated compositor events — the cache write is skipped\nentirely, avoiding unnecessary disk I/O. When new content does arrive, buffer ownership is transferred by\nnulling the source pointer (`sel = NULL`) after assigning it to `last_known_content`, rather than\nduplicating the buffer — eliminating one `malloc` + `memcpy` per selection event.\n\nThe X11 and XWayland agents intentionally skip deduplication: they always increment the sequence counter and\nwrite, because a re-selection of identical text (e.g., deselect then re-select the same word) must still\nfire a new event in the shell for correct mouse-selection tracking.\n\n**Descriptor Safety**\n\n`O_CLOEXEC` is applied to every file descriptor: all `open()`, `pipe2()`, and `memfd_create()` calls include\nthe close-on-exec flag. This prevents file descriptor leaks if the agent forks a clipboard server child\nprocess.\n\n**Sequence Counter Design**\n\nThe sequence counter is seeded from `time(NULL)` at daemon startup. This provides monotonic ordering across\nagent restarts — a newly started agent will always produce sequence values higher than those from the previous\ninstance, preventing the shell from misinterpreting a restart as \"no change.\" The daemon writes the initial\nsequence value to the `seq` file before the shell begins polling, closing the startup race window.\n\n**X11 Atom Handling**\n\n- The native X11 agent (`zes-x11-selection-agent`) uses private atom names (`ZES_SEL`, `ZES_CLIP`) as\n  selection conversion properties. This avoids collisions with properties written by other applications on a\n  shared X server.\n- The XWayland agent (`zes-xwayland-agent`) reuses the standard `PRIMARY` and `CLIPBOARD` atoms directly as\n  property names, which is safe because XWayland provides an isolated per-session X server where no other\n  clients compete for property names.\n- Both agents intern all atom handles once at startup and reuse them for the agent's lifetime — no per-event\n  `XInternAtom()` round-trips to the X server.\n\n**Clipboard Server Lifecycle**\n\nWhen the shell copies text to the clipboard (`--copy-clipboard`), the agent forks a background child process\nthat becomes the clipboard owner and serves paste requests to other applications:\n\n- The parent process exits immediately, returning control to the shell\n- The child calls `setsid()` to create a new session and ignores `SIGHUP` to survive terminal closure.\n  The Wayland agent additionally ignores `SIGPIPE` because paste requestors may close their pipe\n  mid-transfer\n- **X11 / XWayland**: The server advertises `TARGETS`, `UTF8_STRING`, and `XA_STRING`, and serves\n  `SelectionRequest` events in a `poll()` loop with 100 ms timeout. It exits when another application takes\n  clipboard ownership (`SelectionClear`) or after approximately 50 seconds of idle time\n- **Wayland**: The server creates a `wl_data_source` offering multiple MIME types\n  (`text/plain;charset=utf-8`, `text/plain`, `UTF8_STRING`, `STRING`) and responds to `send` callbacks. It\n  exits when the compositor signals ownership loss via the `cancelled` callback\n\n**Adaptive Poll Timeouts** _(selection retrieval)_\n\nWhen reading selection content after a conversion request, the agents use adaptive timeouts to balance\nresponsiveness against syscall frequency:\n\n- **X11 / XWayland**: 5 ms polls for the first 20 ms (catching common fast responses), then 20 ms polls\n  thereafter to reduce syscall rate during slow responses\n- **Wayland**: 500 ms initial timeout covers the IPC round-trip; subsequent read chunks use a 100 ms timeout\n  to detect EOF quickly\n\n**Non-Blocking Clipboard Reads** _(Wayland agent)_\n\nClipboard read pipes are created with `pipe2(O_CLOEXEC)` and configured with `fcntl(fd, F_SETFL, O_NONBLOCK)`\ndirectly — without a preceding `F_GETFL` read — then read via `poll()` + `read()` in a loop with exponential\nbuffer growth (capped at 1 MB for PRIMARY, 4 MB for CLIPBOARD).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eProtocol \u0026 Compositor Compatibility\u003c/b\u003e\u003c/summary\u003e\n\n**Wayland Protocol Integration**\n\nThe Wayland agent connects directly to the compositor via `wl_display_connect()` and negotiates protocol\nsupport through the registry. It handles three distinct compositor architectures:\n\nPRIMARY selection is managed via `zwp_primary_selection_unstable_v1`, which is the standard unstable protocol\nsupported by all major compositors.\n\nClipboard operations use a three-mechanism priority chain, selected based on compositor capabilities:\n\n1. **`ext_data_control_v1`** (preferred) — The standardized successor to the wlroots data-control protocol.\n   Supports clipboard read and write without requiring keyboard focus. The agent prefers this over `zwlr` when\n   both are advertised.\n2. **`zwlr_data_control_unstable_v1`** — The wlroots-originated data-control protocol, serving as fallback\n   when `ext_data_control_v1` is not available. Same capabilities.\n3. **`wl_data_device`** — Core Wayland protocol fallback for compositors without any data-control extension\n   (primarily GNOME/Mutter versions before 47). Requires a valid keyboard focus serial, which the agent\n   obtains by creating a visible surface.\n\nAn additional **OSC 52** path is available for clipboard writes — a fire-and-forget terminal escape sequence\nwritten in a single `write()` call to `/dev/tty`, requiring no Wayland protocol involvement.\n\n**Mutter/GNOME Compatibility**\n\nMutter only delivers PRIMARY selection events to Wayland clients that have a mapped surface. The daemon creates\na permanent 1×1 pixel transparent `xdg_toplevel` surface with an **empty input region** (so it cannot receive\ninput focus or interfere with user interaction). The surface pixel is a fully transparent ARGB value, rendered\nvia a SHM buffer created with `memfd_create()` (or `shm_open()` on systems without `memfd_create`).\n\nFor `--copy-clipboard` on compositors requiring a keyboard focus serial (`wl_data_device` path), a separate\nsurface without an empty input region is created to receive `wl_keyboard.enter` events that carry the serial\nneeded by `wl_data_device.set_selection()`.\n\nThe `xdg_wm_base` ping/pong handler responds to compositor ping requests — failure to respond causes the\ncompositor to mark the client as unresponsive and stop delivering events.\n\n**X11 XFixes Integration**\n\n- The X11 agents use `XFixesSelectSelectionInput()` to subscribe to `SetSelectionOwnerNotifyMask` on the root\n  window\n- Events are delivered by the X server on selection owner changes — no polling is required\n- The main loop uses `poll()` on the X connection file descriptor instead of blocking `XNextEvent()`, because\n  with glibc's `signal()` (which sets `SA_RESTART`), a blocking `XNextEvent` cannot be interrupted by\n  `SIGTERM`. After `poll()` returns, `XPending()` is called to drain Xlib's internal buffer — data may have\n  arrived during a previous `read()` that filled the internal buffer with multiple events.\n\n**XWayland Agent Selection**\n\nOn Wayland sessions where `DISPLAY` is also set (XWayland available), the plugin selects `zes-xwayland-agent`\nover `zes-wl-selection-agent`. The XWayland agent reads selection state through X11 atoms via the XWayland\nbridge, bypassing the Wayland protocol stack entirely. This provides lower latency, avoids the Mutter surface\nrequirement, and offers broader compositor compatibility.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eSelection Detection Architecture\u003c/b\u003e\u003c/summary\u003e\n\n**Shell-Side Detection Path**\n\nThe `_zes_sync_selection_state()` function is called by every widget before acting. Its execution path:\n\n1. `zstat -A stat_info +mtime \"$SEQ_FILE\"` — reads the sequence cache file's mtime via a single `stat()`\n   syscall (Zsh builtin, zero forks)\n2. If the mtime matches `_EDIT_SELECT_LAST_MTIME`, the function returns immediately\n3. If the mtime has changed, the `primary` file is read via `$(\u003cfile)` (Zsh builtin) and\n   `_EDIT_SELECT_NEW_SELECTION_EVENT` is set to 1\n4. The new mtime and an event-fired gate (`_EDIT_SELECT_EVENT_FIRED_FOR_MTIME`) are updated to prevent the\n   same mtime from re-triggering across multiple ZLE callbacks\n\n**ZLE Pre-Redraw Hook**\n\nThe `edit-select::zle-line-pre-redraw` hook is registered via `add-zle-hook-widget` and runs before every\nprompt redraw. It performs:\n\n1. **Amortized liveness probe**: Checks `kill -0 $pid` only if `EPOCHSECONDS \u003e _ZES_LAST_PID_CHECK + 30`.\n   If the agent has died, restarts it transparently.\n2. **Mtime check**: Same `zstat` path as `_zes_sync_selection_state()` — one `stat()` syscall per redraw. On\n   mtime change, reads the `primary` file and sets the event flag.\n\n**Cache File Protocol**\n\n- The agent writes primary content first, then increments and writes the sequence number — this ordering\n  guarantee prevents the shell from reading a partially updated `primary` file\n- The shell reads only the sequence file's mtime as the change signal\n- Full content is read only when a change is confirmed\n- The sequence counter starts from `time(NULL)`, providing monotonic ordering even across agent restarts\n\n**Early Return Conditions**\n\n- Unchanged mtime → immediate return before any selection comparison\n- Mouse replacement disabled → `_zes_detect_mouse_selection()` returns immediately\n- Active keyboard selection → mouse detection path is never entered\n- Stale selection state → invalidated on mtime change, not on a timer\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eTerminal Focus \u0026 Multi-Pane Isolation\u003c/b\u003e\u003c/summary\u003e\n\n**DECSET 1004 Focus Tracking**\n\nTerminal focus tracking is enabled at startup via `printf '\\e[?1004h' \u003e/dev/tty`. The escape sequence is\nwritten to `/dev/tty` rather than stdout to avoid triggering Powerlevel10k instant-prompt console-output\nwarnings. Terminals that do not support DECSET 1004 silently ignore the request; the plugin's behavior is\nunchanged.\n\n**Focus-In Handler**\n\nWhen the terminal pane receives focus (`CSI I` escape sequence), the `_zes_terminal_focus_in` handler:\n\n1. Records the current `seq` file mtime as already-seen (`_EDIT_SELECT_LAST_MTIME`)\n2. Sets `_EDIT_SELECT_EVENT_FIRED_FOR_MTIME = 1`\n3. Clears `_EDIT_SELECT_NEW_SELECTION_EVENT`, `_EDIT_SELECT_ACTIVE_SELECTION`, and\n   `_EDIT_SELECT_PENDING_SELECTION`\n\nThis ensures that selection events written by another pane to the shared cache while this pane was unfocused\nare not mistakenly treated as new mouse selections. Focus events are bound in all keymaps (`emacs`,\n`edit-select`, and `main`).\n\n**Independent Selection State**\n\nEach terminal pane maintains its own selection state in independent shell variables. PRIMARY selection is\ncleared after each cut/paste operation to prevent a subsequent pane's detection from reading a stale value.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eResource Behavior\u003c/b\u003e\u003c/summary\u003e\n\n- All detection and configuration reads use in-memory cached values — no file I/O during normal typing\n- Selection state changes are detected via one `stat()` syscall per keypress; file content is read only when\n  the mtime has changed\n- Agent liveness verification runs at 30-second intervals; it is not issued on individual keystroke operations\n- C agents operate with direct system calls only; no interpreter or scripting runtime is involved at runtime\n- Zsh plugin scripts are compiled to `.zwc` bytecode on first load; source parsing is skipped on all\n  subsequent sessions\n- Cache files reside in `XDG_RUNTIME_DIR` (tmpfs on most Linux distributions), `TMPDIR`, or `/tmp`; on\n  standard systemd-based systems, no disk I/O occurs\n- Integer state flags (`_EDIT_SELECT_DAEMON_ACTIVE`, `_EDIT_SELECT_NEW_SELECTION_EVENT`, etc.) enable fast\n  arithmetic checks without string comparison\n- `EPOCHSECONDS` and `EPOCHREALTIME` (from `zsh/datetime`) provide second-resolution and\n  microsecond-resolution timestamps for liveness probes and selection timing respectively — no `date` forks\n- The cache holds only the current selection state; stale entries are not accumulated\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eClipboard Operation Responsiveness\u003c/b\u003e\u003c/summary\u003e\n\nThe following tables document clipboard operation latency for the custom agent, measured with\n`clock_gettime(CLOCK_MONOTONIC)` across multiple payload sizes and iteration counts. All measurements include\nfull end-to-end time: from operation initiation through data availability.\n\n**X11 Clipboard Latency:**\n\n| Test Scenario                          | xclip Avg    | Custom Avg   | Improvement      |\n| -------------------------------------- | ------------ | ------------ | ---------------- |\n| Small text (50 chars, 100 iterations)  | 4.025 ms     | 2.258 ms     | **43.9% faster** |\n| Medium text (500 chars, 50 iterations) | 4.307 ms     | 2.211 ms     | **48.7% faster** |\n| Large text (5KB, 25 iterations)        | 3.949 ms     | 2.310 ms     | **41.5% faster** |\n| Very large (50KB, 10 iterations)       | 4.451 ms     | 2.499 ms     | **43.9% faster** |\n| Rapid consecutive (200 iterations)     | 4.206 ms     | 2.321 ms     | **44.8% faster** |\n| **Overall Average**                    | **4.187 ms** | **2.320 ms** | **44.6% faster** |\n\n**Wayland Clipboard Latency:**\n\n| Test Scenario                          | wl-copy Avg   | Custom Avg   | Improvement      |\n| -------------------------------------- | ------------- | ------------ | ---------------- |\n| Small text (50 chars, 100 iterations)  | 57.073 ms     | 1.966 ms     | **96.6% faster** |\n| Medium text (500 chars, 50 iterations) | 60.382 ms     | 2.441 ms     | **96.0% faster** |\n| Large text (5KB, 25 iterations)        | 63.020 ms     | 1.809 ms     | **97.1% faster** |\n| Very large (50KB, 10 iterations)       | 58.343 ms     | 2.907 ms     | **95.0% faster** |\n| Rapid consecutive (200 iterations)     | 58.860 ms     | 1.546 ms     | **97.4% faster** |\n| **Overall Average**                    | **59.535 ms** | **2.134 ms** | **96.4% faster** |\n\n**Observed Latency:**\n\n- **X11:** 2.320 ms average; 2.211 ms minimum across all payload sizes\n- **Wayland:** 2.134 ms average; 1.546 ms minimum under rapid consecutive operations\n- Latency is consistent across payload sizes from 50 bytes to 50 KB\n- Paste operations retrieve data directly from the in-memory agent cache\n\n**Why the Wayland improvement is larger than X11:**\n\n`wl-copy` forks a new process for every clipboard operation, adding approximately 60 ms of `fork()+exec()`\nand IPC overhead regardless of payload size. `xclip` also forks per operation, but its overhead is\napproximately 4.2 ms — one order of magnitude lower. The persistent agent eliminates the process spawn cost\non both platforms; the remaining latency is the native protocol IPC round-trip time.\n\n**Clipboard Server Behavior:**\n\n- The agent maintains clipboard ownership and responds to paste requests internally, without involving the\n  shell process\n- On X11/XWayland, the clipboard server exits when another application takes clipboard ownership\n  (`SelectionClear` event) or after approximately 50 seconds of inactivity (whichever comes first)\n- On Wayland, the clipboard server exits when the compositor signals ownership loss via the `cancelled`\n  callback\n- If the compiled agents are unavailable, the plugin falls back to `xclip` (X11) or `wl-copy`/`wl-paste`\n  (Wayland) — all functionality is preserved\n\n\u003e **Benchmark Methodology:** Tests conducted using purpose-built C benchmarking tools with\n\u003e `clock_gettime(CLOCK_MONOTONIC)` for nanosecond accuracy. Each iteration measures the full end-to-end path\n\u003e including process spawn, IPC, and data transfer. The benchmark suite is available in\n\u003e [`assets/benchmarks/`](assets/benchmarks/).\n\n\u003c/details\u003e\n\n\u003e Operations complete faster than the\n\u003e [human perception threshold](https://www.tobii.com/resource-center/learn-articles/speed-of-human-visual-perception).\n\n\n---\n\n## Contributing\n\nContributions, suggestions, and recommendations are welcome. If you encounter a bug or unexpected behavior,\nplease [open an issue](https://github.com/Michael-Matta1/zsh-edit-select/issues) with a clear description and\nsteps to reproduce. Pull requests are open for any meaningful improvement — bug fixes, new features, or\ncompatibility with additional environments.\n\nIf you have ideas for enhancements, feature requests, or recommendations to improve the plugin's functionality\nor documentation, feel free to share them. Your feedback helps shape the direction of the project and ensures\nit meets the needs of the community.\n\n**A note on development:** This plugin is developed and tested privately over an extended period before any\npublic release. After every change — whether a fix, enhancement, or new feature — the plugin is heavily tested\nto validate stability and catch regressions under real conditions. New features are typically accompanied by\nnew edge cases; each one is identified and resolved before the code is released. The goal is to ship complete,\nreliable increments rather than incremental works-in-progress. As a result, public commits tend to represent\nsignificant, well-tested milestones rather than a continuous stream of small changes.\n\nIf something does not work as expected, please report it — every issue report directly improves the plugin's\nreliability for everyone.\n\n---\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE).\n\n---\n\n## Acknowledgments\n\n- #### This project began as a fork ([Michael-Matta1/zsh-shift-select](https://github.com/Michael-Matta1/zsh-shift-select)) of [jirutka/zsh-shift-select](https://github.com/jirutka/zsh-shift-select)\n  - The fork was started to add the ability to copy selected text, because the jirutka/zsh-shift-select plugin\n    only supported deleting selected text and did not offer copying by default. Since then, the project has\n    evolved with its own new features, enhancements, bug fixes, design improvements, and a fully changed\n    codebase, and it now provides a full editor-like experience.\n\n- #### Wayland Protocol Specifications\n\n  The bundled Wayland protocol XML files and their `wayland-scanner`-generated C bindings are covered by their\n  respective copyright and license terms:\n  - [`primary-selection-unstable-v1.xml`](impl-wayland/backends/wayland/primary-selection-unstable-v1.xml) —\n    Copyright © 2015, 2016 Red Hat (MIT License)\n  - [`wlr-data-control-unstable-v1.xml`](impl-wayland/backends/wayland/wlr-data-control-unstable-v1.xml) —\n    Copyright © 2018 Simon Ser, © 2019 Ivan Molodetskikh (MIT-like License)\n  - [`ext-data-control-v1.xml`](impl-wayland/backends/wayland/ext-data-control-v1.xml) — Copyright © 2018\n    Simon Ser, © 2019 Ivan Molodetskikh, © 2024 Neal Gompa (MIT-like License)\n\n  The `xdg-shell` binding files follow the same pattern, generated from the `xdg-shell.xml` specification in\n  the wayland-protocols repository.\n\n---\n\n## References\n\n- [Michael-Matta1/dev-dotfiles](https://github.com/Michael-Matta1/dev-dotfiles) — Dotfiles showcasing the\n  plugin with Kitty, VS Code, and Zsh.\n\n- [Zsh ZLE shift selection — StackOverflow](https://stackoverflow.com/questions/5407916/zsh-zle-shift-selection)\n  — Q\u0026A on Shift-based selection in ZLE.\n\n- [Zsh Line Editor Documentation](https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html) — Official ZLE\n  widgets and keybindings reference.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMichael-Matta1%2Fzsh-edit-select","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMichael-Matta1%2Fzsh-edit-select","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMichael-Matta1%2Fzsh-edit-select/lists"}