feat(plugins): move shortcuts into plugin settings (#3384)

Co-authored-by: Kandev Agent <agent@kandev.dev>
This commit is contained in:
João Salavisa
2026-09-04 17:38:05 +00:00
committed by GitHub
parent 820dc3bae6
commit e4eb278f21
29 changed files with 1709 additions and 146 deletions
+10 -1
View File
@@ -254,6 +254,15 @@ state after load or when the workspace list changes. If a required read is
missing, extend this typed context contract; do not reverse-engineer Zustand
slice shapes in a released plugin.
Users configure a plugin's declared shortcuts on that installed plugin's detail
page at **Settings > Plugins > `<plugin>`**. These overrides are personal to
the signed-in user. The plugin's schema-driven configuration and lifecycle
controls remain install-wide and administrator-only.
If a bundle registers a component at its exact plugin detail route, Kandev
keeps that component and renders the host-owned shortcut card alongside it.
Nested plugin settings routes remain fully plugin-owned.
### Frontend hook/API matrix
| Surface | Location and input | Manifest requirement | Cleanup/lifecycle | Small example |
@@ -263,7 +272,7 @@ slice shapes in a released plugin.
| registerSettingsRoute | registerSettingsRoute(fullPath, Component) with an exact path under /settings/plugins/<id>/...; settings shell supplies chrome | Active ui.bundle | Route is removed on disable/uninstall | registry.registerSettingsRoute("/settings/plugins/acme/health", HealthPage) |
| registerComponent | registerComponent(slot, Component); component receives { slotProps?: unknown } | Active ui.bundle | Every registration is owner-tracked, error-isolated, and bulk-revoked | registry.registerComponent("task-sidebar", Panel) |
| registerWsHandler | registerWsHandler(action, handler(payload)); receives actions bridged from lib/ws | Active ui.bundle | Handler is removed on disable/uninstall; tolerate duplicate/replayed actions | registry.registerWsHandler("acme.updated", renderUpdate) |
| registerKeybinding | registerKeybinding(id, handler(event)); id must be declared in ui.keybindings; users can override the effective combo | ui.bundle and ui.keybindings[] | Handler is removed on disable/uninstall; core shortcuts win, and editable targets are skipped unless that entry set ui.keybindings[].allow_in_editor | registry.registerKeybinding("open-panel", () => host.openModal(...)) |
| registerKeybinding | registerKeybinding(id, handler(event)); id must be declared in ui.keybindings; users can override the effective combo on the installed plugin detail page at **Settings > Plugins > `<plugin>`** | ui.bundle and ui.keybindings[] | Handler is removed on disable/uninstall; core shortcuts win, and editable targets are skipped unless that entry set ui.keybindings[].allow_in_editor | registry.registerKeybinding("open-panel", () => host.openModal(...)) |
| registerIntegrationSettings | One provider-owned settings component with an optional action mounted in the detail header and the integrations index card | Active ui.bundle | Registration is exclusive by id and revoked on unload; host owns workspace selection and settings navigation; component and action receive the routed `workspaceId`, and action receives its `surface` | registry.registerIntegrationSettings({ id: "acme", Component, action: Toggle }) |
| registerTranslations | Flat English fallback plus optional Kandev locale catalogs, isolated to this plugin's namespace | Active ui.bundle | Catalogs are replaced atomically, removed on unload, and registry consumers invalidate when the host locale changes | registry.registerTranslations({ en: { settings: "Settings" }, "pt-pt": { settings: "Definições" } }) |
| registerRepositoryProvider | Provider-owned paged/searchable repository list, URL match/inspect, branches, and optional native `createChangeRequest` transport | ui.bundle and matching `repository_providers[]` id | Registration and in-flight callbacks are result-fenced on unload; host owns native task and Create PR UI | registry.registerRepositoryProvider({ id: "acme", ...provider }) |