Skip to content

Commit 4f6c2df

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/algolia-search
2 parents 555470b + 13ef534 commit 4f6c2df

30 files changed

+720
-288
lines changed

app/assets/main.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,26 @@
171171
}
172172
}
173173

174+
/*
175+
* Forced Colors Mode (WHCM) Override for Icons
176+
*
177+
* By default, `forced-color-adjust: preserve-parent-color` (from UnoConfig) works fine
178+
* for most icons as they inherit the correct text color.
179+
*
180+
* However, if icons disappear in specific contexts (e.g., inside buttons with
181+
* complex backgrounds or transparent states), uncomment the following block
182+
* to enforce visibility using `CanvasText`.
183+
*/
184+
/*
185+
@media (forced-colors: active) {
186+
[class^='i-'],
187+
[class*=' i-'] {
188+
forced-color-adjust: none !important;
189+
color: CanvasText !important;
190+
}
191+
}
192+
*/
193+
174194
html {
175195
-webkit-font-smoothing: antialiased;
176196
-moz-osx-font-smoothing: grayscale;

app/components/Header/AuthModal.client.vue

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<script setup lang="ts">
22
import { useAtproto } from '~/composables/atproto/useAtproto'
33
import { authRedirect } from '~/utils/atproto/helpers'
4+
import { ensureValidAtIdentifier } from '@atproto/syntax'
45
56
const handleInput = shallowRef('')
7+
const errorMessage = shallowRef('')
68
const route = useRoute()
79
const { user, logout } = useAtproto()
810
@@ -13,26 +15,41 @@ const localeSubPath = ['ko', 'pt', 'ja'].includes(currentLang) ? currentLang : '
1315
const atprotoLink = `https://atproto.com/${localeSubPath}`
1416
1517
async function handleBlueskySignIn() {
16-
await authRedirect('https://bsky.social', { redirectTo: route.fullPath })
18+
await authRedirect('https://bsky.social', { redirectTo: route.fullPath, locale: locale.value })
1719
}
1820
1921
async function handleCreateAccount() {
20-
await authRedirect('https://npmx.social', { create: true, redirectTo: route.fullPath })
22+
await authRedirect('https://npmx.social', {
23+
create: true,
24+
redirectTo: route.fullPath,
25+
locale: locale.value,
26+
})
2127
}
2228
2329
async function handleLogin() {
2430
if (handleInput.value) {
25-
await authRedirect(handleInput.value)
31+
// URLS to PDSs are valid for oauth redirects
32+
if (!handleInput.value.startsWith('https://')) {
33+
try {
34+
ensureValidAtIdentifier(handleInput.value)
35+
} catch (error) {
36+
errorMessage.value =
37+
error instanceof Error ? error.message : $t('auth.modal.default_input_error')
38+
return
39+
}
40+
}
41+
await authRedirect(handleInput.value, {
42+
redirectTo: route.fullPath,
43+
locale: locale.value,
44+
})
2645
}
2746
}
2847
2948
watch(handleInput, newHandleInput => {
49+
errorMessage.value = ''
3050
if (!newHandleInput) return
3151
32-
const normalized = newHandleInput
33-
.trim()
34-
.toLowerCase()
35-
.replace(/[^a-z0-9.-]/g, '')
52+
const normalized = newHandleInput.trim().toLowerCase().replace(/@/g, '')
3653
3754
if (normalized !== newHandleInput) {
3855
handleInput.value = normalized
@@ -81,6 +98,9 @@ watch(handleInput, newHandleInput => {
8198
v-bind="noCorrect"
8299
class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 hover:border-fg-subtle focus:border-accent focus-visible:(outline-2 outline-accent/70)"
83100
/>
101+
<p v-if="errorMessage" class="text-red-500 text-xs mt-1" role="alert">
102+
{{ errorMessage }}
103+
</p>
84104
</div>
85105

86106
<details class="text-sm">

app/components/Header/MobileMenu.client.vue

Lines changed: 99 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -99,28 +99,84 @@ onUnmounted(deactivate)
9999
</button>
100100
</div>
101101

102-
<!-- Navigation links -->
103-
<div class="flex-1 overflow-y-auto overscroll-contain py-2">
104-
<!-- Main navigation -->
105-
<div class="px-2 py-2">
106-
<NuxtLink
107-
:to="{ name: 'about' }"
108-
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
109-
@click="closeMenu"
102+
<!-- Account section -->
103+
<div class="px-2 py-2">
104+
<span
105+
class="px-3 py-2 block font-mono text-xs text-fg-subtle uppercase tracking-wider"
106+
>
107+
{{ $t('account_menu.account') }}
108+
</span>
109+
110+
<!-- npm CLI connection status (only show if connected) -->
111+
<button
112+
v-if="isConnected && npmUser"
113+
type="button"
114+
class="w-full flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200 text-start"
115+
@click="handleShowConnector"
116+
>
117+
<img
118+
v-if="npmAvatar"
119+
:src="npmAvatar"
120+
:alt="npmUser"
121+
width="20"
122+
height="20"
123+
class="w-5 h-5 rounded-full object-cover"
124+
/>
125+
<span
126+
v-else
127+
class="w-5 h-5 rounded-full bg-bg-muted flex items-center justify-center"
110128
>
111-
<span class="i-carbon:information w-5 h-5 text-fg-muted" aria-hidden="true" />
112-
{{ $t('footer.about') }}
113-
</NuxtLink>
129+
<span class="i-carbon-terminal w-3 h-3 text-fg-muted" aria-hidden="true" />
130+
</span>
131+
<span class="flex-1">~{{ npmUser }}</span>
132+
<span class="w-2 h-2 rounded-full bg-green-500" aria-hidden="true" />
133+
</button>
114134

115-
<NuxtLink
116-
:to="{ name: 'privacy' }"
117-
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
118-
@click="closeMenu"
135+
<!-- Atmosphere connection status -->
136+
<button
137+
v-if="atprotoUser"
138+
type="button"
139+
class="w-full flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200 text-start"
140+
@click="handleShowAuth"
141+
>
142+
<img
143+
v-if="atprotoUser.avatar"
144+
:src="atprotoUser.avatar"
145+
:alt="atprotoUser.handle"
146+
width="20"
147+
height="20"
148+
class="w-5 h-5 rounded-full object-cover"
149+
/>
150+
<span
151+
v-else
152+
class="w-5 h-5 rounded-full bg-bg-muted flex items-center justify-center"
119153
>
120-
<span class="i-carbon:security w-5 h-5 text-fg-muted" aria-hidden="true" />
121-
{{ $t('privacy_policy.title') }}
122-
</NuxtLink>
154+
<span class="i-carbon-cloud w-3 h-3 text-fg-muted" aria-hidden="true" />
155+
</span>
156+
<span class="flex-1 truncate">@{{ atprotoUser.handle }}</span>
157+
</button>
123158

159+
<!-- Connect Atmosphere button (show if not connected) -->
160+
<button
161+
v-else
162+
type="button"
163+
class="w-full flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200 text-start"
164+
@click="handleShowAuth"
165+
>
166+
<span class="w-5 h-5 rounded-full bg-bg-muted flex items-center justify-center">
167+
<span class="i-carbon-cloud w-3 h-3 text-fg-muted" aria-hidden="true" />
168+
</span>
169+
<span class="flex-1">{{ $t('account_menu.connect_atmosphere') }}</span>
170+
</button>
171+
</div>
172+
173+
<!-- Divider -->
174+
<div class="mx-4 my-2 border-t border-border" />
175+
176+
<!-- Navigation links -->
177+
<div class="flex-1 overflow-y-auto overscroll-contain py-2">
178+
<!-- App navigation -->
179+
<div class="px-2 py-2">
124180
<NuxtLink
125181
:to="{ name: 'compare' }"
126182
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
@@ -164,7 +220,31 @@ onUnmounted(deactivate)
164220
<!-- Divider -->
165221
<div class="mx-4 my-2 border-t border-border" />
166222

167-
<!-- External links (from footer) -->
223+
<!-- Informational links -->
224+
<div class="px-2 py-2">
225+
<NuxtLink
226+
:to="{ name: 'about' }"
227+
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
228+
@click="closeMenu"
229+
>
230+
<span class="i-carbon:information w-5 h-5 text-fg-muted" aria-hidden="true" />
231+
{{ $t('footer.about') }}
232+
</NuxtLink>
233+
234+
<NuxtLink
235+
:to="{ name: 'privacy' }"
236+
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
237+
@click="closeMenu"
238+
>
239+
<span class="i-carbon:security w-5 h-5 text-fg-muted" aria-hidden="true" />
240+
{{ $t('privacy_policy.title') }}
241+
</NuxtLink>
242+
</div>
243+
244+
<!-- Divider -->
245+
<div class="mx-4 my-2 border-t border-border" />
246+
247+
<!-- External links -->
168248
<div class="px-2 py-2">
169249
<span class="px-3 py-2 font-mono text-xs text-fg-subtle uppercase tracking-wider">
170250
{{ $t('nav.links') }}
@@ -227,80 +307,6 @@ onUnmounted(deactivate)
227307
</a>
228308
</div>
229309
</div>
230-
231-
<!-- Divider -->
232-
<div class="mx-4 my-2 border-t border-border" />
233-
234-
<!-- Account section -->
235-
<div class="px-2 py-2">
236-
<span
237-
class="px-3 py-2 block font-mono text-xs text-fg-subtle uppercase tracking-wider"
238-
>
239-
{{ $t('account_menu.account') }}
240-
</span>
241-
242-
<!-- npm CLI connection status (only show if connected) -->
243-
<button
244-
v-if="isConnected && npmUser"
245-
type="button"
246-
class="w-full flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200 text-start"
247-
@click="handleShowConnector"
248-
>
249-
<img
250-
v-if="npmAvatar"
251-
:src="npmAvatar"
252-
:alt="npmUser"
253-
width="20"
254-
height="20"
255-
class="w-5 h-5 rounded-full object-cover"
256-
/>
257-
<span
258-
v-else
259-
class="w-5 h-5 rounded-full bg-bg-muted flex items-center justify-center"
260-
>
261-
<span class="i-carbon-terminal w-3 h-3 text-fg-muted" aria-hidden="true" />
262-
</span>
263-
<span class="flex-1">~{{ npmUser }}</span>
264-
<span class="w-2 h-2 rounded-full bg-green-500" aria-hidden="true" />
265-
</button>
266-
267-
<!-- Atmosphere connection status -->
268-
<button
269-
v-if="atprotoUser"
270-
type="button"
271-
class="w-full flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200 text-start"
272-
@click="handleShowAuth"
273-
>
274-
<img
275-
v-if="atprotoUser.avatar"
276-
:src="atprotoUser.avatar"
277-
:alt="atprotoUser.handle"
278-
width="20"
279-
height="20"
280-
class="w-5 h-5 rounded-full object-cover"
281-
/>
282-
<span
283-
v-else
284-
class="w-5 h-5 rounded-full bg-bg-muted flex items-center justify-center"
285-
>
286-
<span class="i-carbon-cloud w-3 h-3 text-fg-muted" aria-hidden="true" />
287-
</span>
288-
<span class="flex-1 truncate">@{{ atprotoUser.handle }}</span>
289-
</button>
290-
291-
<!-- Connect Atmosphere button (show if not connected) -->
292-
<button
293-
v-else
294-
type="button"
295-
class="w-full flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200 text-start"
296-
@click="handleShowAuth"
297-
>
298-
<span class="w-5 h-5 rounded-full bg-bg-muted flex items-center justify-center">
299-
<span class="i-carbon-cloud w-3 h-3 text-fg-muted" aria-hidden="true" />
300-
</span>
301-
<span class="flex-1">{{ $t('account_menu.connect_atmosphere') }}</span>
302-
</button>
303-
</div>
304310
</nav>
305311
</Transition>
306312
</div>

app/components/Package/ClaimPackageModal.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ const isChecking = computed(() => {
3636
})
3737
3838
const mergedError = computed(() => {
39-
return (
40-
publishError.value ??
41-
(checkError.value instanceof Error
42-
? checkError.value.message
43-
: $t('claim.modal.failed_to_check'))
44-
)
39+
return checkResult.value !== null
40+
? null
41+
: (publishError.value ??
42+
(checkError.value instanceof Error
43+
? checkError.value.message
44+
: $t('claim.modal.failed_to_check')))
4545
})
4646
4747
const connectorModal = useModal('connector-modal')

app/components/Package/Skeleton.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
>
99
<!-- Package header — matches area-header in [...name].vue -->
1010
<header class="area-header sticky top-14 z-1 bg-[--bg] py-2">
11-
<div class="flex items-baseline gap-2 sm:gap-3 flex-wrap min-w-0">
11+
<div class="flex items-baseline gap-x-2 gap-y-1 sm:gap-x-3 flex-wrap min-w-0">
1212
<!-- Package name -->
1313
<div class="min-w-0">
1414
<h1 class="font-mono text-2xl sm:text-3xl font-medium">
@@ -19,8 +19,8 @@
1919
<span class="inline-flex items-baseline font-mono text-base sm:text-lg shrink-0">
2020
<SkeletonInline class="h-6 w-20" />
2121
</span>
22-
<!-- Metrics badges placeholder -->
23-
<div class="flex items-center gap-1.5 relative top-[5px] self-baseline ms-1 sm:ms-2">
22+
<!-- Metrics badges -->
23+
<div class="basis-full flex items-center gap-1.5 self-baseline">
2424
<SkeletonBlock class="w-16 h-5.5 rounded" />
2525
<SkeletonBlock class="w-13 h-5.5 rounded" />
2626
<SkeletonBlock class="w-13 h-5.5 rounded" />

0 commit comments

Comments
 (0)