{"id":46013615,"url":"https://github.com/edumucelli/docking","last_synced_at":"2026-06-13T23:01:40.376Z","repository":{"id":341262385,"uuid":"1169477397","full_name":"edumucelli/docking","owner":"edumucelli","description":"A lightweight, feature-rich dock for Linux written in Python with GTK 3 and Cairo","archived":false,"fork":false,"pushed_at":"2026-06-06T23:05:10.000Z","size":27324,"stargazers_count":55,"open_issues_count":9,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-06-07T00:07:13.610Z","etag":null,"topics":["cairo","dock","dockbar","gtk3","linux","python"],"latest_commit_sha":null,"homepage":"https://www.docking.cc","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edumucelli.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":"2026-02-28T18:42:28.000Z","updated_at":"2026-06-06T21:41:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/edumucelli/docking","commit_stats":null,"previous_names":["edumucelli/docking"],"tags_count":105,"template":false,"template_full_name":null,"purl":"pkg:github/edumucelli/docking","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edumucelli%2Fdocking","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edumucelli%2Fdocking/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edumucelli%2Fdocking/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edumucelli%2Fdocking/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edumucelli","download_url":"https://codeload.github.com/edumucelli/docking/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edumucelli%2Fdocking/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34303280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["cairo","dock","dockbar","gtk3","linux","python"],"created_at":"2026-03-01T01:00:29.847Z","updated_at":"2026-06-13T23:01:40.366Z","avatar_url":"https://github.com/edumucelli.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Docking\n\n[![CI](https://github.com/edumucelli/docking/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/edumucelli/docking/actions/workflows/ci.yml)\n[![Coverage](https://codecov.io/gh/edumucelli/docking/branch/master/graph/badge.svg)](https://codecov.io/gh/edumucelli/docking)\n[![Release](https://img.shields.io/github/v/release/edumucelli/docking?display_name=tag)](https://github.com/edumucelli/docking/releases)\n[![Downloads](https://img.shields.io/github/downloads/edumucelli/docking/total)](https://github.com/edumucelli/docking/releases)\n[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)\n[![GTK 3](https://img.shields.io/badge/GTK-3-blue)](#requirements)\n[![Platform](https://img.shields.io/badge/platform-Linux-lightgrey)](#requirements)\n[![License](https://img.shields.io/github/license/edumucelli/docking)](LICENSE)\n[![Last commit](https://img.shields.io/github/last-commit/edumucelli/docking)](https://github.com/edumucelli/docking/commits/master)\n\n\nA lightweight, feature-rich dock for Linux written in Python with GTK 3 and Cairo. Inspired by [Plank](https://launchpad.net/plank) and [Cairo-Dock](https://github.com/Cairo-Dock), with an extensible applet system for custom widgets.\n\n\n## Contents\n\n- [Highlights](#highlights)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Running](#running)\n- [Configuration](#configuration)\n- [Managing Dock Items](#managing-dock-items)\n- [Applets](#applets)\n- [Theming](#theming)\n- [Writing Custom Applets](#writing-custom-applets)\n- [Translations](#translations)\n- [Developer Workflow](#developer-workflow)\n- [Additional Docs](#additional-docs)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Highlights\n\n- Fast launcher workflow with running indicators, previews, app actions, and drag-and-drop organization.\n- Native Linux desktop integration across X11 and Wayland, with support for GNOME, KDE Plasma, wlroots compositors, MATE, Xfce, Cinnamon, and reduced fallback mode.\n- 57 built-in applets for launching apps and commands, monitoring system state, controlling media, managing notes, files, folders, screenshots, power, networking, weather, and more.\n- Folder stacks and pinned files/folders, so directories and documents can live directly in the dock alongside applications.\n- Flexible dock layout with multi-position, multi-monitor, auto-hide, separators, and scalable sizing.\n- Deep customization through 13 built-in themes, transparency, icon sizing, menu behavior, and tooltip controls.\n- Broad release packaging: AppImage, Debian package, RPM, Flatpak, Snap, Arch package, and Nix output.\n- Desktop integration details such as Unity LauncherEntry badge/progress support, X11 background blur region export, and 74 locale catalogs plus English fallback.\n- Extensible Python applet system for adding custom dock-resident tools without changing the core runtime.\n\n## Requirements\n\n- Linux desktop with X11 or Wayland\n- Python 3.10+\n- X11 remains fully supported.\n- Wayland support is backend-specific:\n  - GNOME / Mutter 45+ through the companion `docking-bridge@docking.org` extension\n  - KDE Plasma 6 through the native KWin backend\n  - wlroots-style compositors through layer-shell and advertised Wayland protocols\n  - reduced mode when compositor integration is unavailable\n- System packages (Ubuntu/Debian):\n\n```bash\nsudo apt install \\\n  python3-venv \\\n  python3-gi python3-gi-cairo \\\n  gir1.2-gtk-3.0 gir1.2-gdkpixbuf-2.0 gir1.2-wnck-3.0 gir1.2-pango-1.0 \\\n  gir1.2-nm-1.0 gir1.2-gstreamer-1.0 \\\n  libcairo2-dev libgirepository1.0-dev pkg-config\n```\n\nNative Wayland layer-shell source installs also need the system\n`gtk-layer-shell` GIR package:\n\n```bash\n# Debian / Ubuntu\nsudo apt install gir1.2-gtklayershell-0.1\n\n# Fedora\nsudo dnf install gtk-layer-shell\n\n# Arch\nsudo pacman -S gtk-layer-shell\n```\n\nRelease packages include or depend on this where native Wayland support is\nadvertised. Source installs must install it separately.\n\nLive Wayland protocol clients in source installs need the `[wayland]` extra:\n\n```bash\n# Debian / Ubuntu build dependencies for pywayland\nsudo apt install libwayland-dev wayland-protocols\n\npip install -e \".[wayland]\"\n```\n\n## Installation\n\nPrebuilt latest release packages are also available on [GitHub Releases](https://github.com/edumucelli/docking/releases), you can download them directly below.\n- `AppImage`: [x64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-x86_64.AppImage), [arm64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-aarch64.AppImage)\n- `Debian .deb`: [x64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-x86_64.deb), [arm64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-aarch64.deb)\n- `RPM`: [x64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-x86_64.rpm), [arm64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-aarch64.rpm)\n- `Flatpak`: [x64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-x86_64.flatpak), [arm64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-aarch64.flatpak)\n- `Snap`: [x64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-x86_64.snap), [arm64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-aarch64.snap)\n- `Arch package`: [x64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-x86_64.pkg.tar.zst), [arm64](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-aarch64.pkg.tar.zst)\n- `Nix`: [x64 store path](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-x86_64-nix-store-path.txt), [x64 output tarball](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-x86_64-nix-output.tar.gz), [arm64 store path](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-aarch64-nix-store-path.txt), [arm64 output tarball](https://github.com/edumucelli/docking/releases/latest/download/docking-latest-linux-aarch64-nix-output.tar.gz)\n\nTypical local install/run commands after downloading a release asset:\n\n```bash\n# AppImage\nchmod +x docking-latest-linux-x86_64.AppImage\n./docking-latest-linux-x86_64.AppImage\nchmod +x docking-latest-linux-aarch64.AppImage\n./docking-latest-linux-aarch64.AppImage\n\n# Debian / RPM / Arch\nsudo apt install ./docking-latest-linux-x86_64.deb\nsudo apt install ./docking-latest-linux-aarch64.deb\nsudo dnf install ./docking-latest-linux-x86_64.rpm\nsudo dnf install ./docking-latest-linux-aarch64.rpm\nsudo pacman -U ./docking-latest-linux-x86_64.pkg.tar.zst\nsudo pacman -U ./docking-latest-linux-aarch64.pkg.tar.zst\n\n# Flatpak / Snap\nflatpak install --user ./docking-latest-linux-x86_64.flatpak\nflatpak install --user ./docking-latest-linux-aarch64.flatpak\nsudo snap install --dangerous ./docking-latest-linux-x86_64.snap\nsudo snap install --dangerous ./docking-latest-linux-aarch64.snap\n\n# Nix output tarball\nmkdir docking-nix-output\ntar -C docking-nix-output -xf docking-latest-linux-x86_64-nix-output.tar.gz\n./docking-nix-output/bin/docking\n```\n\n```bash\n# Clone\ngit clone https://github.com/edumucelli/docking.git\ncd docking\n\n# Create venv with access to system GI bindings\npython3 -m venv --system-site-packages .venv\nsource .venv/bin/activate\n\n# Install with dependencies\npip install -e \".[dev]\"\n```\n\nOr with [uv](https://docs.astral.sh/uv/):\n\n```bash\nuv venv --python /usr/bin/python3 --system-site-packages .venv\nsource .venv/bin/activate\nuv pip install -e \".[dev]\"\n```\n\n## Running\n\n```bash\n# Via entry point\ndocking\n\n# Or directly\npython run.py\n\n# With debug logging\nDOCKING_LOG_LEVEL=DEBUG python run.py\n```\n\n### Wayland Support\n\nDocking selects a backend from the current desktop session. You can also force\none with `DOCKING_BACKEND`.\n\n| Backend | Compositor | Coverage |\n|---|---|---|\n| **GNOME Shell bridge** | GNOME / Mutter 45+ | Full: dock placement, window tracking, window actions (activate / minimize / close), window previews, workspace switching, Show Desktop, Alt+Tab hiding |\n| **KWin** | KDE Plasma 6 Wayland | Dock placement (layer-shell), window tracking with titles via AT-SPI accessibility bus, workspace switching via KWin D-Bus. No window actions (KWin 6 does not expose a public activate/close/minimize protocol) |\n| **Native layer-shell** | wlroots-based (Hyprland, Sway) | Dock placement, window tracking, workspace switching (varies by compositor protocol support) |\n| **Reduced** | Any Wayland | Dock visible but no window management (no running indicators, no previews, no workspace switching) |\n\n#### GNOME Shell Bridge\n\nOn GNOME, Docking uses a companion GNOME Shell extension\n(`docking-bridge@docking.org`) that provides window management, previews,\nworkspace switching, and Show Desktop over a private session D-Bus interface.\n\n**How to enable:**\n```bash\n# Install and enable the extension (one-time)\ntools/gnome_bridge.sh install\n\n# Run the dock with the GNOME Shell bridge backend\nDOCKING_BACKEND=gnome-shell docking\n```\n\nSystem packages include the extension. AppImage and Nix users should run\n`tools/gnome_bridge.sh install` once, or copy\n`docking/platform/backends/gnome/extension/` into the GNOME Shell user\nextensions directory.\n\n#### KWin / KDE Plasma 6\n\nOn KDE Plasma 6 Wayland, Docking uses a native KWin backend with Wayland\nlayer-shell dock placement, running-window indicators, and workspace\nswitching.\n\n**What works:**\n- Proper layer-shell anchored dock positioning\n- Running window indicators with titles\n- Workspace list and switching\n- Workspace-aware filtering\n\n**Not available through public KWin 6 APIs:**\n- Window actions (activate, minimize, close) — KWin 6 has no public\n  protocol for third-party window management\n- Window previews — no capture protocol available\n- Active-window highlighting — KWin does not expose the focused window\n  through a public API\n\nNo extra configuration is needed. The backend auto-detects a KDE Plasma\nsession and is also selectable with `DOCKING_BACKEND=kwin`.\n\n#### Native layer-shell\n\nNative layer-shell mode needs a compositor with `zwlr_layer_shell_v1`.\nSource installs also need the system `gtk-layer-shell` GIR package:\n\n```bash\n# Debian / Ubuntu\nsudo apt install gir1.2-gtklayershell-0.1\n\n# Fedora\nsudo dnf install gtk-layer-shell\n\n# Arch\nsudo pacman -S gtk-layer-shell\n```\n\nSource installs that use live protocol clients also need the `[wayland]` extra:\n```bash\nsudo apt install libwayland-dev wayland-protocols\npip install -e \".[wayland]\"\n```\n\nCheck capabilities:\n```bash\nwayland-info | grep -E 'zwlr_layer_shell_v1|zwlr_foreign_toplevel_manager_v1|ext_workspace_manager_v1'\n```\n\nTo force a specific backend for testing:\n```bash\nDOCKING_BACKEND=gnome-shell docking          # GNOME / Mutter 45+\nDOCKING_BACKEND=kwin docking                  # KDE Plasma 6 Wayland\nDOCKING_BACKEND=wayland-layer-shell docking   # wlroots compositors\nDOCKING_BACKEND=reduced docking               # any Wayland (no WM integration)\nDOCKING_BACKEND=x11 docking                   # X11 (full support)\n```\n\n## Configuration\n\nConfig is stored at `~/.config/docking/dock.json` (auto-created on first run). New installs are seeded with a starter dock: Applications, a set of common launchers detected from what is installed, then Clock, Calendar, Weather, System Monitor, Hydration, Notifications, and Session.\n\n```json\n{\n  \"icon_size\": 48,\n  \"zoom_enabled\": true,\n  \"zoom_percent\": 1.5,\n  \"zoom_range\": 3,\n  \"position\": \"bottom\",\n  \"monitor_index\": -1,\n  \"hide_mode\": \"none\",\n  \"hide_delay_ms\": 0,\n  \"unhide_delay_ms\": 0,\n  \"hide_time_ms\": 250,\n  \"previews_enabled\": true,\n  \"tooltips_enabled\": true,\n  \"lock_icons\": false,\n  \"current_workspace_only\": false,\n  \"anchor_applets\": false,\n  \"anchor_files\": false,\n  \"active_display\": false,\n  \"left_click_action\": \"toggle\",\n  \"middle_click_action\": \"new-window\",\n  \"folder_stack_unfold\": \"hover\",\n  \"window_list_sort\": \"default\",\n  \"show_window_count_numbers\": false,\n  \"theme\": \"default\",\n  \"transparency\": 1.0,\n  \"additional_distance_from_edge\": 0,\n  \"pressure_reveal_enabled\": false,\n  \"pressure_threshold\": 50,\n  \"pinned\": [\n    { \"kind\": \"applet\", \"target\": \"applet://applications\" },\n    { \"kind\": \"app\", \"target\": \"firefox.desktop\" },\n    { \"kind\": \"app\", \"target\": \"org.gnome.Nautilus.desktop\" },\n    { \"kind\": \"applet\", \"target\": \"applet://clock\" }\n  ],\n  \"applet_prefs\": {},\n  \"item_prefs\": {}\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `icon_size` | 48 | Base icon size in pixels (all theme proportions scale with this) |\n| `zoom_enabled` | true | Enable or disable parabolic zoom on hover |\n| `zoom_percent` | 1.5 | Zoom multiplier from `1.0` to `4.0` (`1.5` = 150%, `4.0` = 400%) |\n| `zoom_range` | 3 | Icon widths over which zoom tapers off |\n| `position` | bottom | Dock edge: bottom, top, left, right |\n| `monitor_index` | -1 | Target monitor index (`-1` = primary monitor, `0..N` = specific monitor) |\n| `hide_mode` | none | Dock hide behavior: `none`, `always-on-top`, `autohide`, `intelligent`, `dodge-active`, `window-dodge`, `dodge-maximized` |\n| `hide_delay_ms` | 0 | Delay before hiding starts (0 = instant) |\n| `unhide_delay_ms` | 0 | Delay before showing the dock again |\n| `hide_time_ms` | 250 | Duration of hide/show slide animation |\n| `previews_enabled` | true | Show window preview thumbnails on hover |\n| `lock_icons` | false | Prevent reordering, drag-in, and drag-off removal |\n| `current_workspace_only` | false | Only show running apps from the active workspace |\n| `anchor_applets` | false | Keep applets anchored at the end of the dock |\n| `anchor_files` | false | Keep file and folder entries anchored at the end independently |\n| `tooltips_enabled` | true | Show hover tooltips for dock items |\n| `active_display` | false | Follow the active monitor instead of staying on one display |\n| `update_check_enabled` | true | Check GitHub for newer Docking releases |\n| `update_check_interval_hours` | 24 | Minimum hours between automatic update checks |\n| `left_click_action` | toggle | Running-app left click: `toggle`, `cycle`, or `most-recent` |\n| `middle_click_action` | new-window | Application middle click: `new-window`, `minimize`, or `close-focused` |\n| `folder_stack_unfold` | hover | Folder stack open behavior: `hover` or `click` |\n| `window_list_sort` | default | Open-window menu order: `default` or `alphabetical` |\n| `show_window_count_numbers` | false | Show numeric window counts inside running indicators |\n| `theme` | default | Theme name (loads from `~/.config/docking/themes/{name}.json` first, then built-in themes) |\n| `transparency` | 1.0 | Multiplier applied to theme alpha from `0.15` to `1.0` (`1.0` = full theme opacity) |\n| `additional_distance_from_edge` | 0 | Extra pixels added to the theme's edge gap |\n| `pressure_reveal_enabled` | false | Require pointer-barrier pressure before revealing a hidden X11 dock |\n| `pressure_threshold` | 50 | Pointer pressure threshold in pixels, from `5` to `500` |\n| `pinned` | [] | Ordered pinned entries for apps, applets, files, and folders. First run seeds a starter set. |\n| `applet_prefs` | `{}` | Per-applet preference storage |\n| `item_prefs` | `{}` | Per-item preference storage for files and folders |\n\n`hide_mode` meanings:\n\n- `none`: Dock stays visible and reserves screen space.\n- `always-on-top`: Dock stays visible above all windows without reserving screen space.\n- `autohide`: Dock hides when the cursor leaves.\n- `intelligent`: Dock hides when a window from the focused app overlaps the dock.\n- `dodge-active`: Dock hides when the focused window overlaps the dock.\n- `window-dodge`: Dock hides when any window on the current workspace overlaps the dock.\n- `dodge-maximized`: Dock hides when the focused window is maximized or a dialog overlaps the dock.\n\nAll settings are also configurable via the dock's right-click menu. On multi-monitor setups, use **Display** to move the dock to another monitor. The preferences window also exposes **Mouse** actions so left click can toggle, cycle, or focus the most recently used window of the running app, and middle click can open a new window, minimize the app windows, or close the app's focused window. Pick the left-click mode under right-click -\u003e **Preferences** -\u003e **Behavior** -\u003e **Mouse**. Update checks live under right-click -\u003e **Preferences** -\u003e **Updates**, where you can disable automatic checks, choose daily or weekly checks, check immediately, or open the releases page. Runtime support details live under right-click -\u003e **Diagnostics**, which shows the selected backend, session environment, available platform features, optional helpers, and a copyable report for support requests.\n\nDocking stores update-check preferences in `dock.json`. Runtime update state,\nsuch as the last checked timestamp, ignored release version, and remind-later\ntimestamp, is stored separately under XDG state storage in\n`~/.local/state/docking/updates.json`.\n\n## Managing Dock Items\n\n- **Drag and drop**: Drag a `.desktop` file, and application, a folder or a file from your file manager onto the dock\n- **Right-click running app**: \"Keep in Dock\" to pin\n- **Drag off**: Drag an icon upward off the dock to remove (poof animation)\n- **Right-click pinned app**: \"Remove from Dock\" to unpin\n- **Edit config**: Add desktop IDs to `\"pinned\"` in `dock.json`\n\n## Theming\n\nThemes are JSON files. Docking loads user themes from:\n\n```text\n~/.config/docking/themes/\n```\n\nand then falls back to the built-in themes bundled in `docking/assets/themes/`.\nThirteen built-in themes are included:\n\n- `default` -- light theme\n- `onyx` -- dark variant\n- `slate` -- flat appearance\n- `transparent` -- minimal, see-through\n- `olive` -- rounded olive-green theme\n- `ember` -- warm dark theme\n- `nord` -- cool, desaturated dark\n- `glass` -- translucent macOS-style floating pill\n- `pill` -- dark floating pill with fully rounded borders\n- `paper` -- matte warm floating pill\n- `candy` -- playful pastel floating pill\n- `gruvbox` -- warm earthy dark\n- `solarized` -- soft light Solarized variant\n\nAll layout values use a **scaling unit** (tenths of a percent of `icon_size`). This means themes adapt automatically to any icon size.\n\nTheme field names use suffixes to make value types clear:\n\n- `_px` means a raw pixel value in JSON/runtime, such as `shelf.stroke_width_px` or `layout.distance_from_edge_px`.\n- No unit suffix means a theme scale unit in JSON, converted to pixels at runtime, such as `layout.horizontal_padding`, `layout.top_padding`, `layout.bottom_padding`, and `layout.item_padding`.\n- `_ms` means milliseconds.\n- `_ratio` means a relative fraction, such as a bounce height relative to icon size.\n- `_color` means an RGBA color array stored as `[red, green, blue, alpha]` in 0-255 values.\n\nBoolean, enum/string-choice, and count fields are exceptions and stay semantic without a type suffix, for example `shelf.round_bottom`, `indicators.style`, and `indicators.max_dots`.\n\nTheme layout also controls edge spacing through `layout.distance_from_edge_px`, which is how floating themes such as `slate` keep the dock visually separated from the screen edge.\n\nOlder flat theme fields such as `h_padding` are migrated automatically when a user theme is loaded.\n\n**Theme fields:**\n\n`shelf.*` -- the dock background bar:\n\n| Key | Default | Values | Notes |\n|---|---|---|---|\n| `shelf.fill_start_color` | `[222, 222, 222, 240]` | `[r, g, b, a]` (0-255) | Top color of the shelf vertical gradient. |\n| `shelf.fill_end_color` | `[247, 247, 247, 240]` | `[r, g, b, a]` | Bottom color of the shelf gradient. |\n| `shelf.stroke_color` | `[145, 145, 145, 255]` | `[r, g, b, a]` | Outer border color. |\n| `shelf.stroke_width_px` | `1.0` | px | Outer border thickness. |\n| `shelf.inner_stroke_color` | `[248, 248, 248, 255]` | `[r, g, b, a]` | Inset highlight stroke drawn 1px inside the outer border. |\n| `shelf.corner_radius_px` | `5` | px | Rounded-corner radius. |\n| `shelf.round_bottom` | `false` | bool | Round the bottom corners too (vs. square flush with the screen edge). |\n\n`layout.*` -- item placement and edge spacing (scale units unless suffixed):\n\n| Key | Default | Values | Notes |\n|---|---|---|---|\n| `layout.horizontal_padding` | `0` | scale units | Gap on each side of the item run. Values `\u003c= 0` fall back to `2 * stroke_width`. |\n| `layout.top_padding` | `-7` | scale units | Vertical offset of the shelf top relative to the icon top. Negative values make icons overflow above the shelf. |\n| `layout.bottom_padding` | `1` | scale units | Gap between the shelf bottom and the screen edge. |\n| `layout.item_padding` | `2.5` | scale units | Horizontal gap between adjacent icons. |\n| `layout.distance_from_edge_px` | `0` | px | Gap between the dock and the screen edge. Floating themes (e.g. `slate`, `pill`) use this to lift the dock away from the edge. User setting `additional_distance_from_edge` is added on top of this. |\n\n`indicators.*` -- running-app indicators below each icon:\n\n| Key | Default | Values | Notes |\n|---|---|---|---|\n| `indicators.style` | `\"dots\"` | `\"dots\"`, `\"dashes\"` | Indicator shape under running apps. |\n| `indicators.fill` | `\"flat\"` | `\"flat\"`, `\"glow\"` | Rendering style. `flat` is a solid disc/line; `glow` is a soft radial halo around each dot/dash. |\n| `indicators.inactive_color` | `[80, 80, 80, 200]` | `[r, g, b, a]` | Color for running but not focused. |\n| `indicators.active_color` | `[50, 50, 50, 255]` | `[r, g, b, a]` | Color for the currently focused app. |\n| `indicators.size_px` | `5` | px | Indicator diameter (radius = `size_px / 2`). |\n| `indicators.max_dots` | `4` | int | Maximum number of dots shown when an app has multiple windows. Above this, a count badge is drawn. |\n\n`items.hover.*` -- hover lighten effect:\n\n| Key | Default | Values | Notes |\n|---|---|---|---|\n| `items.hover.lighten_amount` | `0.2` | 0.0-1.0 | Additive brightness applied to the hovered icon. |\n| `items.hover.fade_ms` | `150` | ms | Fade in/out duration for the hover lighten. |\n\n`items.bounce.*` -- icon bounce animations:\n\n| Key | Default | Values | Notes |\n|---|---|---|---|\n| `items.bounce.urgent_height_ratio` | `1.66` | fraction of `icon_size` | Peak height of the urgent-window bounce. |\n| `items.bounce.urgent_time_ms` | `600` | ms | Duration of one urgent bounce. |\n| `items.bounce.launch_height_ratio` | `0.625` | fraction of `icon_size` | Peak height of the app-launch bounce. |\n| `items.bounce.launch_time_ms` | `600` | ms | Duration of one launch bounce. |\n| `items.bounce.click_time_ms` | `300` | ms | Duration of the click feedback bounce. |\n\n`items.glow.*` -- active-app shelf glow and urgent halo:\n\n| Key | Default | Values | Notes |\n|---|---|---|---|\n| `items.glow.active_shape` | `\"linear\"` | `\"linear\"`, `\"radial\"`, `\"flat\"` | `linear` = vertical gradient under the icon (default). `radial` = halo centered behind the icon. `flat` = solid color fill. |\n| `items.glow.active_tint` | `\"icon\"` | `\"icon\"`, `\"theme\"` | `icon` = tint the glow with the icon's averaged color. `theme` = use `items.glow.active_color`. |\n| `items.glow.active_color` | mirrors `indicators.active_color` | `[r, g, b, a]` | Consumed only when `active_tint = \"theme\"`. |\n| `items.glow.active_opacity_ratio` | `0.6` | 0.0-1.0 | Maximum alpha of the active-app glow gradient (linear/radial) or solid fill (flat). |\n| `items.glow.urgent_time_ms` | `10000` | ms | How long the urgent halo around an icon stays visible after the urgent flag is set. |\n| `items.glow.urgent_pulse_ms` | `2000` | ms | One pulse cycle period for the urgent halo. |\n| `items.glow.urgent_size_ratio` | `0.6` | fraction of `icon_size` | Radius of the urgent halo. |\n\n**Creating a custom theme:**\n- Docking creates `~/.config/docking/themes/template.json` on startup.\n- Copy `template.json` to a new name, such as `my-theme.json`, then edit it.\n- `template.json` is hidden from the selector; renamed `.json` files appear as themes.\n\n## Applets\n\nApplets are custom widgets that live in the dock alongside application icons. Enable them via right-click on the dock background -\u003e **Applets**.\n\n### Applet Architecture\n\nDocking applets follow a small, testable architecture:\n\n- `docking/applets/base.py` defines the common applet lifecycle and UI hooks:\n  - `create_icon(size)`\n  - `on_clicked()`\n  - `on_scroll(direction_up)`\n  - `get_menu_items()`\n  - optional `start(notify=...)` / `stop()`\n- Most applets are organized as a package with three modules:\n  - `state.py`: pure logic, parsing, command/state helpers (easy to unit test)\n  - `render.py`: Cairo/icon rendering helpers (no applet lifecycle logic)\n  - `applet.py`: GTK/Wnck/Gio wiring, timers, click/scroll/menu behavior\n- Package `__init__.py` stays metadata-only: declare `meta = AppletMeta(...)` there and keep imports cheap for startup discovery.\n- Applet metadata is auto-discovered through `docking/applets/__init__.py:get_applet_catalog()`.\n- Concrete applet classes are loaded on demand through `docking/applets/__init__.py:load_applet_class()`.\n- Each applet package declares a stable identity via `AppletMeta` in `__init__.py`.\n\nThis split keeps runtime behavior in one place while making parsers/rendering highly testable without a live desktop session.\n\n### AI Usage\n\n\nTracks Claude Code, Codex CLI, and OpenCode usage from the dock.\n\n**Scroll:** Cycle provider focus between Auto, Claude, Codex, and OpenCode\n**Right-click options:**\n- **Auto / Claude / Codex / OpenCode** -- filter the displayed provider\n- **Reset Today** -- clear today’s tracked usage\n\n**Tooltip:** Today/week cost summary plus per-model usage for the selected provider\n\n**Update interval:** Updates when usage changes, plus a periodic refresh for providers that need polling\n\n**Preferences stored:** rolling `days` usage history in `applet_prefs.aiusage`\n\n### Clock\n\n\nAnalog or digital clock face. Optional seconds display adds a red seconds hand in analog mode and `HH:MM:SS` in digital mode, and the applet can keep a simple one-shot alarm reminder.\n\n**Click:** Acknowledge a ringing alarm\n**Right-click options:**\n- **Digital Clock** -- switch between analog and digital display\n- **24-Hour Clock** -- toggle 12/24-hour format\n- **Show Date** -- show date below time (digital mode only)\n- **Show Seconds** -- refresh every second and show seconds on the icon\n- **Set Alarm...** -- choose an hour/minute for the next one-shot reminder\n- **Clear Alarm** -- remove a pending alarm\n- **Acknowledge Alarm** -- clear the urgent reminder after it fires\n\n**Preferences stored:** `show_digital`, `show_military`, `show_date`, `show_seconds`, `alarm_target`\n\n### Alarm\n\n\nMultiple alarm presets with local-time scheduling, weekday repeats, one-shot alarms, snooze, and dismiss controls. The icon shows a rounded alarm clock with a compact next-alarm countdown, and switches to a ringing label when an alarm fires.\n\n**Click:** Open the alarm editor, or dismiss the current ringing alarm\n**Right-click options:**\n- **Add Alarm...** -- create a new alarm preset\n- **Snooze** -- move the current ringing alarm forward by its preset snooze duration\n- **Dismiss** -- stop the current ringing alarm\n- **Alarm preset rows** -- enable or disable saved presets from the menu\n- **Edit {label}...** -- edit or remove a saved preset\n\n**Tooltip:** Next enabled alarm with local time, or the currently ringing alarm label.\n\n**Preferences stored:** `presets` with `label`, `hour`, `minute`, `enabled`, `repeat_days`, `snooze_minutes`, `last_triggered`, and `snoozed_until`\n\n**Update interval:** 30 seconds normally, 1 second while ringing\n\n### Trash\n\n\nShows the current state of the system trash. Icon switches between empty and full automatically.\n\n**Click:** Open trash folder in file manager\n**Right-click options:**\n- **Open Trash** -- open in file manager\n- **Empty Trash** -- permanently delete all trashed items\n\n### USB Watch\n\n\nShows mounted removable USB storage devices and provides safe-remove actions without opening a file manager.\n\n**Tooltip:** mounted device count and mount paths\n**Right-click options:**\n- **Safely Remove _device_** -- unmount and eject a removable USB device when supported\n\n### Desktop\n\n\nToggle \"show desktop\" mode -- minimizes or restores all windows.\n\n**Click:** Toggle show/hide all windows\n\n### System Monitor\n\n\nCircular gauge showing real-time CPU and memory usage. The fill color shifts from green (idle) to red (busy). A white arc around the edge shows memory usage.\n\n**Tooltip:** `CPU: 23.5% | Mem: 67.2% | Temp: 54.0°C` when CPU temperature is available\n\n**Update interval:** 1 second\n\n### Thermals\n\n\nHottest lm-sensors temperature plus fastest fan RPM. The icon is a thermometer with a degree-only bottom label for the current temperature, and the tooltip includes the lm-sensors chip and label for both readings.\n\n**Click:** No-op\n**Right-click options:**\n- **Temperature Unit** -- Celsius or Fahrenheit\n- **Refresh Now**\n\n**Tooltip:** `Hot: coretemp Package 72.4C` and `Fan: thinkpad fan1 2987 RPM`\n\n**Update interval:** 5 seconds\n\n### Battery\n\n\nShows battery charge level using standard icons. The icon changes based on charge level and charging state.\n\n**Right-click options:**\n- **Power Settings** -- open the desktop power settings or power management screen when available\n\n**Tooltip:** Shows percentage and, when the system exposes a battery rate, the estimated time left or time until full. If no estimate is available, it keeps the tooltip simple.\n\n**Update interval:** 60 seconds\n\n### Brightness\n\n\nScreen brightness control with a live level indicator.\n\n**Click:** Reset brightness to 100%\n**Scroll:** Adjust brightness by small steps\n**Right-click options:**\n- **Show Level** -- toggle percentage text overlay on icon\n\n**Tooltip:** `Brightness: N%`\n\n**Update interval:** 5 seconds\n\n### Weather\n\n\nShows current weather and air quality for a selected city with a 5-day forecast.\n\n**Click:** Open city search and add/switch the active city\n**Right-click options:**\n- **Show Temperature** -- toggle temperature overlay on icon\n- **Temperature Unit** -- Celsius or Fahrenheit\n- **Remove {city}** -- remove active city when multiple cities are configured\n\n**Scroll:** Cycle through configured cities\n\n**Tooltip:** Bold city header + current conditions + air quality + daily forecast with icons:\n```\nContagem, Brazil\n29°C, Clear sky\nAir: Good\nMon: 25/29°C, Partly cloudy\nTue: 28/32°C, Rain\n```\n\n**Preferences stored:** `city_display`, `lat`, `lng`, `show_temperature`, `temperature_unit`\n\n**Update interval:** 5 minutes\n\n### Sunrise\n\n\nSunrise, sunset, and twilight countdown applet for a selected city. The icon is a rendered 24-hour solar dial with night, astronomical, nautical, civil, and daylight bands plus a current-time marker.\n\n**Click:** Open city search and add/switch the active city\n**Right-click options:**\n- **Label Mode** -- switch between next-event countdown, current phase, and sunrise/sunset times\n- **Remove {city}** -- remove active city when multiple cities are configured\n\n**Scroll:** Cycle through configured cities\n\n**Tooltip:** Selected city, current solar phase, next solar event countdown, and today's solar event times. Times are calculated locally from the city coordinates and shown in the system timezone.\n\n**Preferences stored:** `cities`, `active_index`, `label_mode`\n\n**Update interval:** 60 seconds\n\n### Moon\n\n\nMoon phase applet with a rendered moon disc and illumination shading.\n\n**Click:** Refresh moon data now\n**Right-click options:**\n- **Show Phase Name** -- toggle phase label overlay on icon\n- **Refresh** -- force a refresh\n\n**Tooltip:** Multi-line phase summary with illumination percentage and description\n\n**Update interval:** 6 hours\n\n### Clippy\n\n\nClipboard history manager. Monitors the system clipboard and stores the last 15 text entries.\n\n**Click:** Copy the currently selected clip back to the clipboard\n**Scroll:** Cycle through clipboard history (tooltip updates instantly)\n**Right-click:** List of all clips (newest first), click to copy. \"Clear\" to empty history.\n\n**Preferences stored:** `max_entries`\n\n### Bookmarks\n\n\nBookmarks launcher for pinned URLs.\n\n**Click:** Open the first saved bookmark in the default browser\n**Right-click options:**\n- **Add Bookmark...** -- save a name + URL pair\n- individual bookmark entries -- open that bookmark directly\n- **Remove All** -- clear the saved bookmark list\n\n**Tooltip:** summary of the saved bookmark set\n\n### Quick Note\n\n\nSticky note applet for a single quick text note.\n\n**Click:** Open the note editor dialog\n**Right-click options:**\n- **Edit Note** -- open the editor\n- **Clear Note** -- empty the note\n\n**Tooltip:** note preview or empty-note fallback\n\n### Recent Files\n\n\nLauncher for the most recently opened files.\n\n**Click:** Open the newest recent file\n**Right-click options:**\n- recent file entries -- open the selected file\n- **Clear Recent Files** -- purge the recent-files list\n\n**Tooltip:** most recent file name or empty-state fallback\n\n### Color Picker\n\n\nEyedropper color picker. Click enters fullscreen pick mode, samples a pixel color, copies hex value to clipboard, and updates the icon swatch.\n\n**Click:** Start pick mode and sample next clicked pixel\n**Right-click options:**\n- **Copy #RRGGBB** -- copy current sampled value\n- **Show Hex** -- toggle hex label overlay on icon\n\n**Tooltip:** Current sampled hex value\n\n**Preferences stored:** `show_hex`, `r`, `g`, `b`, `hex`\n\n### Applications\n\n\nCategorized application launcher. Groups all installed `.desktop` applications by FreeDesktop category (Multimedia, Development, Internet, etc.) with icons.\n\n**Click:** Open the categorized launcher menu. The top of the menu includes a search field that filters applications as you type.\n\n### Keyboard Layout\n\n\nKeyboard layout switcher with a compact keyboard icon and active layout code overlay.\n\n**Click:** Cycle to the next available layout\n**Scroll:** Move forward/backward through available layouts\n**Right-click options:**\n- **Keyboard Settings** -- open the desktop keyboard settings screen when available\n- **Show Current Layout** -- open the current keyboard layout dialog when available\n- direct selection of each detected layout\n\n**Tooltip:** active layout code or no-layout fallback\n\n### Caps Lock\n\n\nCaps Lock and Num Lock indicators for keyboards without physical lights. The icon shows which locks are currently active.\n\n**Click:** Refresh lock state immediately\n**Right-click options:**\n- Current Caps Lock and Num Lock states\n- Refresh Now\n\n**Tooltip:** Caps Lock and Num Lock on/off state, or an unavailable-state fallback\n\n**Update interval:** 1 second\n\n### Network\n\n\nShows WiFi signal strength or wired connection status, with live upload/download speed overlay.\n\n**Tooltip:**\n```\nWiFi: MyNetwork (82%)\nIP: 192.168.1.42\ndown-arrow 1.2 MB/s  up-arrow 350 KB/s\n```\n\n**Right-click options:**\n- **Available Networks** -- open a submenu of visible Wi-Fi networks; clicking one asks NetworkManager to connect to it\n- **Connect to Hidden Wi-Fi Network...** -- open the desktop network editor/settings flow for hidden Wi-Fi setup\n- **Create New Wi-Fi Network...** -- open the desktop network editor/settings flow for creating a new Wi-Fi network\n- **VPN Connections** -- open a submenu of saved VPN profiles and toggle them on or off\n- **Connection Information** -- open the desktop network settings or information screen when available\n- **Edit Connections...** -- open the connection editor when available\n- **Enable Networking** -- toggle NetworkManager networking on/off\n- **Enable Wi-Fi** -- toggle Wi-Fi radio on/off when a wireless device is present\n- **Show Download / Show Upload / Hide Speeds** -- control the speed overlay on the icon\n\n**Update interval:** 2 seconds\n\n### Bluetooth\n\n\nBluetooth manager applet for quick adapter and device control from the dock.\n\n**Click:** Toggle Bluetooth power for the active adapter\n**Right-click options:**\n- **Turn Bluetooth On / Turn Bluetooth Off** -- power toggle for the active adapter\n- **Disconnect {device}** -- quick disconnect action for connected devices\n- **Send Files to Device...** -- open the desktop Bluetooth file sender when available\n- **Recent Connections** -- reopen recently connected paired devices\n- **Devices...** -- open the desktop Bluetooth devices/settings screen when available\n- **Adapters...** -- open the desktop Bluetooth adapter/settings screen when available\n- **Local Services...** -- open the desktop Bluetooth local-services screen when available\n- **Continuous Discovery** -- keeps discovery active while enabled\n- **Adapter** -- switch active adapter on multi-adapter systems\n- **Connected / Paired / Discovered Devices** -- per-device actions:\n  connect/disconnect, pair, remove pairing, trust toggle\n\n**Tooltip:** adapter state, connected/paired counts, discovery status, optional battery line\n**Badge:** connected device count\n\n**Update interval:** 2 seconds\n\n### Cam Shield\n\n\nCamera privacy indicator. The icon shows a red dot while an app is using a camera.\n\n**Right-click options:**\n- Active app list when available\n- Lock Camera / Unlock Camera\n- Refresh Now\n\n**Tooltip:** Shows whether the camera is idle, active, or unavailable, plus active holders when detected\n\nLocking blocks new camera sessions. Apps that are already using the camera may need to be closed first.\n\n**Update interval:** 2 seconds\n\n### Mic Shield\n\n\nMicrophone privacy indicator and mute toggle. The icon shows a red dot while an app is using microphone input, and clicking the applet quickly mutes or unmutes the microphone.\n\n**Click:** Toggle microphone mute\n**Right-click options:**\n- Active app list when available\n- Mute Microphone / Unmute Microphone\n- Refresh Now\n\n**Tooltip:** Shows mute state, idle/active state, and active capture streams when detected\n\n**Update interval:** 2 seconds\n\n### Power Profiles\n\n\nPower profile applet for quick laptop/handheld mode switching.\n\n**Click:** Cycle to next available profile\n**Right-click options:**\n- **Select Profile** -- radio selector for available profiles\n- **Power Saver / Balanced / Performance** -- set active profile\n\n**Tooltip:** current profile and available profiles\n\n### Caffeine\n\nKeeps the session awake for a selected duration or indefinitely.\n\n**Click:** Toggle inhibit on/off\n**Right-click:** Duration presets and status\n\n### Notifications\n\n\nNotification center applet with a compact status icon, Do Not Disturb toggle, and pending badge when available.\n\n**Click:** Toggle Do Not Disturb on/off\n**Right-click options:**\n- **Do Not Disturb** -- toggle notification pause state\n- **Pending: N** -- pending notifications (when available)\n- **Clear Notifications** -- clear notification history (when available)\n\n**Update interval:** 2 seconds\n\n### Session\n\n\nLock, log out, suspend, restart, or shut down from the dock.\n\n**Click:** Lock screen\n**Right-click options:**\n- **Lock Screen**\n- **Log Out**\n- **Suspend**\n- **Restart**\n- **Shut Down**\n\n### Calendar\n\n\nShows today's date as a calendar page icon with red header (weekday) and day number.\n\n**Click:** Toggle a calendar popup\n**Tooltip:** Full date (e.g. \"Tuesday, February 25\")\n\n**Update interval:** 30 seconds (refreshes icon at midnight)\n\n### Workspaces\n\n\nWorkspace switcher with a visual grid icon. Active workspace is highlighted in blue.\n\n**Click:** Cycle to next workspace\n**Scroll:** Switch workspace up/down\n**Right-click options:** Radio list of all workspaces\n\n**Tooltip:** Active workspace name\n\n### Screenshot\n\n\nCapture screenshots with the available screenshot tool on your system.\n\n**Click:** Full-screen capture\n**Right-click options:**\n- **Full Screen** -- capture entire screen\n- **Window** -- capture active window\n- **Region** -- interactive area selection\n- **Full Screen in 3s/5s/7s/9s** -- delayed full-screen capture\n\n### Volume\n\n\nSystem volume control. The icon switches between muted, low, medium, and high based on level.\n\n**Click:** Toggle mute\n**Scroll:** Adjust volume ±5%\n**Right-click options:**\n- **Volume Settings** -- open the desktop volume or sound settings screen when available\n**Tooltip:** `Volume: 75%` or `Muted`\n\n**Update interval:** 1 second (refreshes only on change)\n\n### Music\n\n\nMedia controller applet with album-art icon rendering.\n\n**Click:** Play/pause\n**Scroll:** Player volume ±5%\n**Right-click options:**\n- **Previous**\n- **Play** / **Pause**\n- **Next**\n- **Volume Up** / **Volume Down**\n\n**Tooltip:** multiline summary, e.g. `Artist - Title`, `Album: ...`, `Vol N%`\n\n### Last.fm\n\nShows recent Last.fm listening activity for a configured user.\n\n**Click:** Configure the applet, or open the current track when available\n**Right-click:** Recent tracks, profile link, refresh, and configuration\n\n### Pomodoro\n\n\nPomodoro timer with a flat tomato icon. Auto-cycles through work/break phases with configurable durations. Triggers urgent bounce+glow on phase transitions.\n\n**Click:** Start/pause toggle\n**Right-click options:**\n- **Reset** -- back to idle\n- **Work duration** -- 15/25/30/45 min presets\n- **Break duration** -- 5/10 min presets\n- **Long break duration** -- 15/20/30 min presets\n\n**Preferences stored:** `work`, `break_`, `long_break`\n\n### Pet\n\n\nAnimated companion applet that reacts to system activity with different moods.\n\n**Click:** reset the pet back to a happy state\n**Tooltip:** current mood and CPU percentage\n\n\n### Separator\n\nTransparent gap divider between dock items. Supports multiple instances -- each with independent, persistent size.\n\n**Scroll:** Adjust gap width (±2px, range 2–48px)\n**Right-click options:**\n- **Increase Gap** / **Decrease Gap**\n- **Remove from Dock**\n\nAdded via right-click on dock background -\u003e **Add Separator** (inserts at click position).\n\n### Hydration\n\n\nWater drop icon that drains over a configurable interval, reminding you to drink water. Click to refill. Triggers urgent bounce when empty.\n\n**Click:** Refill (log a drink)\n**Scroll:** No-op\n**Right-click options:**\n- **Show Timer** -- toggle countdown overlay on icon\n- **Interval presets** -- 15/30/45/60/90 min\n\n**Preferences stored:** `interval`, `show_timer`\n\n### Stretch Coach\n\n\nPeriodic micro-break reminder applet with offline stretch cards. Reminders stay inside the dock: the icon becomes urgent when a break is due, and clicking acknowledges the reminder and restarts the timer.\n\n**Click:** Trigger a break immediately when idle, or acknowledge the active reminder\n**Scroll:** No-op\n**Right-click options:**\n- **Take Break Now** / **Acknowledge Break**\n- **Show Random Stretch**\n- **Random Stretch Cards** -- toggle offline card attachment on reminders\n- **Interval presets** -- 15/30/45/60/90 min\n\n**Preferences stored:** `interval`, `cards_enabled`\n\n### Quote\n\n\nQuote/joke applet inspired by the original Cairo-Dock Quote plugin. Ships with local fallback quotes and supports online refresh from active sources.\n\n**Click:** Show next quote\n**Right-click options:**\n- **Next Quote**\n- **Copy Quote** -- copy current quote to clipboard\n- **Refresh from Web**\n- **Source** -- switch source (Quotationspage, Qdb, Danstonchat, Viedemerde, Fmylife, Vitadimerda, Chucknorrisfactsfr)\n\n**Preferences stored:** `source`\n\n### Random Trivia\n\n\nQuick trivia applet with local and online questions. The tooltip shows the current question and answer state, the menu exposes answer choices plus refresh/next actions, and the icon displays a small result pill after you answer: green for correct, red for wrong. The pill clears on the next trivia question.\n\n**Click:** Show the next trivia question\n**Scroll:** No-op\n**Right-click options:**\n- **Answer choices** -- pick an answer from the current question\n- **Next Trivia**\n- **Refresh from Web**\n\n### Today in History\n\n\nOne-event-at-a-time history applet with online refresh and offline fallback data. It keeps the current event compact in the tooltip/menu, refreshes for the local date, and lets you step through notable events without leaving the dock.\n\n**Click:** Show the next historical event for today\n**Scroll:** No-op\n**Right-click options:**\n- **Next Event**\n- **Refresh from Web**\n- **Open Article** -- open the current event's Wikipedia page when available\n\n### Hacker News\n\n\nHacker News headline viewer. It fetches HN top stories, keeps a cached list for startup, lazy-loads more when you land on the last loaded item, and shows the selected title plus points/comments in the tooltip. Paging continues up to 100 loaded headlines.\n\n**Click:** Open the current story\n**Scroll:** Cycle headlines\n**Right-click options:**\n- **Open Story**\n- **Open Comments**\n- **Next Headline**\n- **Refresh Now**\n\n**Update interval:** 10 minutes. Additional pages load on demand when you reach the last loaded headline, up to 100 stories.\n\n**Preferences stored:** cached `stories`, `active_index`, `fetched_at`\n\n### Ambient\n\n\nLooping ambient soundscape player with 7 bundled nature sounds plus white and pink noise.\n\n**Click:** Toggle play/stop\n**Scroll:** Adjust volume ±10%\n**Right-click:** Sound selection (Birds, Boat, Coffee Shop, Fireplace, Stream, Summer Night, Wind, White Noise, Pink Noise)\n\n**Preferences stored:** `sound`, `volume`\n\n### Calculator\n\n\nBasic four-function calculator with a popup interface. Supports +, -, *, /, parentheses, and decimal numbers.\n\n**Click:** Toggle calculator popup\n**Keyboard:** Type expression, press Enter to evaluate\n\n**Preferences stored:** `last_expression`\n\n### Unit Converter\n\n\nConvert between units directly from the dock popup. Supports length, weight, temperature, volume, speed, and data categories.\n\n**Click:** Toggle converter popup\n\n**Preferences stored:** `last_category`\n\n### Currency FX\n\n\nLive currency pair monitor with a sparkline icon. Add the pairs you care about, cycle between them from the dock, and choose the chart range that fits your glance.\n\n**Click:** Add FX pair\n**Scroll:** Cycle added pairs\n**Right-click:** Refresh, swap pair, add pair, chart interval, switch/remove added pair\n\n**Update interval:** 15 minutes. Day charts use local samples collected on each successful refresh; week and month charts use remote daily history plus the current rate.\n\n**Preferences stored:** `pairs`, `active_index`, `chart_interval`, `sample_source`, `samples`\n\n### Crypto\n\nTracks selected cryptocurrency prices with compact dock display and refresh actions.\n\n**Click:** Add an asset\n**Scroll:** Switch tracked assets\n**Right-click:** Refresh, chart interval, switch, add, or remove assets\n\n### URL Shortener\n\n\nShorten URLs with one click. Paste a URL, hit Shorten, and copy the result to the clipboard.\n\n**Click:** Toggle URL shortener dialog\n**Keyboard:** Paste URL, press Enter to shorten\n\n**Preferences stored:** `last_url`\n\n### Drag Share\n\n\nDrop a local file onto the applet to upload it to tmpfiles.org and copy the returned URL to the clipboard. Files are temporary and expire automatically.\n\n**Drop:** Upload file and copy URL\n**Click:** Copy last uploaded URL again\n\n**Preferences stored:** `last_url`\n\n### Window Killer\n\n\nClick the applet, then click any window to force-close it.\n\n**Click:** Enter kill mode (cursor changes to crosshair)\n\n### Cert Watch\n\n\nMonitor certificate expiry for a list of domains. The shield color highlights the most urgent domain, and the icon shows the lowest days remaining so expiring certificates are easy to spot.\n\n**Click:** Add domain dialog (accepts `example.com`, `example.com:8443`, or a full URL)\n\n**Right-click menu:**\n- Per-domain status with days remaining\n- Add domain\n- Remove submenu\n- Refresh Now\n\n**Update interval:** 1 hour. Failed certificate checks retry after 5 minutes.\n\n**Preferences stored:** `domains` list (host, port)\n\n### Speedtest\n\n\nOne-click internet speed test. The dial is painted as a classic four-band speedometer (red, orange, yellow, green from left to right); the needle points at the last download speed and takes its color from the current tier. The badge shows Mbps (e.g. `250Mb`, `1.2Gb`). Tooltip shows download, upload, ping, jitter, server, and timestamp.\n\n**Click:** Run one test (~20 seconds: ping + 10s download + 10s upload)\n\n**Right-click menu:**\n- Summary header (Down / Up)\n- Run Test (disabled while running)\n- Copy Last Result (to clipboard)\n\n**Update interval:** Manual. Results update only when you run a test.\n\n**Preferences stored:** `last_result` (download_mbps, upload_mbps, ping_ms, jitter_ms, server, timestamp)\n\n### Desk Presence\n\n\nTracks time at your desk versus away. The icon shows whether you are currently active or away, the bottom label shows today's at-desk hours, and the tooltip summarizes the recent daily breakdown.\n\n**Right-click menu:**\n- Status header (At desk / Away / Status unknown)\n- Idle Threshold submenu (1 / 2 / 5 / 10 min presets)\n- Reset Today\n\n**Preferences stored:** `today` (ISO date), `at_desk_seconds`, `away_seconds`, `idle_threshold_s`, `history` (last 6 days)\n\n### Docker\n\nShows Docker availability and container status when Docker is installed.\n\n**Click:** Refresh container state\n**Right-click:** Container actions when available\n\n### Astronomy Picture of the Day\n\n\nShows NASA's Astronomy Picture of the Day as a dock thumbnail. The tooltip includes the date, title, credit, and a short explanation, and the applet keeps showing a graceful placeholder if the image is unavailable.\n\n**Click:** Open today's page on apod.nasa.gov in the default browser\n\n**Right-click menu:**\n- Title header (date + title)\n- Open on apod.nasa.gov\n- Copy Explanation\n- Refresh Now\n\n**Update interval:** 1 hour. The applet fetches again when the APOD date changes and retries errors after 10 minutes.\n\n**Preferences stored:** `last_result` (date, title, explanation, media_type, image_url, page_url, copyright, cached_path)\n\n## Writing Custom Applets\n\nApplets are discovered from metadata and loaded lazily when enabled.\n\n```text\ndocking/applets/myapplet/\n  __init__.py   # metadata only: AppletMeta declaration\n  applet.py     # GTK wiring and lifecycle\n  state.py      # pure state/logic helpers\n  render.py     # icon rendering helpers\n```\n\n`__init__.py`:\n\n```python\nfrom docking.applets.identity import AppletCategory, AppletMeta\n\nmeta = AppletMeta(\n    id=\"myapplet\",\n    name=\"My Applet\",\n    category=AppletCategory.PRODUCTIVITY,\n)\n\n__all__ = [\"meta\"]\n```\n\n`applet.py`:\n\n```python\nfrom docking.applets.base import Applet, load_theme_icon\n\nclass MyApplet(Applet):\n    def create_icon(self, size):\n        return load_theme_icon(name=\"my-icon\", size=size)\n\n    def refresh_tooltip(self):\n        self.item.name = \"My Applet\"\n        self.item.tooltip_text = \"Useful status\"\n\n    def start(self, notify):\n        super().start(notify)\n\n    def stop(self):\n        super().stop()\n```\n\nUse `self.present()` after state changes to refresh icon, tooltip, and dock UI.\nKeep parsing/state logic in plain Python modules so tests do not need a display.\n\n## Translations\n\nDocking now ships 74 locale catalogs via standard gettext (plus English fallback).\n\nCore locales include:\n\n| Language | Code |\n|----------|------|\n| Brazilian Portuguese | pt_BR |\n| Spanish | es |\n| French | fr |\n| Simplified Chinese | zh_CN |\n| Hindi | hi |\n| Arabic | ar |\n| German | de |\n| Japanese | ja |\n| Korean | ko |\n| Russian | ru |\n\nAdditional locales are available under `docking/locale/*/LC_MESSAGES/docking.po`.\n\nThe dock automatically uses your system locale. To test a specific language:\n\n```bash\nLANGUAGE=pt_BR python run.py\n```\n\n### Adding a new translation\n\n1. Create a new `.po` file from the template:\n   ```bash\n   msginit --input=docking/locale/docking.pot --locale=XX --output=docking/locale/XX/LC_MESSAGES/docking.po\n   ```\n2. Edit the `.po` file with a PO editor (e.g. Poedit, Lokalize, or any text editor)\n3. Compile: `./tools/i18n.sh --compile`\n4. Submit a pull request\n\n### Updating the string template\n\nAfter adding or modifying translatable strings in the source code:\n\n```bash\n./tools/i18n.sh --extract\n```\n\nThis regenerates `docking/locale/docking.pot`. Existing `.po` files are updated separately in translation-refresh pull requests.\n\n### When CI fails after adding `_()` strings\n\nIf you add a new user-visible translatable string such as `_(\"...\")`, CI can fail in two common ways:\n\n- `--check-pot-sync` fails because `docking/locale/docking.pot` is stale\n- `--check-catalogs --allow-incomplete` fails because an existing locale catalog has format errors\n\nRegenerate the template:\n\n```bash\n./tools/i18n.sh --extract\n```\n\nYou do not need to update every `docking.po` catalog or fill in every new `msgstr` on regular feature commits. That creates large translation diffs that obscure the code review. Translation catalogs are refreshed periodically in translation-only pull requests:\n\n```bash\n./tools/i18n.sh --update-translations\n```\n\nTo verify locally with the same i18n gates CI uses:\n\n```bash\n./tools/i18n.sh --check-pot-sync\n./tools/i18n.sh --check-catalogs --allow-incomplete\n./tools/i18n.sh --compile\n```\n\nPractical sequence:\n\n1. `./tools/i18n.sh --extract`\n2. Rerun the three checks above\n3. Leave `.po` refreshes for a translation-only PR unless this branch is specifically about translations\n\n### Unified i18n command\n\n`./tools/i18n.sh` is the single translation utility. Common commands:\n\n```bash\n# Extract/update docking.pot\n./tools/i18n.sh --extract\n\n# Verify docking.pot is in sync with source strings\n./tools/i18n.sh --check-pot-sync\n\n# Update docking.pot, merge every locale catalog, and strip obsolete entries\n./tools/i18n.sh --update-translations\n\n# Validate locale catalogs while allowing incomplete translation backlog\n./tools/i18n.sh --check-catalogs --allow-incomplete\n\n# Strict translation-maintenance validation, fails on untranslated/fuzzy\n./tools/i18n.sh --check-catalogs --require-complete\n\n# Compile all .po catalogs to .mo\n./tools/i18n.sh --compile\n```\n\n## Developer Workflow\n\n### Tests\n\n```bash\n# Run all tests\npytest tests/ -v\n\n# Run specific module\npytest tests/applets/test_clock.py -v\n\n# Coverage report\npytest tests/ -v --cov=docking --cov-report=term-missing\n```\n\nFor the GUI/integration-oriented slice under a headless X11 session:\n\n```bash\nbash tools/test_gui_headless.sh\n```\n\nRequirements for that mode:\n- `xvfb-run`\n- `dbus-run-session`\n\n### D-Bus Remote Control\n\nDocking exposes a small session-bus API for item inspection and control.\n\n- Bus name: `org.docking.Docking`\n- Object path: `/org/docking/Docking`\n- Interface: `org.docking.Docking.Items1`\n\nCurrent methods:\n- `GetCount`\n- `ListPinnedIds`\n- `ListTransientIds`\n- `Pin`\n- `Unpin`\n- `Remove`\n- `GetHoverAnchor`\n\nExample:\n\n```bash\ngdbus call --session \\\n  --dest org.docking.Docking \\\n  --object-path /org/docking/Docking \\\n  --method org.docking.Docking.Items1.ListPinnedIds\n```\n\nFor full examples and expected responses, see [docs/DBUS.md](docs/DBUS.md).\n\nBy default it runs the dock interaction/UI slice:\n- pointer scenarios\n- edges\n- menu integration\n- preview popup integration\n- dock window integration\n- interaction\n- DnD integration\n- renderer integration\n\nYou can also pass explicit pytest targets:\n\n```bash\nbash tools/test_gui_headless.sh tests/ui/test_pointer_scenarios.py\n```\n\n### Building Packages\n\n#### Building a .deb package\n\n```bash\n# Install build dependencies\nsudo apt install python3-all python3-dev python3-setuptools python3-wheel \\\n  python3-pip debhelper dh-python pybuild-plugin-pyproject \\\n  libwayland-dev wayland-protocols gettext\n\n# Build\n./packaging/deb/build.sh\n\n# Install generated package\nsudo apt install ../docking_*_*.deb\n\n# If you used dpkg -i and dependencies were left unconfigured:\nsudo apt-get -f install\n```\n\n#### Building an RPM package\n\n```bash\n# Install tooling\nsudo apt install rpm python3-pip gettext python3-dev libwayland-dev wayland-protocols gcc\n\n# Build package\n./packaging/rpm/build.sh\n\n# Install locally on an RPM-based distro\nsudo dnf install ./artifacts/docking-*.rpm\n```\n\n#### Building a Flatpak bundle\n\n```bash\n# Install tooling\nsudo apt install flatpak flatpak-builder\n\n# Build bundle\n./packaging/flatpak/build.sh\n\n# Install and run locally\nflatpak install --user ./artifacts/cc.docking.Docking.flatpak\nflatpak run cc.docking.Docking\n```\n\n#### Building a Snap package\n\n```bash\n# Install tooling\nsudo apt install snapcraft\n\n# Build snap package\nmkdir -p artifacts\n(\n  cd packaging/snap\n  sudo snapcraft pack --destructive-mode --output ../../artifacts/docking.snap\n)\n\n# Install locally\nsudo snap install --dangerous artifacts/docking.snap\n```\n\n#### Building an AppImage\n\n```bash\n# Install tooling\nsudo apt install python3-apt python3-pip libfuse2 libgdk-pixbuf2.0-bin \\\n  libglib2.0-bin libgtk-3-bin squashfs-tools gettext\npython3 -m pip install --upgrade pip\npython3 -m pip install appimage-builder\n\n# Build AppImage\n./packaging/appimage/build.sh\n```\n\n#### Building an Arch package\n\n```bash\n# Arch Linux tooling\nsudo pacman -S --needed base-devel git python python-pip gettext\n\n# Build package\n./packaging/arch/build.sh\n\n# Install locally\nsudo pacman -U artifacts/docking-*.pkg.tar.*\n```\n\n#### Building with Nix\n\n```bash\n# Build package output\n./packaging/nix/build.sh\n\n# Run from build output\n./result-nix/bin/docking\n```\n\n### Pre-commit Hooks\n\nRuns automatically on `git commit`:\n- **ruff format --check**\n- **ruff check**\n- **ty check**\n- **i18n-pot-sync** -- ensure `docking/locale/docking.pot` matches source strings (`./tools/i18n.sh --check-pot-sync`)\n- **i18n-catalogs** -- fail if existing PO catalogs have format errors, while allowing untranslated/fuzzy backlog (`./tools/i18n.sh --check-catalogs --allow-incomplete`)\n- **package-data sync** -- verify packaged data declarations\n- **translation packaging** -- verify compiled catalog packaging\n- **pytest** -- full test suite\n\nInstall/update the local hook with:\n\n```bash\n./tools/install_precommit_hook.sh\n```\n\n### CI/CD Pipeline\n\nGitHub Actions is split across two workflows:\n\n- **`CI`** (`.github/workflows/ci.yml`)\n  - Triggers on push to `master`, PRs to `master`, and `v*` tags.\n  - **Quality**: `ruff check`, `ruff format --check`, `ty check`.\n  - **Test matrix**:\n    - Ubuntu 22.04 / Python 3.10\n    - Ubuntu 24.04 / Python 3.12 and Python 3.14 smoke tests\n    - Ubuntu 24.04 ARM64 / Python 3.12\n    - Debian 11 / Python 3.10\n    - Debian 12 / Python 3.12 and Python 3.14 smoke tests\n    - Fedora and openSUSE smoke tests\n  - **Coverage**: pytest-cov on Ubuntu with `--cov-fail-under=55`, artifacts uploaded (XML/HTML), optional Codecov upload when token is configured.\n  - **Packaging artifacts**:\n    - `.deb` (x64 and arm64)\n    - `.rpm` (x64 and arm64)\n    - `.flatpak` (x64 and arm64)\n    - `.snap` (x64 and arm64)\n    - `.AppImage` (x64 and arm64)\n    - Arch package (`.pkg.tar.*`, x64 and arm64)\n    - Nix output tarball + store path (x64 and arm64)\n  - **Release naming**:\n    - arch-specific assets use `linux-x86_64` for x64 and `linux-aarch64` for arm64\n    - Debian packages use `linux-x86_64.deb` and `linux-aarch64.deb`\n  - **Release step (CD)**:\n    - Runs on `master` only after all package builds.\n    - Reads version from `pyproject.toml`, checks latest GitHub Release, and only releases if version is newer.\n    - Creates/pushes `v\u003cversion\u003e` tag (if missing), normalizes artifact names, and publishes a GitHub Release with standardized files.\n\n- **`Security`** (`.github/workflows/security.yml`)\n  - Triggers on push/PR to `master` plus weekly schedule (`0 6 * * 1`).\n  - Runs:\n    - `pip-audit` against runtime dependencies exported from `pyproject.toml`\n    - `bandit` SAST scan on `docking/` (excluding tests/packaging)\n\n## Additional Docs\n\n- [Architecture Maintainer Map](docs/ARCHITECTURE.md)\n- [Icon Assets and Packaging](docs/ICONS.md)\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes (tests required for new features)\n4. Run `ruff format docking/ tests/` for formatting\n5. Ensure `ruff check \u0026\u0026 ty check \u0026\u0026 pytest tests/` passes\n6. Submit a pull request\n\n## License\n\nGPL-3.0-or-later\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedumucelli%2Fdocking","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedumucelli%2Fdocking","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedumucelli%2Fdocking/lists"}