Skip to content

Commit 9bd0c4d

Browse files
serhalpghostdevvautofix-ci[bot]
authored
feat(ui): add ⌘+K command palette for quick nav and actions (#2159)
Co-authored-by: Willow (GHOST) <git@willow.sh> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 38820a3 commit 9bd0c4d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4884
-92
lines changed

CONTRIBUTING.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This focus helps guide our project decisions as a community and what we choose t
4242
- [Naming conventions](#naming-conventions)
4343
- [Vue components](#vue-components)
4444
- [Internal linking](#internal-linking)
45+
- [Command palette](#command-palette)
4546
- [Cursor and navigation](#cursor-and-navigation)
4647
- [RTL Support](#rtl-support)
4748
- [Localization (i18n)](#localization-i18n)
@@ -400,6 +401,14 @@ For package links, use the auto-imported `packageRoute()` utility from `app/util
400401
> [!IMPORTANT]
401402
> Never construct package URLs as strings. The route structure uses separate `org` and `name` params, and `packageRoute()` handles the splitting correctly.
402403
404+
### Command palette
405+
406+
The command palette is a first-class navigation surface. When you add a new user-facing capability to the app, you should also register a corresponding command palette entry for it whenever that action or destination makes sense outside its immediate UI.
407+
408+
- Add global entries in the command palette composables.
409+
- Add page- or component-scoped entries from the page/component that owns the capability.
410+
- Prefer palette entries for actions users may reasonably want to trigger from the keyboard or jump to directly.
411+
403412
#### Available route names
404413

405414
| Route name | URL pattern | Parameters |

app/app.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ if (import.meta.client) {
146146
<NuxtPage />
147147
</div>
148148

149+
<CommandPalette />
150+
149151
<AppFooter />
150152

151153
<ScrollToTop />

app/components/AppFooter.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const route = useRoute()
55
const isHome = computed(() => route.name === 'index')
66
77
const discord = useDiscordLink()
8+
const { commandPaletteShortcutLabel } = usePlatformModifierKey()
89
const modalRef = useTemplateRef('modalRef')
910
const showModal = () => modalRef.value?.showModal?.()
1011
const closeModal = () => modalRef.value?.close?.()
@@ -51,14 +52,24 @@ const closeModal = () => modalRef.value?.close?.()
5152
</button>
5253

5354
<Modal
55+
id="keyboard-shortcuts-modal"
5456
ref="modalRef"
5557
:modalTitle="$t('footer.keyboard_shortcuts')"
5658
class="w-auto max-w-lg"
5759
>
60+
<p class="mb-4 text-sm leading-relaxed text-fg-muted">
61+
{{
62+
$t('shortcuts.command_palette_description', { ctrlKey: $t('shortcuts.ctrl_key') })
63+
}}
64+
</p>
5865
<p class="mb-2 font-mono text-fg-subtle">
5966
{{ $t('shortcuts.section.global') }}
6067
</p>
6168
<ul class="mb-6 flex flex-col gap-2">
69+
<li class="flex gap-2 items-center">
70+
<kbd class="kbd">{{ commandPaletteShortcutLabel }}</kbd>
71+
<span>{{ $t('shortcuts.command_palette') }}</span>
72+
</li>
6273
<li class="flex gap-2 items-center">
6374
<kbd class="kbd">/</kbd>
6475
<span>{{ $t('shortcuts.focus_search') }}</span>

app/components/AppHeader.vue

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { NavigationConfig, NavigationConfigWithGroups } from '~/types'
44
import { NPMX_DOCS_SITE } from '#shared/utils/constants'
55
66
const discord = useDiscordLink()
7+
const { open: openCommandPalette } = useCommandPalette()
8+
const { commandPaletteShortcutLabel } = usePlatformModifierKey()
79
810
withDefaults(
911
defineProps<{
@@ -253,6 +255,24 @@ useShortcuts({
253255
<!-- Spacer when logo is hidden on desktop -->
254256
<span v-else class="hidden sm:block w-1" />
255257

258+
<ButtonBase
259+
type="button"
260+
variant="secondary"
261+
class="hidden lg:inline-flex shrink-0 gap-2 px-2.5 me-3"
262+
:aria-label="$t('shortcuts.command_palette')"
263+
:title="$t('shortcuts.command_palette_description', { ctrlKey: $t('shortcuts.ctrl_key') })"
264+
@click="openCommandPalette"
265+
>
266+
<span>{{ $t('command_palette.quick_actions') }}</span>
267+
<span class="inline-flex items-center gap-1 text-xs text-fg-subtle">
268+
<kbd
269+
class="inline-flex items-center justify-center rounded border border-border bg-bg-muted px-1.5 py-0.5 font-mono text-[0.7rem] text-fg-muted"
270+
>
271+
{{ commandPaletteShortcutLabel }}
272+
</kbd>
273+
</span>
274+
</ButtonBase>
275+
256276
<!-- Center: Search bar + nav items -->
257277
<div
258278
class="flex-1 flex items-center md:gap-6"
@@ -288,7 +308,7 @@ useShortcuts({
288308
</div>
289309

290310
<!-- End: Desktop nav items + Mobile menu button -->
291-
<div class="hidden sm:flex flex-shrink-0">
311+
<div class="hidden sm:flex flex-shrink-0 items-center gap-2">
292312
<!-- Desktop: Explore link -->
293313
<LinkBase
294314
v-for="link in desktopLinks"

0 commit comments

Comments
 (0)