feat(dashboard): add hide/show toggle for dashboard plugins in sidebar
- New config key: dashboard.hidden_plugins (list of plugin names)
- GET /api/dashboard/plugins now filters out hidden plugins from sidebar
- POST /api/dashboard/plugins/{name}/visibility toggles visibility
- Hub response includes user_hidden boolean per plugin row
- Eye/EyeOff toggle on plugin cards with dashboard manifests
- i18n: 'Show in sidebar' / 'Hide from sidebar' (en/zh)
This commit is contained in:
parent
a52363231f
commit
c73b799de7
@ -3617,12 +3617,16 @@ def _get_dashboard_plugins(force_rescan: bool = False) -> list:
|
||||
|
||||
@app.get("/api/dashboard/plugins")
|
||||
async def get_dashboard_plugins():
|
||||
"""Return discovered dashboard plugins."""
|
||||
"""Return discovered dashboard plugins (excludes user-hidden ones)."""
|
||||
plugins = _get_dashboard_plugins()
|
||||
# Strip internal fields before sending to frontend.
|
||||
# Read user's hidden plugins list from config.
|
||||
config = load_config()
|
||||
hidden: list = cfg_get(config, "dashboard", "hidden_plugins", default=[]) or []
|
||||
# Strip internal fields before sending to frontend and filter out hidden.
|
||||
return [
|
||||
{k: v for k, v in p.items() if not k.startswith("_")}
|
||||
for p in plugins
|
||||
if p["name"] not in hidden
|
||||
]
|
||||
|
||||
|
||||
@ -3662,6 +3666,10 @@ def _merged_plugins_hub() -> Dict[str, Any]:
|
||||
disabled_set = _get_disabled_set()
|
||||
enabled_set = _get_enabled_set()
|
||||
|
||||
# Read user-hidden plugins from config for the user_hidden field.
|
||||
config = load_config()
|
||||
hidden_plugins: list = cfg_get(config, "dashboard", "hidden_plugins", default=[]) or []
|
||||
|
||||
plugins_root_resolved = (get_hermes_home() / "plugins").resolve()
|
||||
rows: List[Dict[str, Any]] = []
|
||||
|
||||
@ -3718,6 +3726,7 @@ def _merged_plugins_hub() -> Dict[str, Any]:
|
||||
"can_update_git": can_remove_update and (Path(dir_str) / ".git").exists(),
|
||||
"auth_required": auth_required,
|
||||
"auth_command": auth_command,
|
||||
"user_hidden": name in hidden_plugins,
|
||||
})
|
||||
|
||||
agent_names = {r["name"] for r in rows}
|
||||
@ -3863,6 +3872,33 @@ async def put_plugin_providers(request: Request, body: _PluginProvidersPutBody):
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
class _PluginVisibilityBody(BaseModel):
|
||||
hidden: bool
|
||||
|
||||
|
||||
@app.post("/api/dashboard/plugins/{name}/visibility")
|
||||
async def post_plugin_visibility(request: Request, name: str, body: _PluginVisibilityBody):
|
||||
"""Toggle a plugin's sidebar visibility (persists to config.yaml dashboard.hidden_plugins)."""
|
||||
_require_token(request)
|
||||
name = _validate_plugin_name(name)
|
||||
|
||||
config = load_config()
|
||||
if "dashboard" not in config or not isinstance(config.get("dashboard"), dict):
|
||||
config["dashboard"] = {}
|
||||
hidden_list: list = config["dashboard"].get("hidden_plugins") or []
|
||||
if not isinstance(hidden_list, list):
|
||||
hidden_list = []
|
||||
|
||||
if body.hidden and name not in hidden_list:
|
||||
hidden_list.append(name)
|
||||
elif not body.hidden and name in hidden_list:
|
||||
hidden_list.remove(name)
|
||||
|
||||
config["dashboard"]["hidden_plugins"] = hidden_list
|
||||
save_config(config)
|
||||
return {"ok": True, "name": name, "hidden": body.hidden}
|
||||
|
||||
|
||||
@app.get("/dashboard-plugins/{plugin_name}/{file_path:path}")
|
||||
async def serve_plugin_asset(plugin_name: str, file_path: str):
|
||||
"""Serve static assets from a dashboard plugin directory.
|
||||
|
||||
@ -295,6 +295,8 @@ export const en: Translations = {
|
||||
authRequiredHint: "Run this command to authenticate:",
|
||||
updateGit: "Git pull",
|
||||
versionBadge: "Version",
|
||||
showInSidebar: "Show in sidebar",
|
||||
hideFromSidebar: "Hide from sidebar",
|
||||
},
|
||||
|
||||
skills: {
|
||||
|
||||
@ -266,6 +266,8 @@ export interface Translations {
|
||||
authRequiredHint: string;
|
||||
updateGit: string;
|
||||
versionBadge: string;
|
||||
showInSidebar: string;
|
||||
hideFromSidebar: string;
|
||||
};
|
||||
|
||||
// ── Profiles page ──
|
||||
|
||||
@ -291,6 +291,8 @@ export const zh: Translations = {
|
||||
authRequiredHint: "运行此命令以完成认证:",
|
||||
updateGit: "git pull",
|
||||
versionBadge: "版本",
|
||||
showInSidebar: "在侧边栏显示",
|
||||
hideFromSidebar: "从侧边栏隐藏",
|
||||
},
|
||||
|
||||
skills: {
|
||||
|
||||
@ -299,6 +299,16 @@ export const api = {
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
|
||||
setPluginVisibility: (name: string, hidden: boolean) =>
|
||||
fetchJSON<{ ok: boolean; name: string; hidden: boolean }>(
|
||||
`/api/dashboard/plugins/${encodeURIComponent(name)}/visibility`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ hidden }),
|
||||
},
|
||||
),
|
||||
|
||||
// Dashboard themes
|
||||
getThemes: () =>
|
||||
fetchJSON<DashboardThemesResponse>("/api/dashboard/themes"),
|
||||
@ -728,6 +738,7 @@ export interface HubAgentPluginRow {
|
||||
can_update_git: boolean;
|
||||
auth_required: boolean;
|
||||
auth_command: string;
|
||||
user_hidden: boolean;
|
||||
}
|
||||
|
||||
export interface PluginsHubProviders {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { ExternalLink, RefreshCw, Puzzle, Trash2 } from "lucide-react";
|
||||
import { ExternalLink, RefreshCw, Puzzle, Trash2, Eye, EyeOff } from "lucide-react";
|
||||
import type { Translations } from "@/i18n/types";
|
||||
import { Link } from "react-router-dom";
|
||||
import { api } from "@/lib/api";
|
||||
@ -504,6 +504,27 @@ function PluginRowCard(props: PluginRowCardProps) {
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
{row.has_dashboard_manifest ? (
|
||||
<Button
|
||||
disabled={busy}
|
||||
ghost
|
||||
size="sm"
|
||||
title={row.user_hidden ? t.pluginsPage.showInSidebar : t.pluginsPage.hideFromSidebar}
|
||||
onClick={() => {
|
||||
void setRuntimeLoading(row.name, async () => {
|
||||
await api.setPluginVisibility(row.name, !row.user_hidden);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{row.user_hidden ? (
|
||||
<EyeOff className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
<Eye className="h-3.5 w-3.5" />
|
||||
)}
|
||||
{row.user_hidden ? t.pluginsPage.showInSidebar : t.pluginsPage.hideFromSidebar}
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
{row.can_remove ? (
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user