Documentation Index
Fetch the complete documentation index at: https://docs.aspfox.com/llms.txt
Use this file to discover all available pages before exploring further.
Using the command palette
Open the command palette with Cmd+K on macOS or Ctrl+K on Windows and Linux. It opens from any page.
- Type to filter commands by name
- Arrow keys to navigate between results
- Enter to execute the selected command
- Escape to close
The palette is permission-aware: commands the current user does not have access to are not shown. An admin user sees the Admin and Users commands; a regular member does not.
Available commands
| Command | Group | Required permission |
|---|
| Dashboard | Navigation | None |
| Billing | Navigation | tenant.billing.read |
| Settings | Navigation | tenant.settings.read |
| Members | Navigation | tenant.members.read |
| Profile | Navigation | None |
| Admin Dashboard | Navigation | is_admin |
| Admin Users | Navigation | is_admin |
| Admin Tenants | Navigation | is_admin |
| Invite Member | Actions | tenant.members.invite |
| Switch Workspace | Actions | None (user must have multiple tenants) |
| Manage Billing | Actions | tenant.billing.manage |
| Sign Out | Actions | None |
| Switch to Light | Theme | None |
| Switch to Dark | Theme | None |
| Use System Theme | Theme | None |
Adding a navigation command
// frontend/src/components/command-palette/CommandPalette.tsx
// Navigation commands are in the navigationCommands array.
// Add your new command:
{
id: 'nav-projects',
label: 'Projects',
group: 'Navigation',
icon: FolderOpen, // from lucide-react
requiredPermission: Permissions.TenantProjectsRead, // omit if no permission needed
action: () => navigate('/projects'),
},
Adding a permission-gated action command
// Action commands with a required permission:
{
id: 'action-create-project',
label: 'Create Project',
group: 'Actions',
icon: Plus,
requiredPermission: Permissions.TenantProjectsManage,
action: () => {
setCreateProjectModalOpen(true);
closeCommandPalette();
},
},
The requiredPermission field is checked against the current user’s JWT permissions. If the user does not have the permission, the command is filtered out before the list renders.
Adding a command that opens a modal
{
id: 'action-invite-member',
label: 'Invite Member',
group: 'Actions',
icon: UserPlus,
requiredPermission: Permissions.TenantMembersInvite,
action: () => {
// Close the palette first, then open the modal.
// Opening both simultaneously causes focus trap conflicts.
closeCommandPalette();
setTimeout(() => setInviteModalOpen(true), 50);
},
},
Customizing the keyboard shortcut
The shortcut is set in CommandPalette.tsx in the useEffect that registers the keydown listener:
useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setOpen((open) => !open);
}
};
document.addEventListener('keydown', down);
return () => document.removeEventListener('keydown', down);
}, []);
To change to Cmd+P / Ctrl+P:
if (e.key === 'p' && (e.metaKey || e.ctrlKey)) {
Adding command groups
Commands are grouped by their group property. Groups appear as headings in the palette. To add a new group:
// Just set a new group name on your command object.
// The palette renders groups automatically — no group registration needed.
{
id: 'projects-create',
label: 'New Project',
group: 'Projects', // new group — renders as "Projects" heading
icon: Plus,
action: () => navigate('/projects/new'),
},
Groups appear in the order their first command appears in the allCommands array. Put frequently used groups (Navigation, Actions) at the top of the array.