diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 98a6043..f3f4e15 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -35,6 +35,9 @@ - [ ] The directory name matches the part of `id` after the `/` in `plugin.toml` exactly. - [ ] It ships `plugin.toml`, `README.md`, `thumbnail.webp`, and `translations/en.json`. +- [ ] `README.md` follows the + [README template](https://github.com/noctalia-dev/community-plugins/blob/main/README_TEMPLATE.md), documents + every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable. - [ ] I created `thumbnail.webp` with the [thumbnail generator](https://assets.noctalia.dev/plugins/thumbnail-generator.html). - [ ] `version` follows semver and is bumped in this PR; `min_noctalia` is the version I tested against. - [ ] Every non-English translation in this PR uses a locale supported by Noctalia core, and I can read, write, and diff --git a/.github/workflows/test_validate_plugins.py b/.github/workflows/test_validate_plugins.py index 126873b..6887fc3 100644 --- a/.github/workflows/test_validate_plugins.py +++ b/.github/workflows/test_validate_plugins.py @@ -1,6 +1,7 @@ from __future__ import annotations import importlib.util +import tempfile import unittest from pathlib import Path @@ -103,5 +104,100 @@ class PluginConfigAccessorTests(unittest.TestCase): self.assertEqual(validate_plugins.obsolete_config_accessors(source), []) +class ReadmeTests(unittest.TestCase): + MANIFEST = { + "id": "me/example", + "dependencies": ["example-cli"], + "setting": [{"key": "interval"}], + "widget": [{"id": "widget", "entry": "widget.luau"}], + "panel": [{"id": "panel", "entry": "panel.luau"}], + "launcher_provider": [ + {"id": "search", "entry": "launcher.luau", "prefix": "ex"} + ], + } + + VALID_README = """# Example + +Example provides a useful widget, panel, and launcher for demonstration purposes. + +## Plugin + +| Field | Value | +| --- | --- | +| ID | `me/example` | +| Entries | Widget: `widget`; panel: `panel`; launcher: `search` | +| Launcher Prefix | `/ex` | + +## Requirements + +Install `example-cli` on `PATH`. + +## Usage + +Add the widget, type `/ex`, or open the panel: + +```sh +noctalia msg panel-toggle me/example:panel +``` + +## Settings + +Configure the update interval in plugin settings. +""" + + def validate_readme(self, contents: str) -> list[str]: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + plugin_dir = root / "example" + plugin_dir.mkdir() + (plugin_dir / "README.md").write_text(contents, encoding="utf-8") + validator = validate_plugins.Validator(root) + validator.validate_readme(plugin_dir, self.MANIFEST) + return validator.errors + + def test_accepts_official_plugin_readme_structure(self) -> None: + self.assertEqual(self.validate_readme(self.VALID_README), []) + + def test_requires_core_sections_and_intro(self) -> None: + errors = self.validate_readme("# Example\n\nToo short.\n") + self.assertTrue(any("short introduction" in error for error in errors)) + self.assertTrue(any("## Plugin" in error for error in errors)) + self.assertTrue(any("## Usage" in error for error in errors)) + + def test_headings_inside_code_fences_do_not_satisfy_sections(self) -> None: + errors = self.validate_readme( + "# Example\n\nA sufficiently descriptive introduction for this example plugin.\n\n" + "```md\n## Plugin\n## Usage\n```\n" + ) + self.assertTrue(any("## Plugin" in error for error in errors)) + self.assertTrue(any("## Usage" in error for error in errors)) + + def test_derives_documented_values_from_manifest(self) -> None: + readme = self.VALID_README + replacements = { + "`me/example`": "`me/wrong`", + "`widget`": "`other-widget`", + "noctalia msg panel-toggle me/example:panel": "noctalia msg panel-toggle me/example:wrong", + "`/ex`": "`/wrong`", + "`example-cli`": "`other-cli`", + } + for old, new in replacements.items(): + with self.subTest(missing=old): + errors = self.validate_readme(readme.replace(old, new)) + self.assertTrue(errors) + + def test_requires_conditional_sections(self) -> None: + without_requirements = self.VALID_README.replace( + "## Requirements\n\nInstall `example-cli` on `PATH`.\n\n", "" + ) + without_settings = self.VALID_README.replace( + "## Settings\n\nConfigure the update interval in plugin settings.\n", "" + ) + self.assertTrue( + any("## Requirements" in error for error in self.validate_readme(without_requirements)) + ) + self.assertTrue(any("## Settings" in error for error in self.validate_readme(without_settings))) + + if __name__ == "__main__": unittest.main() diff --git a/.github/workflows/validate-plugins.py b/.github/workflows/validate-plugins.py index 7428cf1..a5046db 100644 --- a/.github/workflows/validate-plugins.py +++ b/.github/workflows/validate-plugins.py @@ -151,6 +151,7 @@ HTML_RE = re.compile( ) INLINE_CODE_RE = re.compile(r"(? int | None: return text.count("\n", 0, match.start()) + 1 +def markdown_headings(markdown: str) -> list[tuple[int, str, int, int]]: + """Return ATX headings outside fenced code as (level, title, start, end).""" + headings: list[tuple[int, str, int, int]] = [] + fence_char = "" + fence_length = 0 + offset = 0 + + for line in markdown.splitlines(keepends=True): + stripped = line.rstrip("\r\n") + if fence_char: + closing = rf"^ {{0,3}}{re.escape(fence_char)}{{{fence_length},}}\s*$" + if re.match(closing, stripped): + fence_char = "" + fence_length = 0 + offset += len(line) + continue + + opening = FENCE_OPEN_RE.match(line) + if opening: + fence = opening.group(1) + fence_char = fence[0] + fence_length = len(fence) + offset += len(line) + continue + + match = ATX_HEADING_RE.match(stripped) + if match: + headings.append((len(match.group(1)), match.group(2).strip(), offset, offset + len(line))) + offset += len(line) + + return headings + + +def section_body(markdown: str, headings: list[tuple[int, str, int, int]], index: int) -> str: + """Return a heading's body, including nested subsections.""" + level, _title, _start, body_start = headings[index] + body_end = len(markdown) + for next_level, _next_title, next_start, _next_end in headings[index + 1 :]: + if next_level <= level: + body_end = next_start + break + return markdown[body_start:body_end].strip() + + def obsolete_config_accessors(source: str) -> list[tuple[str, int]]: """Find removed entry-specific getConfig aliases outside Luau comments and strings.""" visible = list(source) @@ -924,7 +969,7 @@ class Validator: f"Export one with {THUMBNAIL_GENERATOR_URL}", ) - def validate_readme(self, plugin_dir: Path) -> None: + def validate_readme(self, plugin_dir: Path, manifest: dict[str, Any]) -> None: readme = plugin_dir / "README.md" if not readme.is_file(): return @@ -939,6 +984,109 @@ class Validator: if line is not None: self.add_error(readme, f"raw HTML on line {line} is not allowed; use Markdown instead") + headings = markdown_headings(contents) + h1_indexes = [index for index, heading in enumerate(headings) if heading[0] == 1] + if not h1_indexes: + self.add_error(readme, "missing a level-one plugin title ('# Plugin Name')") + else: + h1_index = h1_indexes[0] + intro_start = headings[h1_index][3] + intro_end = headings[h1_index + 1][2] if h1_index + 1 < len(headings) else len(contents) + intro = contents[intro_start:intro_end] + intro_words = re.findall(r"[A-Za-z0-9][A-Za-z0-9'_-]*", intro) + if len(intro_words) < 8: + self.add_error(readme, "add a short introduction below the title explaining what the plugin does") + + h2_by_name = { + title.casefold(): index + for index, (level, title, _start, _end) in enumerate(headings) + if level == 2 + } + for section in ("Plugin", "Usage"): + index = h2_by_name.get(section.casefold()) + if index is None: + self.add_error(readme, f"missing required '## {section}' section") + elif not section_body(contents, headings, index): + self.add_error(readme, f"'## {section}' section must not be empty") + + plugin_section_index = h2_by_name.get("plugin") + plugin_section = ( + section_body(contents, headings, plugin_section_index) + if plugin_section_index is not None + else "" + ) + + plugin_id = manifest.get("id") + if not is_non_empty_string(plugin_id): + return + + documented_id = f"`{plugin_id}`" + if documented_id not in plugin_section: + self.add_error(readme, f"Plugin section must document the manifest id as {documented_id}") + + for entry_type in ENTRY_TYPES: + entries = manifest.get(entry_type, []) + if not isinstance(entries, list): + continue + for entry in entries: + if not isinstance(entry, dict) or not is_non_empty_string(entry.get("id")): + continue + entry_id = entry["id"] + documented_entry = f"`{entry_id}`" + if documented_entry not in plugin_section: + self.add_error( + readme, + f"Plugin section must document {entry_type} entry '{entry_id}' as {documented_entry}", + ) + + if entry_type == "panel": + command = f"noctalia msg panel-toggle {plugin_id}:{entry_id}" + if command not in contents: + self.add_error( + readme, + f"missing panel IPC command; add: {command}", + ) + + if entry_type == "launcher_provider" and is_non_empty_string(entry.get("prefix")): + prefix = f"`/{entry['prefix']}`" + if prefix not in plugin_section: + self.add_error( + readme, + f"missing launcher prefix {prefix} for entry '{entry_id}'", + ) + + dependencies = manifest.get("dependencies", []) + if isinstance(dependencies, list) and dependencies: + requirements_index = h2_by_name.get("requirements") + if requirements_index is None: + self.add_error(readme, "plugins with dependencies require a '## Requirements' section") + requirements = "" + else: + requirements = section_body(contents, headings, requirements_index) + if not requirements: + self.add_error(readme, "'## Requirements' section must not be empty") + for dependency in dependencies: + if is_non_empty_string(dependency) and f"`{dependency}`" not in requirements: + self.add_error( + readme, + f"Requirements must mention manifest dependency `{dependency}`", + ) + + has_settings = bool(manifest.get("setting")) + for entry_type in SETTING_OWNER_TYPES: + entries = manifest.get(entry_type, []) + if isinstance(entries, list) and any( + isinstance(entry, dict) and bool(entry.get("setting")) for entry in entries + ): + has_settings = True + break + if has_settings: + settings_index = h2_by_name.get("settings") + if settings_index is None: + self.add_error(readme, "plugins with settings require a '## Settings' section") + elif not section_body(contents, headings, settings_index): + self.add_error(readme, "'## Settings' section must not be empty") + def validate_luau_api(self, plugin_dir: Path) -> None: for source_path in sorted(plugin_dir.rglob("*.luau")): try: @@ -969,7 +1117,7 @@ class Validator: self.validate_root_fields(manifest_path, manifest) self.validate_required_files(manifest_path, plugin_dir) self.validate_thumbnail(manifest_path, plugin_dir) - self.validate_readme(plugin_dir) + self.validate_readme(plugin_dir, manifest) self.validate_luau_api(plugin_dir) self.validate_no_symlinks(manifest_path, plugin_dir) diff --git a/README.md b/README.md index 216e4f2..5be7ad9 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,25 @@ the **[thumbnail generator](https://assets.noctalia.dev/plugins/thumbnail-genera your plugin, set the title, category tag and accent color, then export the 960×540 WebP and commit it as `/thumbnail.webp`. +### README + +`README.md` is the plugin's public page, so it must tell a user how to access every entry instead of only describing +the implementation. Follow [`README_TEMPLATE.md`](README_TEMPLATE.md), which mirrors the structure used by the +official plugins: + +- Start with a title, a short explanation, a `Plugin` table, and practical `Usage` instructions. +- Copy the plugin id and every entry id exactly from `plugin.toml`. +- If the plugin declares a panel, include the exact command + `noctalia msg panel-toggle /:`. +- If it declares a launcher provider, document its `/` and give an example query. +- Mention every manifest dependency under `Requirements`, using the exact dependency name. +- Document declared settings, including units or non-obvious effects. +- Use `IPC` and `Notes` when the plugin exposes extra events or has important filesystem, network, process, privacy, + hardware, or compositor behavior. + +CI derives ids, panel commands, launcher prefixes, dependencies, and whether settings exist from `plugin.toml`. Its +error messages show the exact missing value, while maintainers review the usefulness and accuracy of the prose. + ### Translations Write `translations/en.json` only. Every `label_key` and `description_key` in your manifest must resolve to a key in diff --git a/README_TEMPLATE.md b/README_TEMPLATE.md new file mode 100644 index 0000000..3f8fb4a --- /dev/null +++ b/README_TEMPLATE.md @@ -0,0 +1,61 @@ +# Plugin Name + +Explain in one or two sentences what the plugin does and why someone would use +it. + +## Plugin + + + +| Field | Value | +| --- | --- | +| ID | `/` | +| Entries | Bar widget: ``; panel: ``; service: `` | +| Launcher Prefix | `/` | + +## Requirements + + + +Install `example-cli` on `PATH`. + +## Usage + +Explain how to add or access every user-facing entry and describe the normal +workflow. Use exact labels and ids. For a panel, include its copy-pasteable IPC +command: + +```sh +noctalia msg panel-toggle /: +``` + +For a launcher provider, explain what to type after `/` and what +activating a result does. For a shortcut, say where users add it in Settings. + +## Settings + + + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `example_setting` | `bool` | `false` | What changing this setting does. | + +## IPC + + + +```sh +noctalia msg plugin /: all [payload] +``` + +## Notes + + diff --git a/daily-wallpaper/README.md b/daily-wallpaper/README.md index fbbe67f..d8097ed 100644 --- a/daily-wallpaper/README.md +++ b/daily-wallpaper/README.md @@ -1,10 +1,34 @@ # Daily Wallpaper -Fetches a daily wallpaper from Bing or NASA and applies it through the Noctalia 5 wallpaper API. +Daily Wallpaper fetches Bing's image of the day or NASA's image of the day and +applies it through Noctalia's wallpaper API. -The service checks on startup and then every 10 minutes. It downloads at most one image per source and Bing locale per day, stores its files in a dedicated `daily-wallpaper` directory, and removes cached images older than 5 days. Repeated failures are logged, but error notifications are limited to once per day. +## Plugin -Settings: +| Field | Value | +| --- | --- | +| ID | `nzlov/daily-wallpaper` | +| Entry | Service: `service` | -- Source: Bing or NASA. -- Locale: Bing market locale such as `en-US`, `de-DE`, or `fr-FR`. NASA ignores this setting. +## Usage + +Enable the plugin in Settings → Plugins. Its headless service checks for the +current image on startup and every ten minutes, applying at most one new image +per source and Bing locale each day. + +Choose Bing or NASA under the plugin's settings. Bing accepts a market locale +such as `en-US`, `de-DE`, or `fr-FR`; NASA ignores the locale setting. + +## Settings + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `source` | `select` | `bing` | Selects Bing or NASA as the daily image source. | +| `locale` | `string` | *(automatic)* | Bing market locale; an empty value uses the service default. | + +## Notes + +The service contacts the selected provider and downloads images into a +dedicated `daily-wallpaper` cache directory. It removes cached images older +than five days. Repeated failures are logged, but error notifications are +limited to once per day. diff --git a/iio_lock/README.md b/iio_lock/README.md index 4f64eea..e8738bf 100644 --- a/iio_lock/README.md +++ b/iio_lock/README.md @@ -1,16 +1,48 @@ -iio-lock — Orientation Lock for 2‑in‑1 Devices +# iio-lock -iio-lock is a small bar widget and panel plugin for Noctalia that provides manual control over screen orientation. -It works by stopping and restarting the iio-hyprland service (or similar IIO rotation tools), preventing automatic rotation while the lock is active. +iio-lock provides orientation controls for 2-in-1 devices, allowing automatic +screen rotation to be locked and manual output transforms to be selected. -This is useful on 2‑in‑1 touch laptops, where automatic rotation may not always be desired — such as when using the device in tablet mode, drawing, or note‑taking. +## Plugin -Supported WM and Tools -Hyprland / iio‑hyprland — Working and tested -Sway / iio‑sway — Unverified (experimental) +| Field | Value | +| --- | --- | +| ID | `nikolaj-zwergius/iio_lock` | +| Entries | Bar widgets: `iio-lock`, `lock-panel`; panel: `panel`; shortcut: `toggle`; service: `iio-service` | -External commands -This plugin shells out to: - -pgrep - -pkill - -iio-hyprland (or the user‑configured tool) +## Requirements + +The plugin supports Hyprland with `iio-hyprland` and `hyprctl`. Sway support +uses `iio-sway` and `swaymsg` and is currently experimental. The helper +commands `pgrep` and `pkill` must also be available on `PATH`. + +## Usage + +Add the `iio-lock` widget to toggle the orientation lock with a left click and +open the transform panel with a right click. The `lock-panel` widget opens the +transform panel with a left click. You can also add the `toggle` shortcut under +Settings → Control Center shortcuts. + +Open the transform panel directly with: + +```sh +noctalia msg panel-toggle nikolaj-zwergius/iio_lock:panel +``` + +Locking stops the configured IIO rotation helper; unlocking starts it again. +The panel applies the selected transform to the chosen output. + +## Settings + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `iio` | `select` | `iio-hyprland` | Automatic rotation helper to start and stop. | +| `transform-order` | `string` | `0,1,2,3` | Transform values shown by the panel, in order. | +| `vm` | `select` | `hyprland` | Selects the Hyprland or Sway command backend. | +| `locked` | `glyph` | `lock` | Glyph used while orientation is locked. | +| `unlocked` | `glyph` | `lock-open-2` | Glyph used while orientation is unlocked. | + +## Notes + +Hyprland is the tested backend. Verify the configured rotation helper works on +its own before using the plugin, especially on Sway and similar compositors. diff --git a/mangowm-keymode/README.md b/mangowm-keymode/README.md index 8a62939..43c08f4 100644 --- a/mangowm-keymode/README.md +++ b/mangowm-keymode/README.md @@ -1,3 +1,28 @@ -# mangowm-keymode -![mangowm keymode thumbnail](thumbnail.webp) -**Mangowm Keymode** is a bar widget plugin written for Noctalia (v5) that displays the current keymode from window manager [MANGOWM](https://mangowm.github.io/). It also notifies you when the keymode changes. Notification and bar text are configurable and can be disabled. \ No newline at end of file +# Mangowm Keymode + +Mangowm Keymode adds a bar widget that displays MangoWC's current keymode and +can notify you whenever the active keymode changes. + +## Plugin + +| Field | Value | +| --- | --- | +| ID | `gambled23/mangowm-keymode` | +| Entry | Bar widget: `mangowm-keymode` | + +## Requirements + +This plugin requires MangoWC and its `mmsg` command on `PATH`. It listens to +`mmsg watch keymode` for live keymode changes. + +## Usage + +Add the `mangowm-keymode` widget from Noctalia's widget picker. It updates as +MangoWC changes keymode; click it to show the current keymode in a notification. + +## Settings + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `show_text` | `bool` | `true` | Shows the current keymode beside the widget glyph. | +| `notify_change` | `bool` | `true` | Sends a notification when the keymode changes. | diff --git a/mini-docker/README.md b/mini-docker/README.md index 2d5b35f..71635f5 100644 --- a/mini-docker/README.md +++ b/mini-docker/README.md @@ -1,32 +1,55 @@ # Mini Docker -Mini Docker is a Noctalia v5 plugin for managing Docker from the shell. The bar widget shows Docker availability and the running-container count. Its management panel can start, stop, restart, and remove containers; run and remove images; and inspect or remove volumes and networks. +Mini Docker manages Docker containers, images, volumes, and networks from +Noctalia. Its bar widget shows Docker availability and the number of running +containers, while its panel provides common management actions. -This Luau implementation migrates the Noctalia v4 Mini Docker plugin originally written by [MannuVilasara](https://github.com/MannuVilasara). +## Plugin + +| Field | Value | +| --- | --- | +| ID | `8bury/mini-docker` | +| Entries | Bar widget: `mini-docker`; panel: `manager`; service: `docker-service` | ## Requirements -- Noctalia 5.0.0 or newer -- Docker CLI and a reachable Docker daemon -- Permission for the current user to access Docker +Install the Docker `docker` CLI and make sure your user can connect to the +Docker daemon. Test that `docker info` succeeds without unexpected prompts. ## Usage -Enable `8bury/mini-docker`, then add its `mini-docker` widget to a bar. Click the widget to open the management panel. Right-click it to refresh Docker state immediately. +Add the `mini-docker` widget to a bar. Left-click it to open the management +panel and right-click it to refresh Docker state immediately. + +Open the panel directly with: + +```sh +noctalia msg panel-toggle 8bury/mini-docker:manager +``` The panel has four tabs: -- Containers: start, stop, restart, or remove a selected container -- Images: run an image with optional name, network, port, and environment variables, or remove an unused image -- Volumes: inspect and remove volumes -- Networks: inspect and remove non-default networks +- **Containers:** start, stop, restart, and remove containers. +- **Images:** run images with an optional name, network, published port, and + environment variables; remove images that are not in use. +- **Volumes:** inspect and remove volumes. +- **Networks:** inspect and remove non-default networks. -Settings control the refresh interval, default run network, running count, icon color, and status indicator. +## Settings -## Security +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `refresh_interval` | `int` | `5` | Seconds between Docker state refreshes. | +| `default_network` | `string` | `bridge` | Network initially selected when running an image. | +| `show_count` | `bool` | `true` | Shows the running-container count in the widget. | +| `glyph_color` | `select` | `on_surface` | Theme color used for the Docker glyph. | +| `status_mode` | `select` | `always` | Shows the status dot always, only while running, or never. | +| `active_color` | `select` | `tertiary` | Status color when a container is running. | +| `inactive_color` | `select` | `error` | Status color when no containers are running. | -Mini Docker invokes only the local `docker` CLI. Subprocess arguments are shell-quoted, and user-entered container names, ports, and environment-variable keys are validated before execution. +## Notes -## License - -MIT. Attribution to the original v4 author is retained above. +Mini Docker runs the local Docker CLI with your user's existing daemon access. +Destructive actions require confirmation in the panel. It does not request +elevated privileges, store Docker credentials, mount host paths, or expose +ports unless you explicitly configure a port while running an image. diff --git a/nix-monitor/README.md b/nix-monitor/README.md index 7a0db7b..1112ea9 100644 --- a/nix-monitor/README.md +++ b/nix-monitor/README.md @@ -1,21 +1,70 @@ # Nix Monitor -![Nix Monitor thumbnail](thumbnail.webp) +Nix Monitor compares the local Nixpkgs revision with a remote branch and shows +NixOS generations, store size, closure size, and update status from the bar. -**Nix Monitor** checks Nixpkgs update by comparing local nix hash and remote Nixpkgs's hash +## Plugin -## Features - - Shows local and remote nixpkgs hash - - Shows NixOS and optionally Home Manager generations - - Shows Nix Store size and closure size - - Customizable clean and update command +| Field | Value | +| --- | --- | +| ID | `avivbintangaringga/nix-monitor` | +| Entries | Bar widget: `nix-monitor`; panel: `panel`; service: `service` | - ## Requirements - - Git - - NixOS commands (`nix`, `nixos-rebuild`, `nixos-version`) - - Linux tools (`du`, `cat`, `awk`, `grep`, `tail`, `wc`, `kill`, `pkill`) - - Optionally `home-manager` +## Requirements + +This plugin is intended for NixOS. It uses `nix`, `nixos-version`, +`nixos-rebuild`, and `git`, plus the standard commands `du`, `cat`, `awk`, +`grep`, `tail`, `wc`, `kill`, and `pkill`. Home Manager generation information +is shown when `home-manager` is available. + +## Usage + +Add the `nix-monitor` widget to a bar. Click it to open a panel showing local +and remote Nixpkgs revisions, NixOS and Home Manager generations, store usage, +and update controls. + +Open the panel directly with: + +```sh +noctalia msg panel-toggle avivbintangaringga/nix-monitor:panel +``` + +Set `update_command` before using **Update**. **Clean** runs the configured +cleanup command, which defaults to `nix-collect-garbage -d`. Both commands open +in a terminal so you can review their output. + +## Settings + +Update behavior: + +| Setting | Default | Description | +| --- | --- | --- | +| `update_check_interval` | `60` | Minutes between remote revision checks. | +| `update_check_duration_threshold` | `5` | Minutes before an update check is cancelled. | +| `system_stats_check_interval` | `10` | Minutes between store-statistics checks. | +| `system_stats_check_duration_threshold` | `5` | Minutes before a statistics check is cancelled. | +| `show_update_check_notification` | `false` | Notifies when an update check starts and finishes. | +| `show_update_available_notification` | `true` | Notifies when a newer revision is available. | +| `branch` | `nixos-unstable` | Nixpkgs branch compared by the service. | +| `update_command` | *(empty)* | Command launched by the panel's **Update** button. | +| `clean_command` | `nix-collect-garbage -d` | Command launched by **Clean**. | +| `close_on_enter` | `true` | Keeps the command terminal open until Enter is pressed. | + +Widget appearance: + +| Setting | Default | Description | +| --- | --- | --- | +| `show_text` | `true` | Shows status text beside the glyph. | +| `colorize_text` | `false` | Colors status text using the current state color. | +| `show_glyph` | `true` | Shows the status glyph. | +| `colorize_glyph` | `true` | Colors the glyph using the current state color. | +| `up_to_date_glyph` / `up_to_date_color` | `rosette-discount-check` / `#57ff57` | Up-to-date appearance. | +| `checking_glyph` / `checking_color` | `loader-3` / `#ffeb57` | Checking appearance. | +| `update_available_glyph` / `update_available_color` | `cloud-download` / `#ff5757` | Update-available appearance. | +| `unknown_glyph` / `unknown_color` | `cloud-question` / `on_surface` | Unknown-state appearance. | ## Notes - - You need to add the update command in the setting first - - By default, clean command executes `nix-collect-garbage -d`, you can change it too! + +Remote checks contact the configured Nixpkgs Git source. The service writes +temporary revision, size, and PID files under Noctalia's state directory and +terminates overdue helper processes using the declared process tools. diff --git a/prismlauncher-instances/README.md b/prismlauncher-instances/README.md index 54b7844..aec1dcb 100644 --- a/prismlauncher-instances/README.md +++ b/prismlauncher-instances/README.md @@ -1,6 +1,34 @@ # PrismLauncher Instances -![prismlauncher instances thumbnail](thumbnail.webp) -**PrismLauncher Instances** is a plugin that adds PrismLauncher instances to the noctalia launcher. + +PrismLauncher Instances adds your local Minecraft instances to the Noctalia +launcher so they can be searched and started directly. + +## Plugin + +| Field | Value | +| --- | --- | +| ID | `radimous/prismlauncher-instances` | +| Entry | Launcher provider: `prismlauncher-instances` | +| Launcher Prefix | `/pl` | ## Requirements -- [prismlauncher](https://github.com/PrismLauncher/PrismLauncher) + +Install [PrismLauncher](https://github.com/PrismLauncher/PrismLauncher) and make +sure the `prismlauncher` command is available on `PATH`. + +## Usage + +Open the Noctalia launcher and type `/pl` to list all detected PrismLauncher +instances. Continue typing to filter by instance name, then activate a result +to launch that instance with `prismlauncher --launch`. + +## Settings + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `prism_path` | `string` | `~/.local/share/PrismLauncher` | PrismLauncher data directory containing its configuration and instances. | + +## Notes + +The provider reads `prismlauncher.cfg`, instance metadata, and local instance +icons from the configured PrismLauncher directory. It does not modify them. diff --git a/proton-pass/README.md b/proton-pass/README.md index 4a0b36f..3ce0bfa 100644 --- a/proton-pass/README.md +++ b/proton-pass/README.md @@ -1,15 +1,31 @@ # Proton Pass -A Noctalia Launcher plugin that integrates with the Proton Pass CLI. +Proton Pass integrates the Proton Pass CLI with the Noctalia launcher, letting +you browse vaults, copy passwords, and display time-based one-time codes. -## Features +## Plugin -- Browse your Proton Pass vaults -- Browse items within each vault -- Copy passwords to the clipboard -- Display TOTP codes as a notification (when available) +| Field | Value | +| --- | --- | +| ID | `lucasoe/proton-pass` | +| Entry | Launcher provider: `proton-pass` | +| Launcher Prefix | `/pass` | ## Requirements -- [proton-pass-cli](https://protonpass.github.io/pass-cli/) installed and available in your `PATH` -- An authenticated Proton Pass CLI session (run `pass-cli login`) +Install `proton-pass-cli` and authenticate it with `pass-cli login` before +using the provider. The `pass-cli` executable must be available on `PATH`. + +## Usage + +Open the Noctalia launcher and type `/pass` to list Proton Pass vaults. Select +a vault, continue typing to filter its items, and activate an item to copy its +password to the clipboard. When the item has a TOTP secret, the current code is +also displayed in a notification. + +## Notes + +Vault metadata is cached only in the plugin process for the current session. +Secret values are requested from the authenticated CLI when you activate an +item; passwords are copied to the clipboard and TOTP codes are shown through +Noctalia notifications. diff --git a/sharednd/README.md b/sharednd/README.md index 2dd7344..1f497fe 100644 --- a/sharednd/README.md +++ b/sharednd/README.md @@ -6,6 +6,20 @@ When the first screencast starts (any app sharing via the portal — Discord, OBS, browsers, `niri msg action set-dynamic-cast-*`, ...), notification DND is enabled. When the last screencast stops, notifications come back. +## Plugin + +| Field | Value | +| --- | --- | +| ID | `whyoolw/sharednd` | +| Entry | Service: `service` | + +## Usage + +Enable the plugin in Settings → Plugins while running a niri session. The +headless service starts monitoring screencasts immediately; it has no bar +widget or panel. Start and stop a screen share to verify that Noctalia's DND +state follows the session according to the ownership settings below. + ## How it works - A headless service follows `niri msg -j event-stream` and reacts to @@ -36,9 +50,9 @@ enabled. When the last screencast stops, notifications come back. - **Always disable DND after sharing** — force DND off when sharing ends, regardless of its state before sharing started. -## Requirements & limitations +## Requirements -- Requires **niri** (detection is niri IPC). Detection only starts inside a +- Requires `niri` (detection is niri IPC). Detection only starts inside a niri session (`NIRI_SOCKET` set and the `niri` binary in `PATH`); on other compositors the service is inert and spawns no processes. - Disabling or reloading the plugin mid-share while it owns DND turns DND diff --git a/shelly/README.md b/shelly/README.md index cadadd4..e7c3fab 100644 --- a/shelly/README.md +++ b/shelly/README.md @@ -7,14 +7,25 @@ Shelly is a plugin that uses the [Shelly Arch Package Manager](https://github.co | Field | Value | | ------- | --------------------------------------- | | ID | `joshuaslate/shelly` | -| Entries | Service: `poller`; bar widget: `shelly` | +| Entries | Service: `update_poller`; bar widget: `shelly` | ## Requirements -Install the Shelly Arch Package Manager from the [AUR](https://aur.archlinux.org/packages/shelly). +Install the Shelly Arch Package Manager from the [AUR](https://aur.archlinux.org/packages/shelly). The `shelly` +command must be available on `PATH`. Test that it works by running `shelly check-updates` in a terminal. If it returns a list of packages, then it is working correctly. +## Usage + +Add the `shelly` widget from Noctalia's widget picker. It periodically checks +for available Arch package updates and shows their count and names in the bar +tooltip. Click behavior is configurable: open Shelly's graphical interface, +run `shelly upgrade-all` in a terminal, or do nothing. + +The `update_poller` service owns the periodic checks and can notify you when +new updates become available. + ## Settings | Setting | Type | Default | Description | diff --git a/todo/README.md b/todo/README.md index 22a3668..25b1ca9 100644 --- a/todo/README.md +++ b/todo/README.md @@ -6,6 +6,22 @@ add tasks with **+**, tick them off (the text is struck through), delete them, and set each task's priority. The list is kept sorted by priority and stored as a single JSON file; no external commands are run. +## Plugin + +| Field | Value | +| --- | --- | +| ID | `nightwatch75/todo` | +| Entries | Bar widget: `todo`; panel: `panel` | + +## Usage + +Add the `todo` widget from Noctalia's widget picker and click it to open the +task panel. You can also open the panel directly or bind it in your compositor: + +```sh +noctalia msg panel-toggle nightwatch75/todo:panel +``` + | Action | Effect | |-------------------------------|-----------------------------------------------------| | Left click (bar glyph) | Open/close the To Do panel | diff --git a/um5606_fan_state/README.md b/um5606_fan_state/README.md index 1e86fc1..1db123c 100644 --- a/um5606_fan_state/README.md +++ b/um5606_fan_state/README.md @@ -1 +1,39 @@ -Set the fan state on the ZenBook S 16 UM5606 (and related laptops). Requires https://github.com/ThatOneCalculator/asus-5606-fan-state +# ASUS UM5606 Fan State + +ASUS UM5606 Fan State shows and changes the firmware fan profile on the +ZenBook S 16 UM5606 and compatible laptops from the bar or Control Center. + +## Plugin + +| Field | Value | +| --- | --- | +| ID | `thatonecalculator/um5606_fan_state` | +| Entries | Bar widget: `fan_state`; shortcut: `toggle`; service: `service` | + +## Requirements + +Install the `fan_state` command from +[asus-5606-fan-state](https://github.com/ThatOneCalculator/asus-5606-fan-state) +and verify that `fan_state get --int` works for your hardware. + +## Usage + +Add the `fan_state` widget to a bar, or add the `toggle` shortcut under +Settings → Control Center shortcuts. Click either entry to cycle through +Standard, Quiet, High-Performance, and Full fan profiles. + +The headless `service` polls the current profile and owns all calls to the +hardware helper. Both user-facing entries become disabled or show +**Unavailable** when the helper cannot report a valid state. + +## Settings + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `show_label` | `bool` | `false` | Shows the current profile name beside the bar glyph. | +| `poll_interval` | `int` | `2000` | Hardware polling interval in milliseconds, from 250 to 60000. | + +## Notes + +This plugin changes a hardware fan-control setting by running `fan_state set`. +Only use it on hardware supported by the helper project. diff --git a/upbeat/README.md b/upbeat/README.md index 3ff8d2a..b3204f5 100644 --- a/upbeat/README.md +++ b/upbeat/README.md @@ -1,3 +1,30 @@ # Upbeat -![upbeat thumbnail](thumbnail.webp) -**Upbeat** is a bar widget plugin written for Noctalia (v5) that displays the current Internet Time. Internet time is a subversive, yet extremely accurate and location agnostic way of telling time created in the 90s for the purpose of communicating in the future. Also it's kinda funny. + +Upbeat adds a bar widget that displays Swatch Internet Time, a timezone-neutral +decimal time system measured in beats and centibeats. + +## Plugin + +| Field | Value | +| --- | --- | +| ID | `neuro/upbeat` | +| Entry | Bar widget: `upbeat` | + +## Usage + +Add the `upbeat` widget from Noctalia's widget picker. The widget displays the +current Internet Time at UTC+1; hover it to see conventional local time in the +configured 12- or 24-hour format. + +## Settings + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `show_centibeats` | `bool` | `true` | Shows two centibeat digits after the decimal point. | +| `beat_display` | `bool` | `false` | Appends the `.beats` label to the value. | +| `time_format_toggle` | `bool` | `false` | Uses 24-hour time instead of 12-hour time in the tooltip. | + +## Notes + +Internet Time is computed locally and does not contact a network service. The +widget updates more frequently when centibeats are displayed.