diff --git a/docs/superpowers/specs/2026-04-02-context-aware-cmdk-design.md b/docs/superpowers/specs/2026-04-02-context-aware-cmdk-design.md new file mode 100644 index 00000000..18bac7e9 --- /dev/null +++ b/docs/superpowers/specs/2026-04-02-context-aware-cmdk-design.md @@ -0,0 +1,66 @@ +# Context-Aware Cmd-K Search + +## Goal + +Make the cmd-k command palette context-aware: when the user is on admin pages, search users, groups, and roles instead of operational data (applications, routes, exchanges, agents). + +## Background + +The `CommandPalette` component from `@cameleer/design-system` currently searches five categories: application, exchange, attribute, route, agent. All data is assembled in `LayoutShell.tsx` and passed as a flat `SearchResult[]` array. The `SearchCategory` type is a closed union in the DS. + +The DS team is already updating `CommandPalette` to accept an open `SearchCategory` type (string instead of fixed union) and handle unknown categories gracefully (title-cased tab labels, default icons). + +## Approach + +Route-based data swap in `LayoutShell`. The component already tracks `isAdminPage` via `useLocation()`. When true, it provides admin search data instead of operational search data. No new providers, contexts, or abstractions. + +## Design + +### Search Data Swap (LayoutShell.tsx) + +When `isAdminPage` is true: + +1. **Skip operational data assembly** — do not build catalog/agent/attribute search items, do not fire the debounced `useSearchExecutions` query +2. **Fetch admin data** — call existing hooks: `useUsers()`, `useGroups()`, `useRoles()` from `api/queries/admin/rbac.ts` +3. **Build admin search items** — map each entity to a `SearchResult`: + - **Users**: `{ id: 'user:{userId}', category: 'user', title: displayName, meta: userId, path: '/admin/rbac' }` + - **Groups**: `{ id: 'group:{id}', category: 'group', title: name, meta: parentGroupId ?? 'top-level group', path: '/admin/rbac' }` + - **Roles**: `{ id: 'role:{id}', category: 'role', title: name, meta: scope, path: '/admin/rbac' }` +4. **Swap** — `searchData = isAdminPage ? adminSearchData : operationalSearchData` + +All three admin collections are small (dozens of items at most) — client-side filtering is sufficient. No server-side search endpoint needed. + +### Navigation on Select + +When the user selects an admin search result: + +1. Navigate to `/admin/rbac` with location state: `{ tab: 'users' | 'groups' | 'roles', highlight: itemId }` +2. Tab mapping from category: `'user' → 'users'`, `'group' → 'groups'`, `'role' → 'roles'` +3. `RbacPage` reads location state, switches to the correct tab, and highlights the matching item +4. The `highlight` state is consumed once — after reading, clear it so back-navigation doesn't re-trigger + +### Submit Handler (Enter without selection) + +When the user presses Enter without explicitly selecting a result, navigate to the top (first visible) result — same behavior as selecting it. This matches standard command palette UX. + +### Operational search behavior (unchanged) + +When not on admin pages, search data assembly remains exactly as-is: catalog apps/routes, agents, exchanges (server-side), and attributes. + +## Files Changed + +| File | Change | +|------|--------| +| `ui/src/components/LayoutShell.tsx` | Import admin hooks, build admin search data, swap based on `isAdminPage`, update `handlePaletteSelect` for admin categories | +| `ui/src/pages/Admin/RbacPage.tsx` | Read location state `{ tab, highlight }`, switch tab and highlight item on mount | + +## Dependencies + +- `@cameleer/design-system` update: `SearchCategory` opened to `string`, unknown categories handled gracefully (DS team already working on this) + +## Out of Scope + +- Audit log search via cmd-k (has its own dedicated filter UI) +- App config search via cmd-k (already accessible via operational sidebar) +- Server-side admin search endpoint (not needed for small collections) +- Admin-specific submit behavior beyond navigating to top result