Skip to content

Commit 17bb481

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/skills-modal-tabs
# Conflicts: # shared/types/index.ts
2 parents 805b335 + d9172a9 commit 17bb481

172 files changed

Lines changed: 7074 additions & 912 deletions

File tree

Some content is hidden

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

.gitattributes

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
* text eol=lf
1+
* text=auto eol=lf

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ on:
1111
branches:
1212
- main
1313

14+
# cancel in-progress runs on new commits to same PR (gitub.event.number)
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
17+
cancel-in-progress: true
18+
1419
permissions:
1520
contents: read
1621

CONTRIBUTING.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ The connector will check your npm authentication, generate a connection token, a
105105

106106
## Code style
107107

108+
When committing changes, try to keep an eye out for unintended formatting updates. These can make a pull request look noisier than it really is and slow down the review process. Sometimes IDEs automatically reformat files on save, which can unintentionally introduce extra changes.
109+
110+
To help with this, the project uses `oxfmt` to handle formatting via a pre-commit hook. The hook will automatically reformat files when needed. If something can’t be fixed automatically, it will let you know what needs to be updated before you can commit.
111+
112+
If you want to get ahead of any formatting issues, you can also run `pnpm lint:fix` before committing to fix formatting across the whole project.
113+
108114
### Typescript
109115

110116
- We care about good types – never cast things to `any` 💪
@@ -301,6 +307,14 @@ For example to check if all Japanese translation keys are up-to-date, run:
301307
pnpm i18n:check ja-JP
302308
```
303309

310+
To automatically add missing keys with English placeholders, use `--fix`:
311+
312+
```bash
313+
pnpm i18n:check:fix fr-FR
314+
```
315+
316+
This will add missing keys with `"EN TEXT TO REPLACE: {english text}"` as placeholder values, making it easier to see what needs translation.
317+
304318
#### Country variants (advanced)
305319

306320
Most languages only need a single locale file. Country variants are only needed when you want to support regional differences (e.g., `es-ES` for Spain vs `es-419` for Latin America).
@@ -361,13 +375,17 @@ We recommend the [i18n-ally](https://marketplace.visualstudio.com/items?itemName
361375

362376
The extension is included in our workspace recommendations, so VSCode should prompt you to install it.
363377

364-
### Formatting with locale
378+
### Formatting numbers and dates
365379

366-
When formatting numbers or dates that should respect the user's locale, pass the locale:
380+
Use vue-i18n's built-in formatters for locale-aware formatting:
367381

368-
```typescript
369-
const { locale } = useI18n()
370-
const formatted = formatNumber(12345, locale.value) // "12,345" in en-US
382+
```vue
383+
<template>
384+
<p>{{ $n(12345) }}</p>
385+
<!-- "12,345" in en-US, "12 345" in fr-FR -->
386+
<p>{{ $d(new Date()) }}</p>
387+
<!-- locale-aware date -->
388+
</template>
371389
```
372390

373391
## Testing

