Skip to content

Commit 7204a44

Browse files
committed
fix: conflicts
2 parents 5422e01 + 4232ae2 commit 7204a44

52 files changed

Lines changed: 3177 additions & 465 deletions

Some content is hidden

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

CONTRIBUTING.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ npmx.dev uses [@nuxtjs/i18n](https://i18n.nuxtjs.org/) for internationalization.
214214

215215
### Approach
216216

217-
- All user-facing strings should use translation keys via `$t()` in templates or `t()` in script
217+
- All user-facing strings should use translation keys via `$t()` in templates and script
218218
- Translation files live in `i18n/locales/` (e.g., `en.json`)
219219
- We use the `no_prefix` strategy (no `/en/` or `/fr/` in URLs)
220220
- Locale preference is stored in cookies and respected on subsequent visits
@@ -233,8 +233,9 @@ npmx.dev uses [@nuxtjs/i18n](https://i18n.nuxtjs.org/) for internationalization.
233233
Or in script:
234234

235235
```typescript
236-
const { t } = useI18n()
237-
const message = t('my.translation.key')
236+
<script setup lang="ts">
237+
const message = computed(() => $t('my.translation.key'))
238+
</script>
238239
```
239240

240241
3. For dynamic values, use interpolation:

app/app.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ const route = useRoute()
55
const router = useRouter()
66
const { settings } = useSettings()
77
8+
// Initialize accent color before hydration to prevent flash
9+
initAccentOnPrehydrate()
10+
811
const isHomepage = computed(() => route.path === '/')
912
const theme = settings.value.theme === 'dark' ? 'dark' : 'light'
1013
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<script setup lang="ts">
2+
import { useAccentColor } from '~/composables/useSettings'
3+
4+
const { accentColors, selectedAccentColor, setAccentColor } = useAccentColor()
5+
</script>
6+
7+
<template>
8+
<div role="listbox" aria-label="Accent colors" class="flex items-center justify-between">
9+
<button
10+
v-for="color in accentColors"
11+
: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)"
17+
:style="{ backgroundColor: color.value }"
18+
@click="setAccentColor(color.id)"
19+
/>
20+
<button
21+
type="button"
22+
aria-label="Clear accent color"
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)"
25+
>
26+
<span class="i-carbon-error size-4 text-bg" aria-hidden="true" />
27+
</button>
28+
</div>
29+
</template>

