Dashboard

Command palette and keyboard shortcuts

The dashboard command palette, exactly what it searches, and every keyboard shortcut the app binds, with the surface each one is armed on.

The dashboard binds a small, fixed set of shortcuts. There is no remapping screen and no preference to disable them: the keys are hard coded, and each one is armed only where the action it triggers actually exists. This page lists all of them and states plainly where each is bound and where it does nothing.

The sidebar search button draws ⌘ K on macOS and Ctrl K everywhere else, and the narrow screen header search button carries the same label in its tooltip rather than on its face, but that label is only about which symbol to draw. Every combination below accepts either the meta key or the control key on every platform, so Ctrl K works on a Mac too.

Every shortcut

ShortcutActionWhere it applies
⌘ K / Ctrl KOpen the command palette, or close it if it is already openEvery page inside the dashboard shell
↑ ↓Move the highlight in the palette, wrapping at both endsWhile the palette is open
Home / EndJump to the first or last resultWhile the palette is open
↵Open the highlighted resultWhile the palette is open
EscClose the paletteWhile the palette is open
cThe page's primary create actionProducts, a product, a project, an environment table view, Team, Keys, Roles, Webhooks
pOpen Publish versionThe environment table view only
⌘ 1 to ⌘ 9 / Ctrl 1 to Ctrl 9Jump to the first nine entries of whichever switcher the sidebar is currently showingEvery page inside the dashboard shell. What it switches depends on how deep the URL is
↵ while the pointer is hovering a sidebar linkActivate that linkThe sidebar navigation lists, not the switcher button

The dashboard shell is everything behind the sign-in wall. The sign-in page, the OTP page and the CLI authorization page sit outside it, so none of these shortcuts exist there.

Keys that belong to modals

Two more keys are bound by the modal machinery rather than by a page, which is why they are not in the table above. Esc closes the dialog, sheet or drawer on top, and the close button carries an esc chip for as long as that works: a modal that is mid request blocks the key and drops the chip, and the create key modal also blocks it while the new secret is still on screen. ↵ submits the modal you are in when focus is in a text field, file inputs excepted, and it is ignored while that modal is saving or while its form is not complete. The publish version modal is the only one that also takes a bare ↵ outside a field, with ⌘ ↵ or Ctrl ↵ for its note textarea. Both keys work on the sign-in and OTP forms too, which are otherwise outside all of this.

The command palette

⌘ K toggles: pressing it with the palette open closes it again. The listener sits on the window with no guard for the focused element, so it opens even while you are typing in a search box or a variable field.

Open it and the input reads Search products, projects, environments and pages..., which is the literal scope. Results arrive in four groups, always in this order, with empty groups hidden entirely:

GroupSourceOrder
ProductsEvery product in the current workspace you can accessNewest created first
ProjectsEvery project in the current workspace you can access, across all productsNewest created first
EnvironmentsEnvironments of the 25 most recently created projects onlyNewest created first, project by project
PagesThe sidebar navigation entries that resolve for the current URLFixed nav order

Typing is debounced by 200 ms, then the query is sent to the API. Matching on products, projects and environments is a case insensitive substring match against the name and the slug, nothing else. An empty query is not a special case, it simply matches everything, so opening the palette and typing nothing lists the whole workspace. Page entries are filtered in the browser instead, against a keyword string built from the entry's title, its nav group and its URL.

The environment group fans out over the 25 most recently created projects only. If the workspace has more projects than that, environments in the older ones are never searched and never appear, with no indication that anything was left out. The projects and products groups are not capped this way. Environments are also matched on their own name and slug only, so typing a project's name surfaces the project but not its environments.

Results are permission filtered

Nothing in the palette is a shortcut around access control. Every group is filtered before it reaches you:

  • Products, projects and environments are fetched through the same list endpoints the rest of the dashboard uses. Each requires the matching read permission (products:read, projects:read, environments:read); without it the request is refused with 401 and that group is simply empty.
  • Those endpoints then drop anything inside a private product you have not been granted. Any role carrying products:manage_private, which owners and admins both do, is unrestricted. Everyone else sees every product that is not private plus the private ones granted to them, and a project or environment under a product you cannot reach never enters the result set.
  • Page entries carrying a permission are dropped when your role does not hold it, so Team is absent without members:read, Billing without billing:read, Keys without keys:read, Webhooks without webhooks:read and Products without products:read. Settings, Roles, Encryption, CI / Machine Access and Virtual Envs carry no permission and are always listed.

Which pages are listed

The workspace level pages are always candidates. Product, project and environment pages are added only when the URL you are on already carries the slugs needed to build them, because an entry whose path still contains an unresolved :param is discarded rather than guessed at:

You are onPages added
Any dashboard pageProducts, Team, Billing, Keys, Webhooks, Settings, Roles, Encryption, CI / Machine Access, Virtual Envs
A product URLProduct projects, Product settings
A project URLProject environments, Project settings (plus the product ones)
An environment URLEnvironment variables, Environment versions, Environment settings (plus the product and project ones)

