Skip to content

fix(web-ui): Plugin Manager - enable aliased installs, Update All, on-demand modes, long installs, categories, GitHub-URL install - #746

Merged
ChuckBuilds merged 6 commits into
mainfrom
fix/web-plugin-manager
Oct 4, 2026
Merged

ChuckBuilds merged 6 commits into
mainfrom
fix/web-plugin-manager

Conversation

@ChuckBuilds

Copy link
Copy Markdown
Owner

Summary

Seven Plugin Manager bugs, in six commits.

  1. Store Install couldn't enable Weather, Music, Stocks or Leaderboard.
    • Cause: the client toggled the registry ID (weather), but the install is named by the manifest's ID (ledmatrix-weather). The user saw "Plugin not found" and then "installed, but enabling it failed".
    • Fix: the install answer now carries the installed plugin's own plugin_id, found the way update/uninstall find it. The client enables that ID; it falls back to matching aliases, as isStorePluginInstalled does.
  2. Reinstall turned on a plugin the user had disabled. A reinstall now just reloads the list.
  3. Update All worked from a stale list.
    • Cause: it read PluginStateManager.installedPlugins, which only Update All itself ever filled. A second run sent the first run's plugins, and the grid kept showing "Update to vX".
    • Fix: it reads the live window.installedPlugins and finishes with loadInstalledPlugins(true), which redraws the grid and the Updates badge.
  4. The on-demand modal couldn't offer a mode.
    • Cause: /plugins/installed never returned display_modes, so Pin always pinned the first mode.
    • Fix: it now includes them, from the plugin catalog.
  5. Long installs were reported as failed after 60 one-second polls, while the dependency install alone may take 300 s.
    • Fix: the cap is now 600 polls. On timeout the list is refreshed and a "may still be running" warning is shown.
  6. The store's category filter was hard-coded with 7 categories against about 20 in the registry; about 17 of 54 plugins were unreachable. The options are now built from the store data.
  7. The GitHub-URL Install button threw a ReferenceError on every click, from an inline onclick calling an IIFE-local function. Installs only worked because a second listener also fired. The inline handler is removed, and one click sends one request.

Tests

  • New JS units: test_store_install.js (13), test_install_polling.js (8), test_store_categories.js (9), test_github_url_install.js (8), extended test_update_all.js. Plus a shared plugins_manager_sandbox.js harness.
  • New Python: test_api_v3_installed_display_modes.py (4), test_api_v3_install_reports_installed_id.py (8).
  • node test/js/run_all.js: all suites pass. DOM suites, run against the emulator web UI: store 50/50, no-double-fetch 8/8, installed 56/56.
  • Not fixed, noted for follow-up: the install route still computes restart_required from the registry ID, so an aliased plugin never asks for a restart.

On ledpi: /plugins/installed listed display_modes for 27 of 28 plugins (baseball: mlb_live, mlb_recent, mlb_upcoming, …).

Part of a bug sweep

This is one of 10 independent fix PRs from one sweep, all based on main ef69201.

  • Merge order: any. 45 pairwise test merges gave 0 conflicts, and each PR also merges cleanly with fix(ipc): ticks carry the volatile timestamps, so current-status stays known over the socket #737.
  • CHANGELOG: each PR adds its bullet at a different place in Unreleased → Fixes, so squash-merging them one after another needs no conflict fixing.
  • Full suite (Windows), all 10 merged together vs plain main: the same 62 failures and 6 errors on both. These are the known Windows path and file-locking tests. 191 more tests pass.
    • One extra failure in that run, test_backup_manager.py::test_create_backup_contents (os.replace → WinError 5 on a temp zip), was a Windows file-lock flake. It passes on rerun, and nothing here touches create_backup.
  • ledpi: all 10 together ran on ledpi (Pi 4) on top of main, with a clean start and no errors or render stalls in the journal. ledpi is back on plain main.

🤖 Generated with Claude Code

ChuckBuilds and others added 6 commits October 3, 2026 21:04
… grid

updateAll() preferred PluginStateManager.installedPlugins over
window.installedPlugins. Only updateAll's own end-of-run refresh ever
fills PluginStateManager, so from the second run on it sent the first
run's plugins: one uninstalled since failed with "plugin not found" and
one installed since was never updated. That refresh also only replaced
window.installedPlugins, so the installed cards and the Updates badge
kept offering "Update to vX" for what had just been updated.

