Skip to content

Commit c7821af

Browse files
committed
Merge branch 'main' into module-replacements-v3
2 parents 81e5c01 + de26d13 commit c7821af

43 files changed

Lines changed: 4488 additions & 3824 deletions

Some content is hidden

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

.env.example

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#secure password, can use openssl rand -hex 32
1+
# secure password, can use `openssl rand -hex 32`
22
NUXT_SESSION_PASSWORD=""
33

4-
#HMAC secret for image-proxy and OG image URL signing, can use openssl rand -hex 32
5-
NUXT_IMAGE_PROXY_SECRET=""
4+
# HMAC secret for image-proxy and OG image URL signing, can use `openssl rand -hex 32`
5+
NUXT_IMAGE_PROXY_SECRET=""

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ jobs:
114114
steps:
115115
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
116116

117+
- name: 👑 Fix Git ownership
118+
run: git config --global --add safe.directory /__w/npmx.dev/npmx.dev
119+
117120
- uses: voidzero-dev/setup-vp@8ecb39174989ce55af90f45cf55b02738599831d # v1
118121
with:
119122
node-version: lts/*

.github/workflows/dependency-diff.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ jobs:
3535
with:
3636
mode: artifact
3737
detect-replacements: 'true'
38-
duplicate-threshold: '4'
38+
# Too noisy. Disabling until this can report on duplicate CHANGES in this PR.
39+
duplicate-threshold: '999'
3940
dependency-threshold: '15'
4041
size-threshold: '200000'
4142

.nuxtrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
setups.@nuxt/test-utils="4.0.0"
1+
setups.@nuxt/test-utils="4.0.2"

.storybook/main.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { resolve } from 'node:path'
55
const config = {
66
stories: [
77
// List welcome first in sidebar
8-
'../.storybook/docs/welcome.mdx',
9-
'../.storybook/docs/*.mdx',
8+
'../app/storybook/welcome.mdx',
109
'../app/**/*.@(mdx|stories.@(js|ts))',
1110
],
1211
addons: [

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ Ideally, extract utilities into separate files so they can be unit tested. 🙏
348348

349349
### Internal linking
350350

351-
Always use **object syntax with named routes** for internal navigation. This makes links resilient to URL structure changes and provides type safety via `unplugin-vue-router`.
351+
Always use **object syntax with named routes** for internal navigation. This makes links resilient to URL structure changes and provides type safety with the [typedPages Nuxt option](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#typedpages).
352352

353353
```vue
354354
<!-- Good: named route -->

app/components/Brand/Customize.vue

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ async function downloadCustomPng() {
7575
if (!svg) return
7676
pngLoading.value = true
7777
78-
const blob = new Blob([svg], { type: 'image/svg+xml' })
79-
const url = URL.createObjectURL(blob)
78+
const url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
8079
8180
try {
8281
await document.fonts.ready
@@ -108,7 +107,6 @@ async function downloadCustomPng() {
108107
}, 'image/png')
109108
})
110109
} finally {
111-
URL.revokeObjectURL(url)
112110
pngLoading.value = false
113111
}
114112
}
@@ -145,24 +143,30 @@ async function downloadCustomPng() {
145143
<span class="text-sm font-mono text-fg-muted shrink-0">{{
146144
$t('brand.customize.accent_label')
147145
}}</span>
148-
<div class="flex items-center gap-1.5" role="radiogroup">
149-
<ButtonBase
146+
<div class="flex items-center gap-1.5">
147+
<label
150148
v-for="color in pickerColors"
151149
:key="color.id"
152-
role="radio"
153-
:aria-checked="activeAccentId === color.id"
154-
:aria-label="color.label"
155-
class="!w-6 !h-6 !rounded-full !border-2 !p-0 !min-w-0 transition-all duration-150 motion-reduce:transition-none"
150+
class="relative w-6 h-6 rounded-full border-2 cursor-pointer duration-150 motion-reduce:transition-none focus-within:ring-2 focus-within:ring-fg focus-within:ring-offset-2 focus-within:ring-offset-bg"
156151
:class="
157152
activeAccentId === color.id
158-
? '!border-fg scale-110'
153+
? 'border-fg scale-110'
159154
: color.id === 'neutral'
160-
? '!border-border hover:!border-border-hover'
161-
: '!border-transparent hover:!border-border-hover'
155+
? 'border-border hover:border-border-hover'
156+
: 'border-transparent hover:border-border-hover'
162157
"
163158
:style="{ backgroundColor: color.value }"
164-
@click="customAccent = color.id"
165-
/>
159+
>
160+
<input
161+
type="radio"
162+
name="brand-customize-accent"
163+
:value="color.id"
164+
:checked="activeAccentId === color.id"
165+
:aria-label="color.label"
166+
class="sr-only"
167+
@change="customAccent = color.id"
168+
/>
169+
</label>
166170
</div>
167171
</fieldset>
168172

@@ -172,40 +176,33 @@ async function downloadCustomPng() {
172176
<span class="text-sm font-mono text-fg-muted">{{
173177
$t('brand.customize.bg_label')
174178
}}</span>
175-
<div
176-
class="flex items-center border border-border rounded-md overflow-hidden"
177-
role="radiogroup"
178-
>
179-
<ButtonBase
180-
size="md"
181-
role="radio"
182-
:aria-checked="customBgDark"
183-
:aria-label="$t('brand.logos.on_dark')"
184-
class="!border-none !rounded-none motion-reduce:transition-none"
185-
:class="
186-
customBgDark
187-
? 'bg-bg-muted text-fg'
188-
: 'bg-transparent text-fg-muted hover:text-fg'
189-
"
190-
@click="customBgDark = true"
179+
<div class="flex items-center border border-border rounded-md overflow-hidden">
180+
<label
181+
class="px-3 py-1.5 text-sm font-mono cursor-pointer motion-reduce:transition-none focus-within:bg-fg/10"
182+
:class="customBgDark ? 'bg-bg-muted text-fg' : 'text-fg-muted hover:text-fg'"
191183
>
184+
<input
185+
v-model="customBgDark"
186+
type="radio"
187+
name="brand-customize-bg"
188+
:value="true"
189+
class="sr-only"
190+
/>
192191
{{ $t('brand.logos.on_dark') }}
193-
</ButtonBase>
194-
<ButtonBase
195-
size="md"
196-
role="radio"
197-
:aria-checked="!customBgDark"
198-
:aria-label="$t('brand.logos.on_light')"
199-
class="!border-none !rounded-none border-is border-is-border motion-reduce:transition-none"
200-
:class="
201-
!customBgDark
202-
? 'bg-bg-muted text-fg'
203-
: 'bg-transparent text-fg-muted hover:text-fg'
204-
"
205-
@click="customBgDark = false"
192+
</label>
193+
<label
194+
class="px-3 py-1.5 text-sm font-mono cursor-pointer border-is border-is-border motion-reduce:transition-none focus-within:bg-fg/10"
195+
:class="!customBgDark ? 'bg-bg-muted text-fg' : 'text-fg-muted hover:text-fg'"
206196
>
197+
<input
198+
v-model="customBgDark"
199+
type="radio"
200+
name="brand-customize-bg"
201+
:value="false"
202+
class="sr-only"
203+
/>
207204
{{ $t('brand.logos.on_light') }}
208-
</ButtonBase>
205+
</label>
209206
</div>
210207
</div>
211208

app/components/Readme.vue

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,20 @@ function handleClick(event: MouseEvent) {
487487
summary {
488488
font-size: 1rem;
489489
color: var(--fg-muted);
490+
491+
/* Markdown often wraps headings/paragraphs inside <summary>, which
492+
forces them onto new lines. Inline them so the disclosure marker
493+
sits next to the label while preserving heading styles. */
494+
> h1,
495+
> h2,
496+
> h3,
497+
> h4,
498+
> h5,
499+
> h6,
500+
> p {
501+
display: inline;
502+
margin: 0;
503+
}
490504
}
491505
}
492506
</style>

app/composables/npm/useAlgoliaSearch.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ interface AlgoliaHit {
5050
deprecated: boolean | string
5151
isDeprecated: boolean
5252
license: string | null
53+
isSecurityHeld: boolean
5354
}
5455

5556
const ATTRIBUTES_TO_RETRIEVE = [
@@ -67,6 +68,7 @@ const ATTRIBUTES_TO_RETRIEVE = [
6768
'deprecated',
6869
'isDeprecated',
6970
'license',
71+
'isSecurityHeld',
7072
]
7173

7274
const EXISTENCE_CHECK_ATTRS = ['name']
@@ -90,6 +92,7 @@ function hitToSearchResult(hit: AlgoliaHit): NpmSearchResult {
9092
email: owner.email,
9193
}))
9294
: [],
95+
isSecurityHeld: hit.isSecurityHeld,
9396
},
9497
searchScore: 0,
9598
downloads: {

app/composables/npm/useSearch.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ export function useSearch(
261261
const doSearch = provider === 'algolia' ? searchAlgolia : searchNpm
262262
const response = await doSearch(q, { size, from })
263263

264+
const beforeCount = cache.value?.objects.length ?? 0
265+
264266
if (cache.value && cache.value.query === q && cache.value.provider === provider) {
265267
const existingNames = new Set(cache.value.objects.map(obj => obj.package.name))
266268
const newObjects = response.objects.filter(obj => !existingNames.has(obj.package.name))
@@ -279,6 +281,12 @@ export function useSearch(
279281
}
280282
}
281283

284+
// Bail if the provider gave us no new unique items
285+
// Without something like this the recursion below never terminates.
286+
if ((cache.value?.objects.length ?? 0) === beforeCount) {
287+
return
288+
}
289+
282290
if (
283291
cache.value &&
284292
cache.value.objects.length < targetSize &&

0 commit comments

Comments
 (0)