Skip to content

Commit ed824c1

Browse files
committed
fix: merge conflict
2 parents a88334d + 6361d3c commit ed824c1

Some content is hidden

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

54 files changed

+4479
-774
lines changed

app/app.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
11
<script setup lang="ts">
2+
import type { Directions } from '@nuxtjs/i18n'
23
import { useEventListener } from '@vueuse/core'
34
45
const route = useRoute()
56
const router = useRouter()
7+
const { locale, locales } = useI18n()
68
79
// Initialize accent color before hydration to prevent flash
810
initAccentOnPrehydrate()
911
1012
const isHomepage = computed(() => route.name === 'index')
1113
14+
const localeMap = locales.value.reduce(
15+
(acc, l) => {
16+
acc[l.code] = l.dir ?? 'ltr'
17+
return acc
18+
},
19+
{} as Record<string, Directions>,
20+
)
21+
1222
useHead({
23+
htmlAttrs: {
24+
lang: () => locale.value,
25+
dir: () => localeMap[locale.value] ?? 'ltr',
26+
},
1327
titleTemplate: titleChunk => {
1428
return titleChunk ? titleChunk : 'npmx - Better npm Package Browser'
1529
},
1630
})
1731
32+
if (import.meta.server) {
33+
setJsonLd(createWebSiteSchema())
34+
}
35+
1836
// Global keyboard shortcut: "/" focuses search or navigates to search page
1937
function handleGlobalKeydown(e: KeyboardEvent) {
2038
const target = e.target as HTMLElement

app/assets/main.css

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ button {
154154

155155
/* Skip link */
156156
.skip-link {
157-
position: absolute;
157+
position: fixed;
158158
top: -100%;
159159
left: 0;
160160
padding: 0.5rem 1rem;
@@ -165,6 +165,15 @@ button {
165165
transition: top 0.2s ease;
166166
}
167167

168+
html[dir='rtl'] .skip-link {
169+
left: unset;
170+
right: 0;
171+
}
172+
173+
.skip-link:hover {
174+
color: var(--bg);
175+
text-decoration: underline;
176+
}
168177
.skip-link:focus {
169178
top: 0;
170179
}

app/components/AccentColorPicker.vue

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,51 @@
22
import { useAccentColor } from '~/composables/useSettings'
33
44
const { accentColors, selectedAccentColor, setAccentColor } = useAccentColor()
5+
6+
onPrehydrate(el => {
7+
const settings = JSON.parse(localStorage.getItem('npmx-settings') || '{}')
8+
const id = settings.accentColorId
9+
if (id) {
10+
const input = el.querySelector<HTMLInputElement>(`input[value="${id}"]`)
11+
if (input) {
12+
input.checked = true
13+
}
14+
}
15+
})
516
</script>
617

718
<template>
8-
<div role="listbox" :aria-label="$t('settings.accent_colors')" class="flex items-center gap-4">
9-
<button
19+
<fieldset class="flex items-center gap-4">
20+
<legend class="sr-only">{{ $t('settings.accent_colors') }}</legend>
21+
<label
1022
v-for="color in accentColors"
1123
:key="color.id"
12-
type="button"
13-
role="option"
14-
:aria-selected="selectedAccentColor === color.id"
15-
:aria-label="color.name"
16-
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 focus-ring aria-selected:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle)"
24+
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 cursor-pointer has-[:checked]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle) has-[:focus-visible]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle)"
1725
:style="{ backgroundColor: color.value }"
18-
@click="setAccentColor(color.id)"
19-
/>
20-
<button
21-
type="button"
22-
:aria-label="$t('settings.clear_accent')"
23-
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 focus-ring flex items-center justify-center bg-accent-fallback"
24-
@click="setAccentColor(null)"
2526
>
27+
<input
28+
type="radio"
29+
name="accent-color"
30+
class="sr-only"
31+
:value="color.id"
32+
:checked="selectedAccentColor === color.id"
33+
:aria-label="color.name"
34+
@change="setAccentColor(color.id)"
35+
/>
36+
</label>
37+
<label
38+
class="size-6 rounded-full transition-transform duration-150 motion-safe:hover:scale-110 cursor-pointer has-[:checked]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle) has-[:focus-visible]:(ring-2 ring-fg ring-offset-2 ring-offset-bg-subtle) flex items-center justify-center bg-accent-fallback"
39+
>
40+
<input
41+
type="radio"
42+
name="accent-color"
43+
class="sr-only"
44+
value=""
45+
:checked="selectedAccentColor === null"
46+
:aria-label="$t('settings.clear_accent')"
47+
@change="setAccentColor(null)"
48+
/>
2649
<span class="i-carbon-error size-4 text-bg" aria-hidden="true" />
27-
</button>
28-
</div>
50+
</label>
51+
</fieldset>
2952
</template>

