diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be2816ca6c..d196135de7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,6 +42,7 @@ This focus helps guide our project decisions as a community and what we choose t - [Naming conventions](#naming-conventions) - [Vue components](#vue-components) - [Internal linking](#internal-linking) + - [Command palette](#command-palette) - [Cursor and navigation](#cursor-and-navigation) - [RTL Support](#rtl-support) - [Localization (i18n)](#localization-i18n) @@ -400,6 +401,14 @@ For package links, use the auto-imported `packageRoute()` utility from `app/util > [!IMPORTANT] > Never construct package URLs as strings. The route structure uses separate `org` and `name` params, and `packageRoute()` handles the splitting correctly. +### Command palette + +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. + +- Add global entries in the command palette composables. +- Add page- or component-scoped entries from the page/component that owns the capability. +- Prefer palette entries for actions users may reasonably want to trigger from the keyboard or jump to directly. + #### Available route names | Route name | URL pattern | Parameters | diff --git a/app/app.vue b/app/app.vue index 5916e3e328..9a90d2b0b9 100644 --- a/app/app.vue +++ b/app/app.vue @@ -144,6 +144,8 @@ if (import.meta.client) { + + diff --git a/app/components/AppFooter.vue b/app/components/AppFooter.vue index 6955352350..75e725d2fa 100644 --- a/app/components/AppFooter.vue +++ b/app/components/AppFooter.vue @@ -5,6 +5,7 @@ const route = useRoute() const isHome = computed(() => route.name === 'index') const discord = useDiscordLink() +const { commandPaletteShortcutLabel } = usePlatformModifierKey() const modalRef = useTemplateRef('modalRef') const showModal = () => modalRef.value?.showModal?.() const closeModal = () => modalRef.value?.close?.() @@ -52,10 +53,19 @@ const closeModal = () => modalRef.value?.close?.() :modalTitle="$t('footer.keyboard_shortcuts')" class="w-auto max-w-lg" > +

+ {{ + $t('shortcuts.command_palette_description', { ctrlKey: $t('shortcuts.ctrl_key') }) + }} +

{{ $t('shortcuts.section.global') }}

    +
  • + {{ commandPaletteShortcutLabel }} + {{ $t('shortcuts.command_palette') }} +
  • / {{ $t('shortcuts.focus_search') }} diff --git a/app/components/AppHeader.vue b/app/components/AppHeader.vue index 8f0cb2290c..fc3f29631d 100644 --- a/app/components/AppHeader.vue +++ b/app/components/AppHeader.vue @@ -6,6 +6,8 @@ import { NPMX_DOCS_SITE } from '#shared/utils/constants' const keyboardShortcuts = useKeyboardShortcuts() const discord = useDiscordLink() +const { open: openCommandPalette } = useCommandPalette() +const { commandPaletteShortcutLabel } = usePlatformModifierKey() withDefaults( defineProps<{ @@ -258,6 +260,24 @@ onKeyStroke(