app/components/AppFooter.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,12 @@ onMounted(() => {
137137
z-index: 40;
138138
/* Hidden by default (translated off-screen) */
139139
transform: translateY(100%);
140-
transition: transform 0.3s ease-out;
140+
}
141+
142+
@media (prefers-reduced-motion: no-preference) {
143+
.footer-scroll-state {
144+
transition: transform 0.3s ease-out;
145+
}
141146
}
142147
143148
/* Show footer when user can scroll up (meaning they've scrolled down) */

app/components/AppHeader.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const { isConnected, npmUser } = useConnector()
2424
:aria-label="$t('header.home')"
2525
class="header-logo font-mono text-lg font-medium text-fg hover:text-fg transition-colors duration-200 focus-ring rounded"
2626
>
27-
<span class="text-fg-subtle"><span style="letter-spacing: -0.2em">.</span>/</span>npmx
27+
<span class="text-accent"><span class="-tracking-0.2em">.</span>/</span>npmx
2828
</NuxtLink>
2929
<!-- Spacer when logo is hidden -->
3030
<span v-else class="w-1" />

app/components/ChartModal.vue

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<script setup lang="ts">
2+
const open = defineModel<boolean>('open', { default: false })
3+
4+
function handleKeydown(event: KeyboardEvent) {
5+
if (event.key === 'Escape') {
6+
open.value = false
7+
}
8+
}
9+
</script>
10+
11+
<template>
12+
<Teleport to="body">
13+
<Transition
14+
enter-active-class="transition-opacity duration-200"
15+
leave-active-class="transition-opacity duration-200"
16+
enter-from-class="opacity-0"
17+
leave-to-class="opacity-0"
18+
>
19+
<div
20+
v-if="open"
21+
class="fixed inset-0 z-50 flex items-center justify-center p-0 sm:p-4"
22+
@keydown="handleKeydown"
23+
>
24+
<!-- Backdrop -->
25+
<button
26+
type="button"
27+
class="absolute inset-0 bg-black/60 cursor-default"
28+
aria-label="Close modal"
29+
@click="open = false"
30+
/>
31+
32+
<div
33+
class="relative w-full h-full sm:h-auto bg-bg sm:border sm:border-border sm:rounded-lg shadow-xl sm:max-h-[90vh] overflow-y-auto overscroll-contain sm:max-w-3xl"
34+
role="dialog"
35+
aria-modal="true"
36+
aria-labelledby="chart-modal-title"
37+
>
38+
<div class="p-4 sm:p-6">
39+
<div class="flex items-center justify-between mb-4 sm:mb-6">
40+
<h2 id="chart-modal-title" class="font-mono text-lg font-medium">
41+
<slot name="title" />
42+
</h2>
43+
<button
44+
type="button"
45+
class="text-fg-subtle hover:text-fg transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
46+
aria-label="Close"
47+
@click="open = false"
48+
>
49+
<span class="i-carbon-close block w-5 h-5" aria-hidden="true" />
50+
</button>
51+
</div>
52+
<div class="font-mono text-sm">
53+
<slot />
54+
</div>
55+
</div>
56+
</div>
57+
</div>
58+
</Transition>
59+
</Teleport>
60+
</template>

app/components/ClaimPackageModal.vue

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ const props = defineProps<{
88
99
const open = defineModel<boolean>('open', { default: false })
1010
11-
const { t } = useI18n()
12-
1311
const {
1412
isConnected,
1513
state,
@@ -34,7 +32,7 @@ async function checkAvailability() {
3432
try {
3533
checkResult.value = await checkPackageName(props.packageName)
3634
} catch (err) {
37-
publishError.value = err instanceof Error ? err.message : t('claim.modal.failed_to_check')
35+
publishError.value = err instanceof Error ? err.message : $t('claim.modal.failed_to_check')
3836
} finally {
3937
isChecking.value = false
4038
}
@@ -84,7 +82,7 @@ async function handleClaim() {
8482
connectorModalOpen.value = true
8583
}
8684
} catch (err) {
87-
publishError.value = err instanceof Error ? err.message : t('claim.modal.failed_to_claim')
85+
publishError.value = err instanceof Error ? err.message : $t('claim.modal.failed_to_claim')
8886
} finally {
8987
isPublishing.value = false
9088
}
@@ -171,7 +169,7 @@ const connectorModalOpen = shallowRef(false)
171169

172170
<!-- Loading state -->
173171
<div v-if="isChecking" class="py-8 text-center">
174-
<LoadingSpinner :text="t('claim.modal.checking')" />
172+
<LoadingSpinner :text="$t('claim.modal.checking')" />
175173
</div>
176174

177175
<!-- Success state -->

app/components/ConnectorStatus.client.vue

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ const {
1212
const showModal = shallowRef(false)
1313
const showTooltip = shallowRef(false)
1414
15-
const { t } = useI18n()
16-
1715
const tooltipText = computed(() => {
18-
if (isConnecting.value) return t('connector.status.connecting')
19-
if (isConnected.value) return t('connector.status.connected')
20-
return t('connector.status.connect_cli')
16+
if (isConnecting.value) return $t('connector.status.connecting')
17+
if (isConnected.value) return $t('connector.status.connected')
18+
return $t('connector.status.connect_cli')
2119
})
2220
2321
const statusColor = computed(() => {
@@ -31,9 +29,9 @@ const operationCount = computed(() => activeOperations.value.length)
3129
3230
const ariaLabel = computed(() => {
3331
if (error.value) return error.value
34-
if (isConnecting.value) return t('connector.status.aria_connecting')
35-
if (isConnected.value) return t('connector.status.aria_connected')
36-
return t('connector.status.aria_click_to_connect')
32+
if (isConnecting.value) return $t('connector.status.aria_connecting')
33+
if (isConnected.value) return $t('connector.status.aria_connected')
34+
return $t('connector.status.aria_click_to_connect')
3735
})
3836
</script>
3937

@@ -62,7 +60,7 @@ const ariaLabel = computed(() => {
6260
<img
6361
v-if="isConnected && avatar"
6462
:src="avatar"
65-
:alt="t('connector.status.avatar_alt', { user: npmUser })"
63+
:alt="$t('connector.status.avatar_alt', { user: npmUser })"
6664
width="24"
6765
height="24"
6866
class="w-6 h-6 rounded-full"

app/components/HeaderOrgsDropdown.vue

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const props = defineProps<{
33
username: string
44
}>()
55
6-
const { t } = useI18n()
76
const { listUserOrgs } = useConnector()
87
98
const isOpen = ref(false)
@@ -23,11 +22,11 @@ async function loadOrgs() {
2322
// Already sorted alphabetically by server, take top 10
2423
orgs.value = orgList.slice(0, 10)
2524
} else {
26-
error.value = t('header.orgs_dropdown.error')
25+
error.value = $t('header.orgs_dropdown.error')
2726
}
2827
hasLoaded.value = true
2928
} catch {
30-
error.value = t('header.orgs_dropdown.error')
29+
error.value = $t('header.orgs_dropdown.error')
3130
} finally {
3231
isLoading.value = false
3332
}
@@ -62,7 +61,7 @@ function handleKeydown(event: KeyboardEvent) {
6261
:to="`/~${username}/orgs`"
6362
class="link-subtle font-mono text-sm inline-flex items-center gap-1"
6463
>
65-
{{ t('header.orgs') }}
64+
{{ $t('header.orgs') }}
6665
<span
6766
class="i-carbon-chevron-down w-3 h-3 transition-transform duration-200"
6867
:class="{ 'rotate-180': isOpen }"
@@ -80,16 +79,16 @@ function handleKeydown(event: KeyboardEvent) {
8079
<div class="bg-bg-elevated border border-border rounded-lg shadow-lg overflow-hidden">
8180
<div class="px-3 py-2 border-b border-border">
8281
<span class="font-mono text-xs text-fg-subtle">{{
83-
t('header.orgs_dropdown.title')
82+
$t('header.orgs_dropdown.title')
8483
}}</span>
8584
</div>
8685

8786
<div v-if="isLoading" class="px-3 py-4 text-center">
88-
<span class="text-fg-muted text-sm">{{ t('header.orgs_dropdown.loading') }}</span>
87+
<span class="text-fg-muted text-sm">{{ $t('header.orgs_dropdown.loading') }}</span>
8988
</div>
9089

9190
<div v-else-if="error" class="px-3 py-4 text-center">
92-
<span class="text-fg-muted text-sm">{{ t('header.orgs_dropdown.error') }}</span>
91+
<span class="text-fg-muted text-sm">{{ $t('header.orgs_dropdown.error') }}</span>
9392
</div>
9493

9594
<ul v-else-if="orgs.length > 0" class="py-1 max-h-80 overflow-y-auto">
@@ -104,15 +103,15 @@ function handleKeydown(event: KeyboardEvent) {
104103
</ul>
105104

106105
<div v-else class="px-3 py-4 text-center">
107-
<span class="text-fg-muted text-sm">{{ t('header.orgs_dropdown.empty') }}</span>
106+
<span class="text-fg-muted text-sm">{{ $t('header.orgs_dropdown.empty') }}</span>
108107
</div>
109108

110109
<div class="px-3 py-2 border-t border-border">
111110
<NuxtLink
112111
:to="`/~${username}/orgs`"
113112
class="link-subtle font-mono text-xs inline-flex items-center gap-1"
114113
>
115-
{{ t('header.orgs_dropdown.view_all') }}
114+
{{ $t('header.orgs_dropdown.view_all') }}
116115
<span class="i-carbon-arrow-right w-3 h-3" aria-hidden="true" />
117116
</NuxtLink>
118117
</div>

app/components/HeaderPackagesDropdown.vue

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const props = defineProps<{
33
username: string
44
}>()
55
6-
const { t } = useI18n()
76
const { listUserPackages } = useConnector()
87
98
const isOpen = ref(false)
@@ -23,11 +22,11 @@ async function loadPackages() {
2322
// Sort alphabetically and take top 10
2423
packages.value = Object.keys(pkgMap).sort().slice(0, 10)
2524
} else {
26-
error.value = t('header.packages_dropdown.error')
25+
error.value = $t('header.packages_dropdown.error')
2726
}
2827
hasLoaded.value = true
2928
} catch {
30-
error.value = t('header.packages_dropdown.error')
29+
error.value = $t('header.packages_dropdown.error')
3130
} finally {
3231
isLoading.value = false
3332
}
@@ -62,7 +61,7 @@ function handleKeydown(event: KeyboardEvent) {
6261
:to="`/~${username}`"
6362
class="link-subtle font-mono text-sm inline-flex items-center gap-1"
6463
>
65-
{{ t('header.packages') }}
64+
{{ $t('header.packages') }}
6665
<span
6766
class="i-carbon-chevron-down w-3 h-3 transition-transform duration-200"
6867
:class="{ 'rotate-180': isOpen }"
@@ -80,16 +79,16 @@ function handleKeydown(event: KeyboardEvent) {
8079
<div class="bg-bg-elevated border border-border rounded-lg shadow-lg overflow-hidden">
8180
<div class="px-3 py-2 border-b border-border">
8281
<span class="font-mono text-xs text-fg-subtle">{{
83-
t('header.packages_dropdown.title')
82+
$t('header.packages_dropdown.title')
8483
}}</span>
8584
</div>
8685

8786
<div v-if="isLoading" class="px-3 py-4 text-center">
88-
<span class="text-fg-muted text-sm">{{ t('header.packages_dropdown.loading') }}</span>
87+
<span class="text-fg-muted text-sm">{{ $t('header.packages_dropdown.loading') }}</span>
8988
</div>
9089

9190
<div v-else-if="error" class="px-3 py-4 text-center">
92-
<span class="text-fg-muted text-sm">{{ t('header.packages_dropdown.error') }}</span>
91+
<span class="text-fg-muted text-sm">{{ $t('header.packages_dropdown.error') }}</span>
9392
</div>
9493

9594
<ul v-else-if="packages.length > 0" class="py-1 max-h-80 overflow-y-auto">
@@ -104,15 +103,15 @@ function handleKeydown(event: KeyboardEvent) {
104103
</ul>
105104

106105
<div v-else class="px-3 py-4 text-center">
107-
<span class="text-fg-muted text-sm">{{ t('header.packages_dropdown.empty') }}</span>
106+
<span class="text-fg-muted text-sm">{{ $t('header.packages_dropdown.empty') }}</span>
108107
</div>
109108

110109
<div class="px-3 py-2 border-t border-border">
111110
<NuxtLink
112111
:to="`/~${username}`"
113112
class="link-subtle font-mono text-xs inline-flex items-center gap-1"
114113
>
115-
{{ t('header.packages_dropdown.view_all') }}
114+
{{ $t('header.packages_dropdown.view_all') }}
116115
<span class="i-carbon-arrow-right w-3 h-3" aria-hidden="true" />
117116
</NuxtLink>
118117
</div>

0 commit comments

Comments
 (0)