app/app.vue

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const { locale, locales } = useI18n()
1010
initPreferencesOnPrehydrate()
1111
1212
const isHomepage = computed(() => route.name === 'index')
13+
const showKbdHints = shallowRef(false)
1314
1415
const localeMap = locales.value.reduce(
1516
(acc, l) => {
@@ -21,8 +22,9 @@ const localeMap = locales.value.reduce(
2122
2223
useHead({
2324
htmlAttrs: {
24-
lang: () => locale.value,
25-
dir: () => localeMap[locale.value] ?? 'ltr',
25+
'lang': () => locale.value,
26+
'dir': () => localeMap[locale.value] ?? 'ltr',
27+
'data-kbd-hints': () => showKbdHints.value,
2628
},
2729
titleTemplate: titleChunk => {
2830
return titleChunk ? titleChunk : 'npmx - Better npm Package Browser'
@@ -33,16 +35,16 @@ if (import.meta.server) {
3335
setJsonLd(createWebSiteSchema())
3436
}
3537
36-
// Global keyboard shortcut: "/" focuses search or navigates to search page
38+
// Global keyboard shortcut:
39+
// "/" focuses search or navigates to search page
40+
// "?" highlights all keyboard shortcut elements
3741
function handleGlobalKeydown(e: KeyboardEvent) {
3842
const target = e.target as HTMLElement
3943
4044
const isEditableTarget =
4145
target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable
4246
43-
if (isEditableTarget) {
44-
return
45-
}
47+
if (isEditableTarget) return
4648
4749
if (e.key === '/') {
4850
e.preventDefault()
@@ -59,15 +61,26 @@ function handleGlobalKeydown(e: KeyboardEvent) {
5961
6062
router.push('/search')
6163
}
64+
65+
if (e.key === '?') {
66+
e.preventDefault()
67+
showKbdHints.value = true
68+
}
69+
}
70+
71+
function handleGlobalKeyup() {
72+
showKbdHints.value = false
6273
}
6374
6475
if (import.meta.client) {
6576
useEventListener(document, 'keydown', handleGlobalKeydown)
77+
useEventListener(document, 'keyup', handleGlobalKeyup)
6678
}
6779
</script>
6880

6981
<template>
7082
<div class="min-h-screen flex flex-col bg-bg text-fg">
83+
<NuxtPwaAssets />
7184
<a href="#main-content" class="skip-link font-mono">{{ $t('common.skip_link') }}</a>
7285

7386
<AppHeader :show-logo="!isHomepage" />
@@ -81,3 +94,25 @@ if (import.meta.client) {
8194
<ScrollToTop />
8295
</div>
8396
</template>
97+
98+
<style>
99+
/* Keyboard shortcut highlight on "?" key press */
100+
kbd {
101+
position: relative;
102+
}
103+
104+
kbd::before {
105+
content: '';
106+
position: absolute;
107+
inset: 0;
108+
border-radius: inherit;
109+
box-shadow: 0 0 4px 2px var(--accent);
110+
opacity: 0;
111+
transition: opacity 200ms ease-out;
112+
pointer-events: none;
113+
}
114+
115+
html[data-kbd-hints='true'] kbd::before {
116+
opacity: 1;
117+
}
118+
</style>

app/assets/main.css

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ html.light .shiki span {
463463
border-collapse: collapse;
464464
margin: 1.5rem 0;
465465
font-size: 0.875rem;
466+
word-break: keep-all;
466467
}
467468

468469
.readme-content th,
@@ -541,12 +542,6 @@ input[type='search']::-webkit-search-results-decoration {
541542
appearance: none;
542543
}
543544

544-
/* View transition for logo (hero -> header) */
545-
.hero-logo,
546-
.header-logo {
547-
view-transition-name: site-logo;
548-
}
549-
550545
/* Disable the default fade transition on page navigation */
551546
::view-transition-old(root),
552547
::view-transition-new(root) {
@@ -555,9 +550,7 @@ input[type='search']::-webkit-search-results-decoration {
555550

556551
/* Customize the view transition animations for specific elements */
557552
::view-transition-old(search-box),
558-
::view-transition-new(search-box),
559-
::view-transition-old(site-logo),
560-
::view-transition-new(site-logo) {
553+
::view-transition-new(search-box) {
561554
animation-duration: 0.3s;
562555
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
563556
}

app/components/AnnounceTooltip.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script setup lang="ts">
2+
const props = defineProps<{
3+
/** Tooltip text */
4+
text: string
5+
/** Position: 'top' | 'bottom' | 'left' | 'right' */
6+
position?: 'top' | 'bottom' | 'left' | 'right'
7+
/** is tooltip visible */
8+
isVisible: boolean
9+
}>()
10+
</script>
11+
12+
<template>
13+
<BaseTooltip :text :isVisible :position :tooltip-attr="{ 'aria-live': 'polite' }"
14+
><slot
15+
/></BaseTooltip>
16+
</template>

app/components/AppFooter.vue

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
1+
<script setup lang="ts">
2+
const route = useRoute()
3+
const isHome = computed(() => route.name === 'index')
4+
</script>
5+
16
<template>
27
<footer class="border-t border-border mt-auto">
38
<div class="container py-3 sm:py-8 flex flex-col gap-2 sm:gap-4 text-fg-subtle text-sm">
49
<div
510
class="flex flex-col sm:flex-row items-center sm:items-baseline justify-between gap-2 sm:gap-4"
611
>
7-
<p class="font-mono text-balance m-0 hidden sm:block">{{ $t('tagline') }}</p>
8-
<div class="flex items-center gap-3 sm:gap-6">
9-
<NuxtLink
10-
to="/about"
11-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center"
12-
>
12+
<div>
13+
<p class="font-mono text-balance m-0 hidden sm:block">{{ $t('tagline') }}</p>
14+
<BuildEnvironment v-if="!isHome" footer />
15+
</div>
16+
<!-- Desktop: Show all links. Mobile: Links are in MobileMenu -->
17+
<div class="hidden sm:flex items-center gap-6">
18+
<NuxtLink to="/about" class="link-subtle font-mono text-xs min-h-11 flex items-center">
1319
{{ $t('footer.about') }}
1420
</NuxtLink>
1521
<a
1622
href="https://docs.npmx.dev"
1723
target="_blank"
1824
rel="noopener noreferrer"
19-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center gap-1"
25+
class="link-subtle font-mono text-xs min-h-11 flex items-center gap-1"
2026
>
2127
{{ $t('footer.docs') }}
2228
<span class="i-carbon:launch rtl-flip w-3 h-3" aria-hidden="true" />
@@ -25,7 +31,7 @@
2531
href="https://repo.npmx.dev"
2632
target="_blank"
2733
rel="noopener noreferrer"
28-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center gap-1"
34+
class="link-subtle font-mono text-xs min-h-11 flex items-center gap-1"
2935
>
3036
{{ $t('footer.source') }}
3137
<span class="i-carbon:launch rtl-flip w-3 h-3" aria-hidden="true" />
@@ -34,7 +40,7 @@
3440
href="https://social.npmx.dev"
3541
target="_blank"
3642
rel="noopener noreferrer"
37-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center gap-1"
43+
class="link-subtle font-mono text-xs min-h-11 flex items-center gap-1"
3844
>
3945
{{ $t('footer.social') }}
4046
<span class="i-carbon:launch rtl-flip w-3 h-3" aria-hidden="true" />
@@ -43,7 +49,7 @@
4349
href="https://chat.npmx.dev"
4450
target="_blank"
4551
rel="noopener noreferrer"
46-
class="link-subtle font-mono text-xs min-h-8 sm:min-h-11 flex items-center gap-1"
52+
class="link-subtle font-mono text-xs min-h-11 flex items-center gap-1"
4753
>
4854
{{ $t('footer.chat') }}
4955
<span class="i-carbon:launch rtl-flip w-3 h-3" aria-hidden="true" />

0 commit comments

Comments
 (0)