Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ if (import.meta.server) {
setJsonLd(createWebSiteSchema())
}

const keyboardShortcuts = useKeyboardShortcuts()

onKeyDown(
'/',
e => {
if (isEditableElement(e.target)) return
if (!keyboardShortcuts.value || isEditableElement(e.target)) return
e.preventDefault()

const searchInput = document.querySelector<HTMLInputElement>(
Expand All @@ -70,7 +72,7 @@ onKeyDown(
onKeyDown(
'?',
e => {
if (isEditableElement(e.target)) return
if (!keyboardShortcuts.value || isEditableElement(e.target)) return
e.preventDefault()
showKbdHints.value = true
},
Expand All @@ -80,7 +82,7 @@ onKeyDown(
onKeyUp(
'?',
e => {
if (isEditableElement(e.target)) return
if (!keyboardShortcuts.value || isEditableElement(e.target)) return
e.preventDefault()
showKbdHints.value = false
},
Expand Down
16 changes: 15 additions & 1 deletion app/components/AppFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const isHome = computed(() => route.name === 'index')

const modalRef = useTemplateRef('modalRef')
const showModal = () => modalRef.value?.showModal?.()
const closeModal = () => modalRef.value?.close?.()
</script>

<template>
Expand Down Expand Up @@ -81,7 +82,7 @@ const showModal = () => modalRef.value?.showModal?.()
<p class="mb-2 font-mono text-fg-subtle">
{{ $t('shortcuts.section.package') }}
</p>
<ul class="mb-6 flex flex-col gap-2">
<ul class="mb-8 flex flex-col gap-2">
<li class="flex gap-2 items-center">
<kbd class="kbd">.</kbd>
<span>{{ $t('shortcuts.open_code_view') }}</span>
Expand All @@ -95,6 +96,19 @@ const showModal = () => modalRef.value?.showModal?.()
<span>{{ $t('shortcuts.compare_from_package') }}</span>
</li>
</ul>
<p class="text-fg-muted leading-relaxed">
<i18n-t keypath="shortcuts.disable_shortcuts" tag="span" scope="global">
<template #settings>
<NuxtLink
:to="{ name: 'settings' }"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Link should target the keyboard-shortcuts section directly.

Line 103 currently routes to the settings page root only. To meet the “direct to setting” objective, point this link to the keyboard shortcuts section anchor instead of the page top.

Suggested change
-                    :to="{ name: 'settings' }"
+                    :to="{ name: 'settings', hash: '#keyboard-shortcuts' }"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
:to="{ name: 'settings' }"
:to="{ name: 'settings', hash: '#keyboard-shortcuts' }"

class="hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
@click="closeModal"
>
{{ $t('settings.title') }}
</NuxtLink>
</template>
</i18n-t>
</p>
</Modal>
<LinkBase :to="NPMX_DOCS_SITE">
{{ $t('footer.docs') }}
Expand Down
4 changes: 3 additions & 1 deletion app/components/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type { NavigationConfig, NavigationConfigWithGroups } from '~/types'
import { isEditableElement } from '~/utils/input'
import { NPMX_DOCS_SITE } from '#shared/utils/constants'

const keyboardShortcuts = useKeyboardShortcuts()

withDefaults(
defineProps<{
showLogo?: boolean
Expand Down Expand Up @@ -175,7 +177,7 @@ function handleSearchFocus() {

onKeyStroke(
e => {
if (isEditableElement(e.target)) {
if (!keyboardShortcuts.value || isEditableElement(e.target)) {
return
}

Expand Down
20 changes: 12 additions & 8 deletions app/components/Button/Base.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const props = withDefaults(

const el = useTemplateRef('el')

const keyboardShortcutsEnabled = computed(() => import.meta.client && useKeyboardShortcuts().value)

defineExpose({
focus: () => el.value?.focus(),
getBoundingClientRect: () => el.value?.getBoundingClientRect(),
Expand Down Expand Up @@ -56,16 +58,18 @@ defineExpose({
*/
disabled ? true : undefined
"
:aria-keyshortcuts="ariaKeyshortcuts"
:aria-keyshortcuts="keyboardShortcutsEnabled ? ariaKeyshortcuts : undefined"
>
<span v-if="classicon" class="size-[1em]" :class="classicon" aria-hidden="true" />
<slot />
<kbd
v-if="ariaKeyshortcuts"
class="ms-2 inline-flex items-center justify-center w-4 h-4 text-xs text-fg bg-bg-muted border border-border rounded no-underline"
aria-hidden="true"
>
{{ ariaKeyshortcuts }}
</kbd>
<ClientOnly>
<kbd
v-if="keyboardShortcutsEnabled && ariaKeyshortcuts"
class="ms-2 inline-flex items-center justify-center w-4 h-4 text-xs text-fg bg-bg-muted border border-border rounded no-underline"
aria-hidden="true"
>
{{ ariaKeyshortcuts }}
</kbd>
</ClientOnly>
</button>
</template>
4 changes: 4 additions & 0 deletions app/components/Compare/PackageSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ function removePackage(name: string) {
packages.value = packages.value.filter(p => p !== name)
}

const keyboardShortcutsEnabled = useKeyboardShortcuts()

function handleKeydown(e: KeyboardEvent) {
if (!keyboardShortcutsEnabled) return

const items = navigableItems.value
const count = items.length

Expand Down
20 changes: 12 additions & 8 deletions app/components/Link/Base.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ const isLink = computed(() => props.variant === 'link')
const isButton = computed(() => !isLink.value)
const isButtonSmall = computed(() => props.size === 'small' && !isLink.value)
const isButtonMedium = computed(() => props.size === 'medium' && !isLink.value)
const keyboardShortcuts = useKeyboardShortcuts()
const keyboardShortcutsEnabled = computed(() => import.meta.client && keyboardShortcuts.value)
</script>

<template>
Expand Down Expand Up @@ -97,7 +99,7 @@ const isButtonMedium = computed(() => props.size === 'medium' && !isLink.value)
variant === 'button-primary',
}"
:to="to"
:aria-keyshortcuts="ariaKeyshortcuts"
:aria-keyshortcuts="keyboardShortcutsEnabled ? ariaKeyshortcuts : undefined"
:target="isLinkExternal ? '_blank' : undefined"
>
<span v-if="classicon" class="size-[1em]" :class="classicon" aria-hidden="true" />
Expand All @@ -113,12 +115,14 @@ const isButtonMedium = computed(() => props.size === 'medium' && !isLink.value)
class="i-lucide:link size-[1em] opacity-0 group-hover/link:opacity-100 transition-opacity duration-200"
aria-hidden="true"
/>
<kbd
v-if="ariaKeyshortcuts"
class="ms-2 inline-flex items-center justify-center size-4 text-xs text-fg bg-bg-muted border border-border rounded no-underline"
aria-hidden="true"
>
{{ ariaKeyshortcuts }}
</kbd>
<ClientOnly>
<kbd
v-if="keyboardShortcutsEnabled && ariaKeyshortcuts"
class="ms-2 inline-flex items-center justify-center size-4 text-xs text-fg bg-bg-muted border border-border rounded no-underline"
aria-hidden="true"
>
{{ ariaKeyshortcuts }}
</kbd>
</ClientOnly>
</NuxtLink>
</template>
12 changes: 12 additions & 0 deletions app/composables/useSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export interface AppSettings {
selectedLocale: LocaleObject['code'] | null
/** Search provider for package search */
searchProvider: SearchProvider
/** Enable/disable keyboard shortcuts */
keyboardShortcuts: boolean
/** Connector preferences */
connector: {
/** Automatically open the web auth page in the browser */
Expand All @@ -53,6 +55,7 @@ const DEFAULT_SETTINGS: AppSettings = {
selectedLocale: null,
preferredBackgroundTheme: null,
searchProvider: import.meta.test ? 'npm' : 'algolia',
keyboardShortcuts: true,
connector: {
autoOpenURL: false,
},
Expand Down Expand Up @@ -97,6 +100,15 @@ export function useRelativeDates() {
return computed(() => settings.value.relativeDates)
}

/**
* Composable for accessing just the keyboard shortcuts setting.
* Useful for components that only need to read this specific setting.
*/
export function useKeyboardShortcuts() {
const { settings } = useSettings()
return computed(() => settings.value.keyboardShortcuts)
}

/**
* Composable for managing accent color.
*/
Expand Down
8 changes: 5 additions & 3 deletions app/pages/package/[[org]]/[name].vue
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,10 @@ const codeLink = computed((): RouteLocationRaw | null => {
}
})

const keyboardShortcuts = useKeyboardShortcuts()

onKeyStroke(
e => isKeyWithoutModifiers(e, '.') && !isEditableElement(e.target),
e => keyboardShortcuts.value && isKeyWithoutModifiers(e, '.') && !isEditableElement(e.target),
e => {
if (codeLink.value === null) return
e.preventDefault()
Expand All @@ -700,7 +702,7 @@ onKeyStroke(
)

onKeyStroke(
e => isKeyWithoutModifiers(e, 'd') && !isEditableElement(e.target),
e => keyboardShortcuts.value && isKeyWithoutModifiers(e, 'd') && !isEditableElement(e.target),
e => {
if (!docsLink.value) return
e.preventDefault()
Expand All @@ -710,7 +712,7 @@ onKeyStroke(
)

onKeyStroke(
e => isKeyWithoutModifiers(e, 'c') && !isEditableElement(e.target),
e => keyboardShortcuts.value && isKeyWithoutModifiers(e, 'c') && !isEditableElement(e.target),
e => {
if (!pkg.value) return
e.preventDefault()
Expand Down
15 changes: 15 additions & 0 deletions app/pages/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ const setLocale: typeof setNuxti18nLocale = locale => {
</div>
</section>

<!-- LANGUAGE Section -->
<section>
<h2 class="text-xs text-fg-muted uppercase tracking-wider mb-4">
{{ $t('settings.sections.language') }}
Expand Down Expand Up @@ -260,6 +261,20 @@ const setLocale: typeof setNuxti18nLocale = locale => {
</template>
</div>
</section>

<!-- KEYBOARD SHORTCUTS Section -->
<section>
<h2 class="text-xs text-fg-muted uppercase tracking-wider mb-4">
{{ $t('settings.sections.keyboard_shortcuts') }}
</h2>
<div class="bg-bg-subtle border border-border rounded-lg p-4 sm:p-6">
<SettingsToggle
:label="$t('settings.keyboard_shortcuts_enabled')"
:description="$t('settings.keyboard_shortcuts_enabled_description')"
v-model="settings.keyboardShortcuts"
/>
</div>
</section>
</div>
</article>
</main>
Expand Down
10 changes: 7 additions & 3 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"navigate_results": "Navigate results",
"go_to_result": "Go to result",
"open_code_view": "Open code view",
"open_docs": "Open docs"
"open_docs": "Open docs",
"disable_shortcuts": "You could disable keyboard shortcuts in {settings}."
Comment thread
danielroe marked this conversation as resolved.
Outdated
},
"search": {
"label": "Search npm packages",
Expand Down Expand Up @@ -84,7 +85,8 @@
"appearance": "Appearance",
"display": "Display",
"search": "Data source",
"language": "Language"
"language": "Language",
"keyboard_shortcuts": "Keyboard shortcuts"
},
"data_source": {
"label": "Data source",
Expand All @@ -108,7 +110,9 @@
"accent_colors": "Accent colors",
"clear_accent": "Clear accent color",
"translation_progress": "Translation progress",
"background_themes": "Background shade"
"background_themes": "Background shade",
"keyboard_shortcuts_enabled": "Enable keyboard shortcuts",
"keyboard_shortcuts_enabled_description": "Keyboard shortcuts can be disabled if they conflict with other browser or system shortcuts"
},
"i18n": {
"missing_keys": "{count} missing translation | {count} missing translations",
Expand Down
12 changes: 12 additions & 0 deletions i18n/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@
},
"open_docs": {
"type": "string"
},
"disable_shortcuts": {
"type": "string"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -258,6 +261,9 @@
},
"language": {
"type": "string"
},
"keyboard_shortcuts": {
"type": "string"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -330,6 +336,12 @@
},
"background_themes": {
"type": "string"
},
"keyboard_shortcuts_enabled": {
"type": "string"
},
"keyboard_shortcuts_enabled_description": {
"type": "string"
}
},
"additionalProperties": false
Expand Down
10 changes: 7 additions & 3 deletions lunaria/files/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"navigate_results": "Navigate results",
"go_to_result": "Go to result",
"open_code_view": "Open code view",
"open_docs": "Open docs"
"open_docs": "Open docs",
"disable_shortcuts": "You could disable keyboard shortcuts in {settings}."
},
"search": {
"label": "Search npm packages",
Expand Down Expand Up @@ -83,7 +84,8 @@
"appearance": "Appearance",
"display": "Display",
"search": "Data source",
"language": "Language"
"language": "Language",
"keyboard_shortcuts": "Keyboard shortcuts"
},
"data_source": {
"label": "Data source",
Expand All @@ -107,7 +109,9 @@
"accent_colors": "Accent colors",
"clear_accent": "Clear accent colour",
"translation_progress": "Translation progress",
"background_themes": "Background shade"
"background_themes": "Background shade",
"keyboard_shortcuts_enabled": "Enable keyboard shortcuts",
"keyboard_shortcuts_enabled_description": "Keyboard shortcuts can be disabled if they conflict with other browser or system shortcuts"
},
"i18n": {
"missing_keys": "{count} missing translation | {count} missing translations",
Expand Down
10 changes: 7 additions & 3 deletions lunaria/files/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"navigate_results": "Navigate results",
"go_to_result": "Go to result",
"open_code_view": "Open code view",
"open_docs": "Open docs"
"open_docs": "Open docs",
"disable_shortcuts": "You could disable keyboard shortcuts in {settings}."
},
"search": {
"label": "Search npm packages",
Expand Down Expand Up @@ -83,7 +84,8 @@
"appearance": "Appearance",
"display": "Display",
"search": "Data source",
"language": "Language"
"language": "Language",
"keyboard_shortcuts": "Keyboard shortcuts"
},
"data_source": {
"label": "Data source",
Expand All @@ -107,7 +109,9 @@
"accent_colors": "Accent colors",
"clear_accent": "Clear accent color",
"translation_progress": "Translation progress",
"background_themes": "Background shade"
"background_themes": "Background shade",
"keyboard_shortcuts_enabled": "Enable keyboard shortcuts",
"keyboard_shortcuts_enabled_description": "Keyboard shortcuts can be disabled if they conflict with other browser or system shortcuts"
},
"i18n": {
"missing_keys": "{count} missing translation | {count} missing translations",
Expand Down
Loading
Loading