Co-authored-by: Kandev Agent <agent@kandev.dev>
8.2 KiB
created, status, requirements, system_design, legacy_specs
| created | status | requirements | system_design | legacy_specs | ||
|---|---|---|---|---|---|---|
| 2026-09-04 | complete |
|
|
Implementation Plan: Plugin Shortcut Settings
Overview
Move dynamic plugin shortcut editors out of Preferences > Keyboard Shortcuts and into each plugin's detail page while preserving the existing per-user override map, shared Save behavior, full conflict comparison, and dispatch rules. Implement the vertical frontend behavior and its desktop/member/mobile evidence first, then reconcile the public plugin references with the shipped location.
The plugin system owns this plan because manifest-declared keybindings and their namespaced overrides are plugin contracts. The UI system supplies the existing save coordinator and responsive settings shell without taking ownership of the plugin behavior.
Scope
In scope
- Render Kandev-owned shortcuts only on the global keyboard-shortcuts page.
- Render one selected plugin's declared shortcuts on that plugin's detail route for members and administrators.
- Preserve namespaced portable overrides, draft/reset/clear semantics, conflict warnings, core precedence, and inactive-plugin behavior.
- Keep the phone flow within the existing direct plugin detail route, with touch-usable controls and no page-level horizontal overflow.
- Update user-facing plugin docs, the manifest reference, and the maintained frontend plugin API contract to name the new configuration location and permission split.
Out of scope
- Backend, manifest-schema, plugin SDK, registry, or shortcut-dispatch changes.
- A new Settings route, drawer, plugin shortcut search index, or plugin-list shortcut badge/count.
- Changing access to plugin-authored settings, plugin config, or lifecycle actions.
- Cleaning up stored shortcut overrides during uninstall.
- Updating the core keyboard-shortcuts documentation screenshot, which remains accurate as a core-only settings view.
Technical approach
Core and plugin shortcut presentation
Keep KeyboardShortcutsSettings responsible for the chat-submit preference and
the Kandev shortcut draft. Continue loading plugin declarations there only so
core rows can report cross-source conflicts. Refine
KeyboardShortcutsCard so its rendered entries are always
CONFIGURABLE_SHORTCUTS, while its conflict input can still include every
plugin entry.
Add a plugin-focused shortcut settings component under
apps/web/components/settings/plugins/. It receives the selected plugin and
complete installed plugin list, filters visible entries to the selected owner,
and reuses the current recorder and conflict-label logic. Place it outside
PluginDetail's canManage block, after operator configuration and before the
manifest card. Do not render it for a plugin without parseable keybindings.
When an active bundle replaces the exact plugin detail route, wrap its content
and append the same host-owned card so the supported route and keybinding
capabilities remain composable.
Persistence and permissions
Register a route-local plugin-shortcuts:{pluginId} save contributor. Its
draft contains the existing complete StoredShortcutOverrides map and saves
through updateUserSettings({ keyboard_shortcuts: ... }); it never calls the
plugin configuration endpoint. Synchronize the baseline after initial
hydration and later portable-settings updates, rebase local changes onto the
newest complete map at save time, reject stale response authority, and preserve
edits made while the request is pending. Preserve the existing namespaced keys
so no migration or dispatcher change is needed. When an administrator changes
both plugin config and a personal shortcut, the shared coordinator owns partial
success, retry, discard, and navigation protection across the two contributors.
Responsive composition
Reuse the existing direct phone path demonstrated by
mobile-plugin-settings-row.spec.ts: Settings navigation sheet > Plugins >
full plugin row > detail page. Keep the card inline rather than introducing a
drawer. On narrow/coarse-pointer layouts, let shortcut rows stack as needed,
give recorder/reset/clear controls a 44 px active dimension, retain the page's
single vertical scroll owner, and assert no document horizontal overflow.
Desktop keeps the denser inline row arrangement.
Copy and documentation
Add localized, self-documenting copy explaining that plugin shortcuts are personal and only run while the plugin is active. Update English, Portuguese, Simplified Chinese, and generated Traditional Chinese catalogs, plus the pseudo-locale through the existing scripts.
Update docs/public/plugins.md, docs/public/plugins-manifest.md, and
docs/public/plugins-authoring.md, whose dominant types remain explanation,
reference, and how-to respectively. Reconcile the maintained
docs/plans/plugins/PLUGIN-API.md contract comment. No public screenshot
currently shows plugin rows on the core page, so no image replacement is
required.
Tests
apps/web/components/settings/keyboard-shortcuts-card.test.tsxproves the global card renders core entries only while still reporting a conflict with a plugin binding.- A focused plugin-shortcut card test under
apps/web/components/settings/plugins/proves selected-owner rendering, no empty card, draft/reset behavior, and shortcut-only persistence without a plugin-config mutation. apps/web/src/settings-routes.plugin.test.tsproves an exact plugin detail route replacement retains both the plugin-owned content and host shortcut surface.apps/web/lib/keyboard/plugin-shortcuts.test.tsandapps/web/lib/keyboard/shortcut-conflicts.test.tspreserve entry identity, labels, defaults, and core/plugin comparison behavior.apps/web/hooks/use-plugin-shortcuts.test.tsremains targeted regression evidence that the relocated override still dispatches and core bindings win.
E2E tests
- Extend
apps/web/e2e/tests/auth/plugin-settings-member.spec.tsforAC-PLUGINS-PLUGIN-SHORTCUT-SETTINGS-001.1through.5: install as an administrator, sign in as a member, prove the plugin recorder is absent from the global page and present on the plugin detail page while operator controls remain hidden, then save and reload the member's override. - Extend
apps/web/e2e/tests/plugins/mobile-plugin-settings-row.spec.tsforAC-PLUGINS-PLUGIN-SHORTCUT-SETTINGS-001.6and.7: enter through the existing full-card tap after disabling the fixture plugin, edit and save its shortcut, reload, and verify touch geometry, internal vertical scrolling, and zero document horizontal overflow. Capture and restore the persisted shortcut baseline so the worker does not leak settings state.
Both scenarios must be written and observed failing for the old placement before the production UI changes are made.
Work orders
Verification results
- Task 01 targeted unit, typecheck, lint, i18n, E2E sleep-ratchet, authenticated member E2E, and mobile E2E checks passed.
- Task 02 scoped documentation searches, public-doc tests/validation, and whitespace checks passed.
- Review remediation removed unrelated generated locale churn and covered the exact-root plugin settings-route compatibility path.
- Review remediation rebased shortcut drafts across hydration, live settings updates, and in-flight saves, and made both persistence E2E scenarios await the successful settings response before reload.
Risks
- The plugin detail page currently groups its configuration UI under an administrator gate; placing the new personal card inside that block would silently remove member access.
- Separating rendered lists can accidentally narrow conflict detection to the visible page and hide core-versus-plugin or plugin-versus-plugin collisions.
- Saving still uses the complete-map backend contract, so the contributor must continue rebasing its local delta onto the latest authoritative store map.
- Mobile recorder rows currently use compact desktop geometry and need an explicit narrow/coarse-pointer composition to avoid clipped actions.