feat: show update available when catalog version differs from installed

Catalog partial now compares catalog version vs installed plugin.yaml version.
Gold border + "Update available — installed vX.Y.Z" badge when newer.
Update button uses btn-warn styling. Route passes installed_versions dict.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 18:42:50 -04:00
parent 3f437141fb
commit 484da1f263
2 changed files with 20 additions and 5 deletions
+2
View File
@@ -256,11 +256,13 @@ async def plugins_catalog():
discovered = _discover_plugins()
installed_names = {p["_dir"] for p in discovered}
installed_versions = {p["_dir"]: p.get("version", "0.0.0") for p in discovered}
return await render_template(
"settings/_catalog_partial.html",
catalog=catalog,
installed_names=installed_names,
installed_versions=installed_versions,
loaded_names=_LOADED_PLUGINS,
error=error,
)
@@ -12,17 +12,27 @@
{% for plugin in catalog %}
{% set is_installed = plugin.name in installed_names %}
{% set is_loaded = plugin.name in loaded_names %}
<div class="card" style="margin-bottom:0;padding:1rem 1.25rem;">
{% set installed_ver = installed_versions.get(plugin.name, "") %}
{# update_available: catalog version is strictly newer than what's on disk #}
{% set update_available = is_installed and installed_ver and installed_ver != plugin.version %}
<div class="card" style="margin-bottom:0;padding:1rem 1.25rem;{% if update_available %}border-color:var(--gold);{% endif %}">
<div style="display:flex;align-items:flex-start;gap:1rem;">
<div style="flex:1;min-width:0;">
<div style="display:flex;align-items:center;gap:0.6rem;flex-wrap:wrap;">
<span style="font-weight:600;color:var(--text);font-size:0.9rem;">{{ plugin.name }}</span>
<span style="font-size:0.75rem;color:var(--text-muted);">v{{ plugin.version }}</span>
{% if is_loaded %}
{% if update_available %}
<span class="badge badge-gold" style="font-size:0.68rem;">
Update available — installed v{{ installed_ver }}
</span>
{% elif is_loaded %}
<span class="badge badge-green" style="font-size:0.68rem;">Active</span>
{% elif is_installed %}
<span class="badge" style="background:var(--bg-elevated);color:var(--text-muted);font-size:0.68rem;">Installed</span>
{% endif %}
{% for tag in plugin.tags %}
<span style="font-size:0.68rem;padding:0.1em 0.4em;background:var(--bg-elevated);color:var(--text-muted);border-radius:3px;">{{ tag }}</span>
{% endfor %}
@@ -46,8 +56,8 @@
class="btn btn-ghost btn-sm" style="font-size:0.78rem;">Source</a>
{% endif %}
{% if is_loaded %}
{# Already active — update available if versions differ #}
{% if update_available %}
{# Installed but outdated — offer update (download + restart) #}
<form method="post"
action="/settings/plugins/install/{{ plugin.name }}"
hx-post="/settings/plugins/install/{{ plugin.name }}"
@@ -56,9 +66,12 @@
style="margin:0;">
<input type="hidden" name="download_url" value="{{ plugin.download_url }}">
<input type="hidden" name="checksum_sha256" value="{{ plugin.checksum_sha256 }}">
<button type="submit" class="btn btn-ghost btn-sm" style="font-size:0.78rem;">Update</button>
<button type="submit" class="btn btn-warn btn-sm" style="font-size:0.78rem;">Update</button>
</form>
{% elif is_loaded %}
{# Active and up to date — no action needed #}
{% elif is_installed %}
{# Installed but not active — offer hot-reload #}
<form method="post"