app/components/AppHeader.vue

Lines changed: 17 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
<script setup lang="ts">
2-
import { debounce } from 'perfect-debounce'
3-
42
withDefaults(
53
defineProps<{
64
showLogo?: boolean
@@ -15,28 +13,8 @@ withDefaults(
1513
const { isConnected, npmUser } = useConnector()
1614
1715
const router = useRouter()
18-
const route = useRoute()
19-
20-
const searchQuery = ref('')
21-
const isSearchFocused = ref(false)
22-
23-
const showSearchBar = computed(() => {
24-
return route.name !== 'search' && route.name !== 'index'
25-
})
26-
27-
const debouncedNavigate = debounce(async () => {
28-
const query = searchQuery.value.trim()
29-
await router.push({
30-
name: 'search',
31-
query: query ? { q: query } : undefined,
32-
})
33-
// allow time for the navigation to occur before resetting searchQuery
34-
setTimeout(() => (searchQuery.value = ''), 1000)
35-
}, 100)
3616
37-
async function handleSearchInput() {
38-
debouncedNavigate()
39-
}
17+
const showFullSearch = ref(false)
4018
4119
onKeyStroke(',', e => {
4220
// Don't trigger if user is typing in an input
@@ -57,7 +35,7 @@ onKeyStroke(',', e => {
5735
class="container h-14 flex items-center justify-start"
5836
>
5937
<!-- Start: Logo -->
60-
<div class="flex-shrink-0">
38+
<div :class="{ 'hidden sm:block': showFullSearch }" class="flex-shrink-0">
6139
<NuxtLink
6240
v-if="showLogo"
6341
to="/"
@@ -71,41 +49,17 @@ onKeyStroke(',', e => {
7149
</div>
7250

7351
<!-- Center: Search bar + nav items -->
74-
<div class="flex-1 flex items-center justify-center gap-4 sm:gap-6">
75-
<!-- Search bar (shown on all pages except home and search) -->
76-
<search v-if="showSearchBar" class="hidden sm:block flex-1 max-w-md">
77-
<form method="GET" action="/search" class="relative" @submit.prevent="handleSearchInput">
78-
<label for="header-search" class="sr-only">
79-
{{ $t('search.label') }}
80-
</label>
81-
82-
<div class="relative group" :class="{ 'is-focused': isSearchFocused }">
83-
<div class="search-box relative flex items-center">
84-
<span
85-
class="absolute inset-is-3 text-fg-subtle font-mono text-sm pointer-events-none transition-colors duration-200 motion-reduce:transition-none group-focus-within:text-accent z-1"
86-
>
87-
/
88-
</span>
89-
90-
<input
91-
id="header-search"
92-
v-model="searchQuery"
93-
type="search"
94-
name="q"
95-
:placeholder="$t('search.placeholder')"
96-
v-bind="noCorrect"
97-
class="w-full bg-bg-subtle border border-border rounded-md ps-7 pe-3 py-1.5 font-mono text-sm text-fg placeholder:text-fg-subtle transition-border-color duration-300 motion-reduce:transition-none focus:border-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50"
98-
@input="handleSearchInput"
99-
@focus="isSearchFocused = true"
100-
@blur="isSearchFocused = false"
101-
/>
102-
<button type="submit" class="sr-only">{{ $t('search.button') }}</button>
103-
</div>
104-
</div>
105-
</form>
106-
</search>
107-
108-
<ul class="flex items-center gap-4 sm:gap-6 list-none m-0 p-0">
52+
<div class="flex-1 flex items-center justify-center md:gap-6 mx-2">
53+
<!-- Search bar (shown on all pages except home) -->
54+
<SearchBox
55+
:inputClass="showFullSearch ? '' : 'max-w[6rem]'"
56+
@focus="showFullSearch = true"
57+
@blur="showFullSearch = false"
58+
/>
59+
<ul
60+
:class="{ 'hidden sm:flex': showFullSearch }"
61+
class="flex items-center gap-4 sm:gap-6 list-none m-0 p-0"
62+
>
10963
<!-- Packages dropdown (when connected) -->
11064
<li v-if="isConnected && npmUser" class="flex items-center">
11165
<HeaderPackagesDropdown :username="npmUser" />
@@ -119,11 +73,13 @@ onKeyStroke(',', e => {
11973
</div>
12074

12175
<!-- End: Bookmarks + Settings + Connector -->
122-
<div class="flex-shrink-0 flex items-center gap-4 sm:gap-6 ms-auto sm:ms-0">
76+
<div
77+
:class="{ 'hidden sm:flex': showFullSearch }"
78+
class="flex-shrink-0 flex items-center gap-4 sm:gap-6 ms-auto sm:ms-0"
79+
>
12380
<ClientOnly>
12481
<HeaderBookmarksDropdown />
12582
</ClientOnly>
126-
12783
<NuxtLink
12884
to="/about"
12985
class="sm:hidden link-subtle font-mono text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded"

app/components/ColumnPicker.vue

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ function handleReset() {
9393
v-if="isOpen"
9494
ref="menuRef"
9595
:id="menuId"
96-
class="absolute right-0 mt-2 w-56 bg-bg-subtle border border-border rounded-lg shadow-lg z-20"
96+
class="absolute right-0 mt-2 w-60 bg-bg-subtle border border-border rounded-lg shadow-lg z-20"
9797
role="group"
9898
:aria-label="$t('filters.columns.show')"
9999
>
@@ -109,7 +109,7 @@ function handleReset() {
109109
<label
110110
v-for="column in toggleableColumns"
111111
:key="column.id"
112-
class="flex items-center px-3 py-2 transition-colors duration-200"
112+
class="flex gap-2 items-center px-3 py-2 transition-colors duration-200"
113113
:class="
114114
column.disabled
115115
? 'opacity-50 cursor-not-allowed'
@@ -124,16 +124,20 @@ function handleReset() {
124124
class="w-4 h-4 accent-fg bg-bg-muted border-border rounded disabled:opacity-50"
125125
@change="!column.disabled && emit('toggle', column.id)"
126126
/>
127-
<span class="ml-2 text-sm text-fg-muted font-mono flex-1">
127+
<span class="text-sm text-fg-muted font-mono flex-1">
128128
{{ getColumnLabel(column.id) }}
129129
</span>
130-
<span
130+
<AppTooltip
131131
v-if="column.disabled"
132132
:id="`${column.id}-disabled-reason`"
133-
class="text-xs text-fg-subtle italic"
133+
class="text-fg-subtle"
134+
:text="$t('filters.columns.coming_soon')"
135+
position="left"
134136
>
135-
{{ $t('filters.columns.coming_soon') }}
136-
</span>
137+
<span class="size-4 flex justify-center items-center text-xs border rounded-full"
138+
>i</span
139+
>
140+
</AppTooltip>
137141
</label>
138142
</div>
139143

app/components/DateTime.vue

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,26 +51,17 @@ const titleValue = computed(() => {
5151
</script>
5252

5353
<template>
54-
<ClientOnly>
55-
<NuxtTime
56-
v-if="relativeDates"
57-
:datetime="datetime"
58-
:title="titleValue"
59-
relative
60-
:locale="locale"
61-
/>
62-
<NuxtTime
63-
v-else
64-
:datetime="datetime"
65-
:title="titleValue"
66-
:date-style="dateStyle"
67-
:year="year"
68-
:month="month"
69-
:day="day"
70-
:locale="locale"
71-
/>
72-
<template #fallback>
54+
<span>
55+
<ClientOnly>
7356
<NuxtTime
57+
v-if="relativeDates"
58+
:datetime="datetime"
59+
:title="titleValue"
60+
relative
61+
:locale="locale"
62+
/>
63+
<NuxtTime
64+
v-else
7465
:datetime="datetime"
7566
:title="titleValue"
7667
:date-style="dateStyle"
@@ -79,6 +70,17 @@ const titleValue = computed(() => {
7970
:day="day"
8071
:locale="locale"
8172
/>
82-
</template>
83-
</ClientOnly>
73+
<template #fallback>
74+
<NuxtTime
75+
:datetime="datetime"
76+
:title="titleValue"
77+
:date-style="dateStyle"
78+
:year="year"
79+
:month="month"
80+
:day="day"
81+
:locale="locale"
82+
/>
83+
</template>
84+
</ClientOnly>
85+
</span>
8486
</template>

app/components/OrgMembersPanel.vue

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,7 @@ watch(lastExecutionTime, () => {
286286
</script>
287287

288288
<template>
289-
<section
290-
v-if="isConnected"
291-
aria-labelledby="members-heading"
292-
class="bg-bg-subtle border border-border rounded-lg overflow-hidden"
293-
>
289+
<section v-if="isConnected" class="bg-bg-subtle border border-border rounded-lg overflow-hidden">
294290
<!-- Header -->
295291
<div class="flex items-center justify-between p-4 border-b border-border">
296292
<h2 id="members-heading" class="font-mono text-sm font-medium flex items-center gap-2">

0 commit comments

Comments
 (0)