Skip to content

Commit 7944116

Browse files
committed
Merge origin/main into vt/feat-exec
2 parents 38cace3 + 4232ae2 commit 7944116

35 files changed

Lines changed: 1334 additions & 396 deletions

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/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>

app/components/LoadingSpinner.vue

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ defineProps<{
33
/** Text to display next to the spinner */
44
text?: string
55
}>()
6-
7-
const { t } = useI18n()
86
</script>
97

108
<template>
119
<div aria-busy="true" class="flex items-center gap-3 text-fg-muted font-mono text-sm py-8">
1210
<span
1311
class="w-4 h-4 border-2 border-fg-subtle border-t-fg rounded-full motion-safe:animate-spin"
1412
/>
15-
{{ text ?? t('common.loading') }}
13+
{{ text ?? $t('common.loading') }}
1614
</div>
1715
</template>

app/components/OrgMembersPanel.vue

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ const emit = defineEmits<{
1010
'select-team': [teamName: string]
1111
}>()
1212
13-
const { t } = useI18n()
14-
1513
const {
1614
isConnected,
1715
lastExecutionTime,
@@ -347,7 +345,7 @@ watch(lastExecutionTime, () => {
347345
:aria-pressed="filterRole === role"
348346
@click="filterRole = role"
349347
>
350-
{{ t(`org.members.role.${role}`) }}
348+
{{ $t(`org.members.role.${role}`) }}
351349
<span v-if="role !== 'all'" class="text-fg-subtle">({{ roleCounts[role] }})</span>
352350
</button>
353351
</div>
@@ -465,9 +463,9 @@ watch(lastExecutionTime, () => {
465463
)
466464
"
467465
>
468-
<option value="developer">{{ t('org.members.role.developer') }}</option>
469-
<option value="admin">{{ t('org.members.role.admin') }}</option>
470-
<option value="owner">{{ t('org.members.role.owner') }}</option>
466+
<option value="developer">{{ $t('org.members.role.developer') }}</option>
467+
<option value="admin">{{ $t('org.members.role.admin') }}</option>
468+
<option value="owner">{{ $t('org.members.role.owner') }}</option>
471469
</select>
472470
<!-- Remove button -->
473471
<button
@@ -526,9 +524,9 @@ watch(lastExecutionTime, () => {
526524
name="new-member-role"
527525
class="flex-1 px-2 py-1.5 font-mono text-sm bg-bg border border-border rounded text-fg transition-colors duration-200 focus:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
528526
>
529-
<option value="developer">{{ t('org.members.role.developer') }}</option>
530-
<option value="admin">{{ t('org.members.role.admin') }}</option>
531-
<option value="owner">{{ t('org.members.role.owner') }}</option>
527+
<option value="developer">{{ $t('org.members.role.developer') }}</option>
528+
<option value="admin">{{ $t('org.members.role.admin') }}</option>
529+
<option value="owner">{{ $t('org.members.role.owner') }}</option>
532530
</select>
533531
<!-- Team selection -->
534532
<label for="new-member-team" class="sr-only">{{ $t('org.members.team_label') }}</label>

app/components/PackageDownloadAnalytics.vue

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import type { VueUiXyDatasetItem } from 'vue-data-ui'
44
import { VueUiXy } from 'vue-data-ui/vue-ui-xy'
55
import { useDebounceFn } from '@vueuse/core'
66
7-
const { t } = useI18n()
8-
97
const {
108
weeklyDownloads,
119
inModal = false,
@@ -398,7 +396,7 @@ const config = computed(() => ({
398396
grid: {
399397
labels: {
400398
axis: {
401-
yLabel: t('package.downloads.y_axis_label', { granularity: selectedGranularity.value }),
399+
yLabel: $t('package.downloads.y_axis_label', { granularity: selectedGranularity.value }),
402400
xLabel: packageName,
403401
yLabelOffsetX: 12,
404402
fontSize: 24,
@@ -469,7 +467,7 @@ const config = computed(() => ({
469467
for="granularity"
470468
class="text-[10px] font-mono text-fg-subtle tracking-wide uppercase"
471469
>
472-
{{ t('package.downloads.granularity') }}
470+
{{ $t('package.downloads.granularity') }}
473471
</label>
474472

475473
<div
@@ -480,10 +478,10 @@ const config = computed(() => ({
480478
v-model="selectedGranularity"
481479
class="w-full bg-transparent font-mono text-sm text-fg outline-none"
482480
>
483-
<option value="daily">{{ t('package.downloads.granularity_daily') }}</option>
484-
<option value="weekly">{{ t('package.downloads.granularity_weekly') }}</option>
485-
<option value="monthly">{{ t('package.downloads.granularity_monthly') }}</option>
486-
<option value="yearly">{{ t('package.downloads.granularity_yearly') }}</option>
481+
<option value="daily">{{ $t('package.downloads.granularity_daily') }}</option>
482+
<option value="weekly">{{ $t('package.downloads.granularity_weekly') }}</option>
483+
<option value="monthly">{{ $t('package.downloads.granularity_monthly') }}</option>
484+
<option value="yearly">{{ $t('package.downloads.granularity_yearly') }}</option>
487485
</select>
488486
</div>
489487
</div>
@@ -495,7 +493,7 @@ const config = computed(() => ({
495493
for="startDate"
496494
class="text-[10px] font-mono text-fg-subtle tracking-wide uppercase"
497495
>
498-
{{ t('package.downloads.start_date') }}
496+
{{ $t('package.downloads.start_date') }}
499497
</label>
500498
<div
501499
class="flex items-center gap-2 px-2.5 py-1.75 bg-bg-subtle border border-border rounded-md focus-within:(border-border-hover ring-2 ring-fg/50)"
@@ -515,7 +513,7 @@ const config = computed(() => ({
515513
for="endDate"
516514
class="text-[10px] font-mono text-fg-subtle tracking-wide uppercase"
517515
>
518-
{{ t('package.downloads.end_date') }}
516+
{{ $t('package.downloads.end_date') }}
519517
</label>
520518
<div
521519
class="flex items-center gap-2 px-2.5 py-1.75 bg-bg-subtle border border-border rounded-md focus-within:(border-border-hover ring-2 ring-fg/50)"
@@ -636,7 +634,7 @@ const config = computed(() => ({
636634
v-if="inModal && !chartData.dataset && !pending"
637635
class="min-h-[260px] flex items-center justify-center text-fg-subtle font-mono text-sm"
638636
>
639-
{{ t('package.downloads.no_data') }}
637+
{{ $t('package.downloads.no_data') }}
640638
</div>
641639

642640
<div
@@ -645,7 +643,7 @@ const config = computed(() => ({
645643
aria-live="polite"
646644
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-xs text-fg-subtle font-mono bg-bg/70 backdrop-blur px-3 py-2 rounded-md border border-border"
647645
>
648-
{{ t('package.downloads.loading') }}
646+
{{ $t('package.downloads.loading') }}
649647
</div>
650648
</div>
651649
</template>

app/components/PackageInstallScripts.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ const props = defineProps<{
88
}
99
}>()
1010
11-
const { t } = useI18n()
12-
1311
const outdatedNpxDeps = useOutdatedDependencies(() => props.installScripts.npxDependencies)
1412
const hasNpxDeps = computed(() => Object.keys(props.installScripts.npxDependencies).length > 0)
1513
const sortedNpxDeps = computed(() => {
@@ -58,7 +56,7 @@ const isExpanded = shallowRef(false)
5856
aria-hidden="true"
5957
/>
6058
{{
61-
t(
59+
$t(
6260
'package.install_scripts.npx_packages',
6361
{ count: sortedNpxDeps.length },
6462
sortedNpxDeps.length,
@@ -101,7 +99,7 @@ const isExpanded = shallowRef(false)
10199
:title="
102100
outdatedNpxDeps[dep]
103101
? outdatedNpxDeps[dep].resolved === outdatedNpxDeps[dep].latest
104-
? t('package.install_scripts.currently', {
102+
? $t('package.install_scripts.currently', {
105103
version: outdatedNpxDeps[dep].latest,
106104
})
107105
: getOutdatedTooltip(outdatedNpxDeps[dep])

0 commit comments

Comments
 (0)