Skip to content

Commit 96732fa

Browse files
authored
Merge branch 'main' into patch-2
2 parents c0aab9a + 306d537 commit 96732fa

File tree

173 files changed

+6453
-1725
lines changed

Some content is hidden

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

173 files changed

+6453
-1725
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
#secure password, can use openssl rand --hex 32
1+
#secure password, can use openssl rand -hex 32
22
NUXT_SESSION_PASSWORD=""

.github/workflows/provenance.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ permissions:
1414
contents: read
1515
jobs:
1616
check-provenance:
17-
runs-on: ubuntu-latest
17+
runs-on: ubuntu-slim
1818
steps:
1919
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
2020
with:

.github/workflows/semantic-pull-requests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
pull-requests: read # for amannn/action-semantic-pull-request to analyze PRs
1717
statuses: write # for amannn/action-semantic-pull-request to mark status of analyzed PR
1818
if: github.repository == 'npmx-dev/npmx.dev'
19-
runs-on: ubuntu-latest
19+
runs-on: ubuntu-slim
2020
name: semantic-pr
2121
steps:
2222
- name: Validate PR title

.lighthouserc.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module.exports = {
2929
url: [
3030
'http://localhost:3000/',
3131
'http://localhost:3000/search?q=nuxt',
32-
'http://localhost:3000/nuxt',
32+
'http://localhost:3000/package/nuxt',
3333
],
3434
numberOfRuns: 1,
3535
chromePath: findChrome(),

CONTRIBUTING.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,43 @@ We want to create 'a fast, modern browser for the npm registry.' This means, amo
1414
- Layout shift, flakiness, slowness is The Worst. We need to continually iterate to create the most performant, best DX possible.
1515
- We want to provide information in the best way. We don't want noise, cluttered display, or confusing UI. If in doubt: choose simplicity.
1616

17+
## Table of Contents
18+
19+
- [Getting started](#getting-started)
20+
- [Prerequisites](#prerequisites)
21+
- [Setup](#setup)
22+
- [Development workflow](#development-workflow)
23+
- [Available commands](#available-commands)
24+
- [Project structure](#project-structure)
25+
- [Local connector CLI](#local-connector-cli)
26+
- [Code style](#code-style)
27+
- [TypeScript](#typescript)
28+
- [Server API patterns](#server-api-patterns)
29+
- [Import order](#import-order)
30+
- [Naming conventions](#naming-conventions)
31+
- [Vue components](#vue-components)
32+
- [RTL Support](#rtl-support)
33+
- [Localization (i18n)](#localization-i18n)
34+
- [Approach](#approach)
35+
- [Adding a new locale](#adding-a-new-locale)
36+
- [Update translation](#update-translation)
37+
- [Adding translations](#adding-translations)
38+
- [Translation key conventions](#translation-key-conventions)
39+
- [Using i18n-ally (recommended)](#using-i18n-ally-recommended)
40+
- [Formatting numbers and dates](#formatting-numbers-and-dates)
41+
- [Testing](#testing)
42+
- [Unit tests](#unit-tests)
43+
- [Component accessibility tests](#component-accessibility-tests)
44+
- [End to end tests](#end-to-end-tests)
45+
- [Submitting changes](#submitting-changes)
46+
- [Before submitting](#before-submitting)
47+
- [Pull request process](#pull-request-process)
48+
- [Commit messages and PR titles](#commit-messages-and-pr-titles)
49+
- [Pre-commit hooks](#pre-commit-hooks)
50+
- [Using AI](#using-ai)
51+
- [Questions](#questions)
52+
- [License](#license)
53+
1754
## Getting started
1855

1956
### Prerequisites
@@ -111,7 +148,7 @@ To help with this, the project uses `oxfmt` to handle formatting via a pre-commi
111148

112149
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.
113150

114-
### Typescript
151+
### TypeScript
115152

116153
- We care about good types – never cast things to `any` 💪
117154
- Validate rather than just assert

app/app.vue

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ const localeMap = locales.value.reduce(
2121
{} as Record<string, Directions>,
2222
)
2323
24+
const darkMode = usePreferredDark()
25+
const colorMode = useColorMode()
26+
const colorScheme = computed(() => {
27+
return {
28+
system: darkMode ? 'dark light' : 'light dark',
29+
light: 'only light',
30+
dark: 'only dark',
31+
}[colorMode.preference]
32+
})
33+
2434
useHead({
2535
htmlAttrs: {
2636
'lang': () => locale.value,
@@ -30,6 +40,7 @@ useHead({
3040
titleTemplate: titleChunk => {
3141
return titleChunk ? titleChunk : 'npmx - Better npm Package Browser'
3242
},
43+
meta: [{ name: 'color-scheme', content: colorScheme }],
3344
})
3445
3546
if (import.meta.server) {
@@ -68,20 +79,37 @@ function handleGlobalKeyup() {
6879
showKbdHints.value = false
6980
}
7081
71-
/* A hack to get light dismiss to work in safari because it does not support closedby="any" yet */
82+
// Light dismiss fallback for browsers that don't support closedby="any" (Safari + old Chrome/Firefox)
7283
// https://codepen.io/paramagicdev/pen/gbYompq
7384
// see: https://github.com/npmx-dev/npmx.dev/pull/522#discussion_r2749978022
7485
function handleModalLightDismiss(e: MouseEvent) {
7586
const target = e.target as HTMLElement
7687
if (target.tagName === 'DIALOG' && target.hasAttribute('open')) {
88+
const rect = target.getBoundingClientRect()
89+
const isOutside =
90+
e.clientX < rect.left ||
91+
e.clientX > rect.right ||
92+
e.clientY < rect.top ||
93+
e.clientY > rect.bottom
94+
95+
if (!isOutside) return
7796
;(target as HTMLDialogElement).close()
7897
}
7998
}
8099
81100
if (import.meta.client) {
82101
useEventListener(document, 'keydown', handleGlobalKeydown)
83102
useEventListener(document, 'keyup', handleGlobalKeyup)
84-
useEventListener(document, 'click', handleModalLightDismiss)
103+
104+
// Feature check for native light dismiss support via closedby="any"
105+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby
106+
const supportsClosedBy =
107+
typeof HTMLDialogElement !== 'undefined' &&
108+
typeof HTMLDialogElement.prototype === 'object' &&
109+
'closedBy' in HTMLDialogElement.prototype
110+
if (!supportsClosedBy) {
111+
useEventListener(document, 'click', handleModalLightDismiss)
112+
}
85113
}
86114
</script>
87115

app/assets/main.css

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77

88
:root[data-theme='dark'] {
99
/* background colors */
10-
--bg: oklch(0.145 0 0);
11-
--bg-subtle: oklch(0.178 0 0);
12-
--bg-muted: oklch(0.218 0 0);
13-
--bg-elevated: oklch(0.252 0 0);
10+
--bg: var(--bg-color, oklch(0.145 0 0));
11+
--bg-subtle: var(--bg-subtle-color, oklch(0.178 0 0));
12+
--bg-muted: var(--bg-muted-color, oklch(0.218 0 0));
13+
--bg-elevated: var(--bg-elevated-color, oklch(0.252 0 0));
1414

1515
/* text colors */
1616
--fg: oklch(0.985 0 0);
1717
--fg-muted: oklch(0.709 0 0);
1818
--fg-subtle: oklch(0.633 0 0);
1919

20-
/* border, seperator colors */
20+
/* border, separator colors */
2121
--border: oklch(0.269 0 0);
2222
--border-subtle: oklch(0.239 0 0);
2323
--border-hover: oklch(0.371 0 0);
@@ -43,11 +43,39 @@
4343
--badge-pink: oklch(0.584 0.189 343);
4444
}
4545

46+
:root[data-theme='dark'][data-bg-theme='slate'] {
47+
--bg: oklch(0.129 0.012 264.695);
48+
--bg-subtle: oklch(0.159 0.022 262.421);
49+
--bg-muted: oklch(0.204 0.033 261.234);
50+
--bg-elevated: oklch(0.259 0.041 260.031);
51+
}
52+
53+
:root[data-theme='dark'][data-bg-theme='zinc'] {
54+
--bg: oklch(0.141 0.005 285.823);
55+
--bg-subtle: oklch(0.168 0.005 285.894);
56+
--bg-muted: oklch(0.209 0.005 285.929);
57+
--bg-elevated: oklch(0.256 0.006 286.033);
58+
}
59+
60+
:root[data-theme='dark'][data-bg-theme='stone'] {
61+
--bg: oklch(0.147 0.004 49.25);
62+
--bg-subtle: oklch(0.178 0.004 49.321);
63+
--bg-muted: oklch(0.218 0.004 49.386);
64+
--bg-elevated: oklch(0.252 0.007 34.298);
65+
}
66+
67+
:root[data-theme='dark'][data-bg-theme='black'] {
68+
--bg: oklch(0 0 0);
69+
--bg-subtle: oklch(0.148 0 0);
70+
--bg-muted: oklch(0.204 0 0);
71+
--bg-elevated: oklch(0.264 0 0);
72+
}
73+
4674
:root[data-theme='light'] {
47-
--bg: oklch(1 0 0);
48-
--bg-subtle: oklch(0.979 0.001 286.375);
49-
--bg-muted: oklch(0.955 0 0);
50-
--bg-elevated: oklch(0.94 0 0);
75+
--bg: var(--bg-color, oklch(1 0 0));
76+
--bg-subtle: var(--bg-subtle-color, oklch(0.979 0.001 286.375));
77+
--bg-muted: var(--bg-muted-color, oklch(0.955 0 0));
78+
--bg-elevated: var(--bg-elevated-color, oklch(0.94 0 0));
5179

5280
--fg: oklch(0.145 0 0);
5381
--fg-muted: oklch(0.439 0 0);
@@ -75,6 +103,27 @@
75103
--badge-cyan: oklch(0.571 0.181 210);
76104
}
77105

106+
:root[data-theme='light'][data-bg-theme='slate'] {
107+
--bg: oklch(1 0 0);
108+
--bg-subtle: oklch(0.982 0.006 264.62);
109+
--bg-muted: oklch(0.96 0.041 261.234);
110+
--bg-elevated: oklch(0.943 0.013 255.52);
111+
}
112+
113+
:root[data-theme='light'][data-bg-theme='zinc'] {
114+
--bg: oklch(1 0 0);
115+
--bg-subtle: oklch(0.979 0.004 286.53);
116+
--bg-muted: oklch(0.958 0.004 286.39);
117+
--bg-elevated: oklch(0.939 0.004 286.32);
118+
}
119+
120+
:root[data-theme='light'][data-bg-theme='stone'] {
121+
--bg: oklch(1 0 0);
122+
--bg-subtle: oklch(0.979 0.005 48.762);
123+
--bg-muted: oklch(0.958 0.005 48.743);
124+
--bg-elevated: oklch(0.943 0.005 48.731);
125+
}
126+
78127
@media (prefers-contrast: more) {
79128
:root[data-theme='dark'] {
80129
/* text colors */
@@ -199,6 +248,14 @@ button {
199248
background: #404040;
200249
}
201250

251+
/* Scrollbar styling for Firefox */
252+
@supports not selector(::-webkit-scrollbar) {
253+
* {
254+
scrollbar-width: thin;
255+
scrollbar-color: var(--border) var(--bg);
256+
}
257+
}
258+
202259
/* Shiki theme colors */
203260
html.light .shiki,
204261
html.light .shiki span {

app/components/AppFooter.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const isHome = computed(() => route.name === 'index')
77
<footer class="border-t border-border mt-auto">
88
<div class="container py-3 sm:py-8 flex flex-col gap-2 sm:gap-4 text-fg-subtle text-sm">
99
<div
10-
class="flex flex-col sm:flex-row items-center sm:items-baseline justify-between gap-2 sm:gap-4"
10+
class="flex flex-col sm:flex-row sm:flex-wrap items-center sm:items-baseline justify-between gap-2 sm:gap-4"
1111
>
1212
<div>
1313
<p class="font-mono text-balance m-0 hidden sm:block">{{ $t('tagline') }}</p>

app/components/AppHeader.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const showMobileMenu = shallowRef(false)
1919
const route = useRoute()
2020
const isMobile = useIsMobile()
2121
const isSearchExpandedManually = shallowRef(false)
22-
const searchBoxRef = shallowRef<{ focus: () => void } | null>(null)
22+
const searchBoxRef = useTemplateRef('searchBoxRef')
2323
2424
// On search page, always show search expanded on mobile
2525
const isOnHomePage = computed(() => route.name === 'index')
@@ -146,7 +146,7 @@ onKeyStroke(
146146
@blur="handleSearchBlur"
147147
/>
148148
<ul
149-
v-if="!isSearchExpanded"
149+
v-if="!isSearchExpanded && isConnected && npmUser"
150150
:class="{ hidden: showFullSearch }"
151151
class="hidden sm:flex items-center gap-4 sm:gap-6 list-none m-0 p-0"
152152
>

app/components/BaseCard.vue

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script setup lang="ts">
2+
defineProps<{
3+
/** Whether this is an exact match for the query */
4+
isExactMatch?: boolean
5+
}>()
6+
</script>
7+
8+
<template>
9+
<article
10+
class="group bg-bg-subtle border border-border rounded-lg p-4 sm:p-6 transition-[border-color,background-color] duration-200 hover:(border-border-hover bg-bg-muted) cursor-pointer relative focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-bg focus-within:ring-offset-2 focus-within:ring-fg/50 focus-within:bg-bg-muted focus-within:border-border-hover"
11+
:class="{
12+
'border-accent/30 contrast-more:border-accent/90 bg-accent/5': isExactMatch,
13+
}"
14+
>
15+
<!-- Glow effect for exact matches -->
16+
<div
17+
v-if="isExactMatch"
18+
class="absolute -inset-px rounded-lg bg-gradient-to-r from-accent/0 via-accent/0 to-accent/10 opacity-100 blur-sm -z-1 pointer-events-none motion-reduce:opacity-50"
19+
aria-hidden="true"
20+
/>
21+
<slot />
22+
</article>
23+
</template>

0 commit comments

Comments
 (0)