Scoped entries are titled with their group in front, so you search for environment versions rather than versions. Duplicate destinations collapse to the first one that claimed the URL, which is why /settings appears once as Settings and not again as Workspace. Every Back to ... nav entry is skipped.

What the palette does not do

It is a navigator, not a command runner. It has no actions in it: nothing in the list creates, publishes, toggles or deletes anything, selecting a result only routes you to a page. It also does not search:

  • Variable names or values. Values are ciphertext the server cannot read, and names are not indexed here either. Search variables from the environment page itself, covered in Variables in the dashboard.
  • Versions, including version labels and notes.
  • Team members, roles, API keys, webhooks or invoices.
  • Workspaces. The palette only ever covers the workspace you are currently in. Switching workspaces is ⌘ 1 to ⌘ 9 or the switcher menu.
  • Pages that are not in the sidebar navigation, which means Account, Preferences, CLIs, Billing invoices, the raw .env editor and a specific version's page are unreachable from the palette even though they exist.

The preview pane on the right is hidden below the md breakpoint, so on a narrow window the palette is the result list alone. The highlight also follows the mouse, so moving the pointer across the list changes what ↵ will open.

The create shortcut

c is context aware: exactly one create action is armed per page, and pressing c runs whichever one it is. No modifier is involved, and the handler returns immediately if meta, control or alt is held, so ⌘ C stays copy. Shift is not excluded, so a capital C triggers it too.

Pagec opensNeeds
ProductsCreate productproducts:create
A productCreate projectprojects:create
A projectCreate environmentenvironments:create, project not locked
An environment (table view)Add variablesvariables:create, project not locked
TeamInvite membermembers:invite
KeysCreate keykeys:create
RolesCreate roleroles:create
WebhooksCreate webhookwebhooks:create

Everywhere else, including the workspace, product, project and environment settings pages, the versions page and the billing pages, c is not bound and does nothing. Roles is the one settings page that binds it.

Two surfaces have an extra condition. On a product, c opens the plan limit modal instead of the create modal when the workspace is already at its project cap, and it does nothing at all until a project list has landed in the cache, because that cached list is what the cap is counted from. On an environment, c is armed only on the table view: adding /raw to the URL for the raw editor disarms it, and it stays inert until the project and environment ids have loaded.

When c and p are ignored

Both shortcuts share the same guards, and both are silent rather than loud when a guard trips:

  • Focus is in an input, textarea, select or any contenteditable element, or inside an element with the switch, checkbox, radio, textbox, searchbox or spinbutton role.
  • Focus is inside an open popup: a listbox, a menu, a menu item, an expanded combobox, or any element advertising an expanded popup of its own.
  • Any overlay is open anywhere on the page: a dialog, a sheet, a drawer, the command palette, an open select, or an open dropdown menu. So c will not stack a second modal on the one you already have.
  • Meta, control or alt is held.
  • More than one handler is registered at once. The shortcut is registered globally and is only fired when exactly one page has claimed it, so if two ever overlap, neither runs. In development that also logs an error naming the count.

The publish shortcut

p opens the Publish version modal, and it is bound on the environment table view and nowhere else in the app. It needs more than the create shortcut does: both variables:create and versions:create, an unlocked project, the table view rather than /raw, loaded project and environment ids, and the environment's variables query already settled. Until all of those hold, the key does nothing. The Publish button in the page header draws its P hint the whole time the button exists, so the hint on its own proves nothing. The button is disabled in exactly the states that keep the shortcut dead, so an enabled Publish button is the reliable tell.

Switching with ⌘ 1 to ⌘ 9

The sidebar renders exactly one switcher at a time, chosen by how deep the current URL is, and the number keys are bound by whichever switcher that is. The same keystroke therefore means four different things depending on where you are:

Current URLSwitcher shown⌘ 1 to ⌘ 9
Anything not under a product (Products, Team, Keys, Webhooks, Billing, Settings)WorkspaceSwitches the active workspace in place, without navigating
/products/<product-slug> and its settingsProductOpens the nth product
/products/<product-slug>/projects/<slug> and its settingsProjectOpens the nth project in the whole workspace, not just the current product
An environment URLEnvironmentOpens the nth environment of the current project

The numbering always matches the order shown in that switcher's dropdown, because both read the same cached list. Products, projects and environments are listed alphabetically by name. Workspaces come back in whatever order the API returns them, so read the numbers off the menu rather than assuming.

Only the first nine entries are addressable, and only those nine are drawn with a number in the menu. A tenth product or environment has no shortcut and has to be clicked. Pressing a number with nothing at that position does nothing at all.

These four handlers have none of the guards that c and p have. They fire while you are typing in a text field and while a modal, a sheet or the command palette is open, so ⌘ 3 in the middle of editing a variable name will navigate away from the page. Envless does call preventDefault on the event, but a browser that reserves ⌘ 1 to ⌘ 9 for its own tab switching may claim the combination first; the switcher menu is always there as the fallback.

Switching a workspace is the one case that does not navigate. It swaps the selected workspace in place and resets the workspace scoped state. If the request fails, the previous workspace is restored and an error toast explains why. Note that the workspace key is per workspace, so after switching you may need to unlock again at Settings, Encryption. See Choosing a passphrase.