Read window.installedPlugins, the list plugins_manager.js republishes
after every install, uninstall and refresh, keeping PluginStateManager
as the fallback for a page without it, and refresh through
pluginManager.loadInstalledPlugins(true), which redraws the grid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The on-demand modal fills its Display Mode select from
plugin.display_modes, but /plugins/installed never sent the field. Every
plugin offered one option, its own id, under "This plugin exposes a
single display mode"; the display resolved that id to the plugin's first
mode, so a multi-mode plugin could only be started, or pinned, there.

Add display_modes to each entry, read from the plugin catalog
(get_plugin_display_modes), the same declared list /display/modes and
on-demand/start use, keeping only strings. Single-mode plugins still get
one option and the same hint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…einstall

The store's Install button enabled the new plugin by the registry id it
installed. Weather, Music, Stocks and Leaderboard install under the id
their manifests declare (weather -> ledmatrix-weather); the plugin list,
the config section and /plugins/toggle know only that id, so the toggle
answered 404 "Plugin not found" and the plugin stayed disabled behind
"installed, but enabling it failed". The same button on an installed
plugin (Reinstall) enabled it too, switching a plugin the user had
turned off back on.

POST /plugins/install now names the installed plugin: plugin_id in the
direct answer and in the queued operation's result, read from the
installed manifest found the way the store's update and uninstall find
it (_find_plugin_path: id, aliases, plugin_path name), else the
requested id. The client reloads the list, then enables that id; from
an answer without it, the installed entry the store entry matches
(findInstalledStorePlugin, which isStorePluginInstalled now uses). A
reinstall, decided by the same match that labelled the button, reloads
the list and leaves the enabled state alone.

test/js/plugins_manager_sandbox.js runs the whole of
plugins_manager.js in a vm context against a fake DOM and API, for
suites that drive its real flows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pollOperationStatus gave a queued install 60 polls, a second apart,
then reported "Install operation timed out" as an error and stopped.
The server allows the plugin's dependency install 300 s on its own
(install_requirements_file in store_install.py), after a download that
fetches the plugin a file at a time, so installs that went on to
succeed were reported as failed, never enabled, and left out of the
installed list until the page was reloaded.

Give installs INSTALL_POLL_MAX_ATTEMPTS (600, ten minutes). When even
that runs out, reload the installed list and the store badges and warn
that the install may still be running; nothing is enabled without the
operation's answer. Uninstall keeps the default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The #plugin-category select listed seven fixed categories while the
registry uses about twenty (productivity, utility, transit, finance,
...), so roughly a third of the store could not be filtered to, and
"Financial" missed the plugin filed under "finance".

The template now ships only "All Categories"; syncStoreCategoryOptions,
run by applyStoreFiltersAndSort, adds one option per category the cached
store plugins have (case folded, as the filter compares), keeps the
current choice, and rebuilds only when the set changes or the partial
was swapped in afresh -- the way the Starlark section builds its own.

The test sandbox gains window.addEventListener (initPluginsPage needs
it) and quiets the script's "element not found" warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#install-plugin-from-url had an inline onclick calling
window.handleGitHubPluginInstall, and attachInstallButtonHandler also
gave it a click listener that installs, so both ran on every click
(and on Enter, which clicks it). The inline handler threw a
ReferenceError -- it called isGithubUrl, which is local to the
plugin-manager IIFE, from outside it -- so only the listener's request
went out; correcting that scope alone would have sent every install
twice.

Remove the inline onclick and the window.handleGitHubPluginInstall it
called, which nothing else uses. The listener, which already sent the
only request, is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 28 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8f232b5b-8e95-4f02-9f3a-74336de80e57
📥 Commits

Reviewing files that changed from the base of the PR and between ef69201 and 55b8990.

📒 Files selected for processing (17)
  • CHANGELOG.md
  • test/js/README.md
  • test/js/plugins_manager_sandbox.js
  • test/js/run_all.js
  • test/js/unit/test_github_url_install.js
  • test/js/unit/test_install_polling.js
  • test/js/unit/test_store_categories.js
  • test/js/unit/test_store_install.js
  • test/js/unit/test_store_registry_fields.js
  • test/js/unit/test_update_all.js
  • test/test_api_v3_install_reports_installed_id.py
  • test/test_api_v3_installed_display_modes.py
  • web_interface/blueprints/api_v3/plugin_store.py
  • web_interface/blueprints/api_v3/plugins.py
  • web_interface/static/v3/js/plugins/install_manager.js
  • web_interface/static/v3/plugins_manager.js
  • web_interface/templates/v3/partials/plugins.html
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 6 complexity · 0 duplication

Metric Results
Complexity 6
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@ChuckBuilds
ChuckBuilds merged commit e32d177 into main Oct 4, 2026
15 checks passed
@ChuckBuilds
ChuckBuilds deleted the fix/web-plugin-manager branch October 4, 2026 02:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant