Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c4776e5
chore: add i18n redirect (#2290)
danielroe Mar 27, 2026
bfc0917
fix(docs): update links to community projects in README.md (#2300)
trueberryless Mar 27, 2026
9dc8512
fix(i18n): add missing russian translations (#2302)
Ibochkarev Mar 28, 2026
18db337
docs(ui): add stories for Accessibility page (#2301)
cylewaitforit Mar 28, 2026
e62baf4
chore: bump vue-data-ui from 3.16.5 to 3.17.1 (#2304)
graphieros Mar 28, 2026
d166ce1
feat: add CSP and some security headers to HTML pages (#2075)
serhalp Mar 28, 2026
7688cd7
fix(docs): replace redirect extension with new one everywhere (#2311)
trueberryless Mar 29, 2026
db6411e
fix: handle scoped packages named v (#2280)
trivikr Mar 29, 2026
e2a7b59
feat(i18n): add missing ar-EG and fr-FR translation keys (#2315)
hamdibenjarrar Mar 29, 2026
b3da028
fix: typo in chart config attribute (#2328)
graphieros Mar 30, 2026
516808b
fix(ui): allow custom badge values in docs generator (#2335)
MathurAditya724 Mar 30, 2026
a54def4
chore(deps): update codecov/codecov-action action to v6 (#2323)
renovate[bot] Mar 31, 2026
934181b
chore(deps): update devdependency @vitest/coverage-v8 to v4.1.2 (#2322)
renovate[bot] Mar 31, 2026
5f20290
feat(i18n): update Indonesian localization (#2334)
alfonsusac Mar 31, 2026
2e0fb05
docs: update CONTRIBUTING i18n and lint docs (#2336)
alfonsusac Mar 31, 2026
b0d97ff
feat: add kawai logo in npmx.dev homepage (#2346)
alfonsusac Mar 31, 2026
f4061b3
fix(ui): flaw in previous version of the trans version (#2349)
alfonsusac Mar 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,14 @@ jobs:
run: pnpm vp test --project nuxt --coverage --reporter=default --reporter=junit --outputFile=test-report.junit.xml

- name: ⬆︎ Upload coverage reports to Codecov
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6
with:
report_type: test_results
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

- name: ⬆︎ Upload coverage reports to Codecov
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

Expand Down
2 changes: 1 addition & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const config = {
'storybook-i18n',
],
framework: '@storybook-vue/nuxt',
staticDirs: ['./.public'],
staticDirs: ['./.public', { from: '../public', to: '/' }],
features: {
backgrounds: false,
},
Expand Down
57 changes: 57 additions & 0 deletions .storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,61 @@
<style>
/* Load Geist fonts to match the production app (normally handled by @nuxt/fonts,
which is disabled in Storybook at this time) */
@font-face {
font-family: 'Geist';
font-weight: 400;
font-style: normal;
font-display: swap;
src: url('/fonts/Geist-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Geist';
font-weight: 500;
font-style: normal;
font-display: swap;
src: url('/fonts/Geist-Medium.ttf') format('truetype');
}
@font-face {
font-family: 'Geist';
font-weight: 600;
font-style: normal;
font-display: swap;
src: url('/fonts/Geist-SemiBold.ttf') format('truetype');
}
@font-face {
font-family: 'Geist';
font-weight: 700;
font-style: normal;
font-display: swap;
src: url('/fonts/Geist-Bold.ttf') format('truetype');
}
@font-face {
font-family: 'Geist Mono';
font-weight: 400;
font-style: normal;
font-display: swap;
src: url('/fonts/GeistMono-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Geist Mono';
font-weight: 500;
font-style: normal;
font-display: swap;
src: url('/fonts/GeistMono-Medium.ttf') format('truetype');
}
@font-face {
font-family: 'Geist Mono';
font-weight: 700;
font-style: normal;
font-display: swap;
src: url('/fonts/GeistMono-Bold.ttf') format('truetype');
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}

/* Override docs story canvas background to match npmx theme */
.docs-story {
background-color: var(--bg, oklch(0.171 0 0)) !important;
Expand Down
20 changes: 10 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pnpm npmx-connector # Start the real connector (requires npm login)
pnpm mock-connector # Start the mock connector (no npm login needed)

# Code Quality
pnpm lint # Run linter (oxlint + oxfmt)
pnpm vp run lint # Run linter (oxlint + oxfmt)
pnpm lint:fix # Auto-fix lint issues
pnpm test:types # TypeScript type checking

Expand Down Expand Up @@ -460,13 +460,13 @@ npmx.dev uses [@nuxtjs/i18n](https://i18n.nuxtjs.org/) for internationalization.

The following scripts help manage translation files. `en.json` is the reference locale.

| Command | Description |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pnpm i18n:check [locale]` | Compares `en.json` with other locale files. Shows missing and extra keys. Optionally filter output by locale (e.g. `pnpm i18n:check ja-JP`). |
| `pnpm i18n:check:fix [locale]` | Same as check, but adds missing keys to other locales with English placeholders. |
| `pnpm i18n:report` | Audits translation keys against code usage in `.vue` and `.ts` files. Reports missing keys (used in code but not in locale), unused keys (in locale but not in code), and dynamic keys. |
| `pnpm i18n:report:fix` | Removes unused keys from `en.json` and all other locale files. |
| `pnpm i18n:schema` | Generates a JSON Schema from `en.json` at `i18n/schema.json`. Locale files reference this schema for IDE validation and autocompletion. |
| Command | Description |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pnpm i18n:check:fix [locale]` | Compares `en.json` with other locale files and adds missing keys with English placeholders. Optionally filter output by locale (e.g. `pnpm i18n:check:fix ja-JP`). |
| `pnpm i18n:report:fix` | Removes unused keys from `en.json` and all other locale files. |
| `pnpm vp run i18n:check [locale]` | Same as check:fix, but only show missing and extra keys. |
| `pnpm vp run i18n:report` | Audits translation keys against code usage in `.vue` and `.ts` files. Reports missing keys (used in code but not in locale), unused keys (in locale but not in code), and dynamic keys. |
| `pnpm vp run i18n:schema` | Generates a JSON Schema from `en.json` at `i18n/schema.json`. Locale files reference this schema for IDE validation and autocompletion. |

### Adding a new locale

Expand Down Expand Up @@ -502,7 +502,7 @@ Check [Pluralization rule callback](https://vue-i18n.intlify.dev/guide/essential
We track the current progress of translations with [Lunaria](https://lunaria.dev/) on this site: https://i18n.npmx.dev/
If you see any outdated translations in your language, feel free to update the keys to match the English version.

Use `pnpm i18n:check` and `pnpm i18n:check:fix` to verify and fix your locale (see [i18n commands](#i18n-commands) above for details).
Use `pnpm i18n:check:fix` to fix your locale (see [i18n commands](#i18n-commands) above for details).

#### Country variants (advanced)

Expand Down Expand Up @@ -590,7 +590,7 @@ See how `es`, `es-ES`, and `es-419` are configured in [config/i18n.ts](./config/
- Use `common.*` for shared strings (loading, retry, close, etc.)
- Use component-specific prefixes: `package.card.*`, `settings.*`, `nav.*`
- Do not use dashes (`-`) in translation keys; always use underscore (`_`): e.g., `privacy_policy` instead of `privacy-policy`
- **Always use static string literals as translation keys.** Our i18n scripts (`pnpm i18n:report`) rely on static analysis to detect unused and missing keys. Dynamic keys cannot be analyzed and will be flagged as errors.
- **Always use static string literals as translation keys.** Our i18n scripts (`pnpm i18n:report:fix`) rely on static analysis to detect unused and missing keys. Dynamic keys cannot be analyzed and will be flagged as errors.

**Bad:**

Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ npmx.dev supports npm permalinks &ndash; just replace `npmjs.com` with `npmx.dev
| `npmjs.com/org/nuxt` | [`npmx.dev/org/nuxt`](https://npmx.dev/org/nuxt) |

> [!TIP]
> Want automatic redirects? Try the [npmx-replace browser extension](https://github.com/tylersayshi/npmx-replace-extension) (Chrome only for now) or the separate [npmx-redirect extension](https://github.com/iaverages/npmx-redirect) for [Chrome](https://chromewebstore.google.com/detail/lbhjgfgpnlihfmobnohoipeljollhlnb) / [Firefox](https://addons.mozilla.org/en-GB/firefox/addon/npmx-redirect/).
> Want automatic redirects? Try the [npmx-redirect extension](https://github.com/iaverages/npmx-redirect) for [Chrome](https://chromewebstore.google.com/detail/lbhjgfgpnlihfmobnohoipeljollhlnb) / [Firefox](https://addons.mozilla.org/en-GB/firefox/addon/npmx-redirect/).

#### Not yet supported

Expand Down Expand Up @@ -148,7 +148,7 @@ We welcome contributions &ndash; please do feel free to explore the project and

## Related projects

- [npmx-replace-extension](https://github.com/tylersayshi/npmx-replace-extension) &ndash; Browser extension to redirect npmjs.com to npmx.dev (Chrome only for now)
- [npmx-redirect](https://github.com/iaverages/npmx-redirect) &ndash; Browser extension that automatically redirects npmjs.com URLs to npmx.dev.
- [JSR](https://jsr.io/) &ndash; The open-source package registry for modern JavaScript and TypeScript
- [npm-userscript](https://github.com/bluwy/npm-userscript) &ndash; Browser userscript with various improvements and fixes for npmjs.com
- [npm-alt](https://npm.willow.sh/) &ndash; An alternative npm package browser
Expand All @@ -158,7 +158,6 @@ We welcome contributions &ndash; please do feel free to explore the project and
- [nxjt](https://nxjt.netlify.app) &ndash; npmx Jump To: Quickly navigate to npmx common webpages.
- [npmx-weekly](https://npmx-weekly.trueberryless.org/) &ndash; A weekly newsletter for the npmx ecosystem. Add your own content via suggestions in the weekly PR on [GitHub](https://github.com/trueberryless-org/npmx-weekly/pulls?q=is%3Aopen+is%3Apr+label%3A%22%F0%9F%95%94+weekly+post%22).
- [npmx-digest](https://npmx-digest.trueberryless.org/) &ndash; An automated news aggregation website that summarizes npmx activity from GitHub and Bluesky every 8 hours.
- [npmx-redirect](https://github.com/iaverages/npmx-redirect) &ndash; Browser extension that automatically redirects npmjs.com URLs to npmx.dev.
- [npmx-badge](https://npmx-badge.vercel.app/) &ndash; A playground to help you create custom badges quickly.

If you're building something cool, let us know! 🙏
Expand Down
2 changes: 1 addition & 1 deletion app/components/Compare/FacetBarChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
csv: false,
altCopy: true,
},
buttonTitle: {
buttonTitles: {
img: $t('package.trends.download_file', { fileType: 'PNG' }),
svg: $t('package.trends.download_file', { fileType: 'SVG' }),
altCopy: $t('package.trends.copy_alt.button_label'),
Expand Down
76 changes: 76 additions & 0 deletions app/components/LandingLogo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<script setup lang="ts">
defineProps<{
class?: string
}>()

const { env } = useAppConfig().buildInfo

onPrehydrate(el => {
const isKawaii = new URLSearchParams(window.location.search).has('kawaii')
const date = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/Los_Angeles',
month: '2-digit',
day: '2-digit',
}).format(new Date())
const isAprilFirst = date === '04/01'

if (!isKawaii && !isAprilFirst) return

const normalLogo = el.querySelector<HTMLElement>('#npmx-index-h1-logo-normal')
const kawaiiLogo = el.querySelector<HTMLElement>('#npmx-index-h1-logo-kawaii')
const tkawaiiLogo = el.querySelector<HTMLElement>('#npmx-index-h1-logo-tkawaii')
const logoEnv = el.querySelector<HTMLElement>('#npmx-index-h1-logo-env')
const logoTagline = el.querySelector<HTMLElement>('#npmx-index-tagline')

if (!normalLogo || !kawaiiLogo || !tkawaiiLogo || !logoEnv || !logoTagline) return

if (isAprilFirst) {
tkawaiiLogo.style.display = 'block'
} else {
kawaiiLogo.style.display = 'block'
}
normalLogo.style.display = 'none'
logoEnv.style.display = 'none'
logoTagline.style.display = 'none'
})
</script>

<template>
<div class="flex flex-col items-center justify-center">
<h1
dir="ltr"
class="relative flex items-center justify-center gap-2 header-logo font-mono text-5xl sm:text-7xl md:text-8xl font-medium tracking-tight mb-6 motion-safe:animate-fade-in motion-safe:animate-fill-both"
>
<img
id="npmx-index-h1-logo-kawaii"
width="400"
class="hidden mb-8 motion-safe:animate-fade-in motion-safe:animate-scale-in motion-safe:hover:scale-105 motion-safe:transition w-80 sm:w-100"
src="/extra/npmx-cute.svg"
:alt="$t('alt_logo_kawaii')"
:class="class"
/>
<img
id="npmx-index-h1-logo-tkawaii"
width="400"
class="hidden mb-8 motion-safe:animate-fade-in motion-safe:animate-scale-in motion-safe:hover:scale-105 motion-safe:transition w-80 sm:w-100"
src="/extra/npmx-cute-transgender.svg"
:alt="$t('alt_logo_kawaii')"
:class="class"
/>
<AppLogo id="npmx-index-h1-logo-normal" :class="class" />
<span
id="npmx-index-h1-logo-env"
aria-hidden="true"
class="text-sm sm:text-base md:text-lg transform-origin-br font-mono tracking-widest text-accent absolute -bottom-4 -inset-ie-1.5"
>
{{ env === 'release' ? 'alpha' : env }}
</span>
</h1>
<p
id="npmx-index-tagline"
class="text-fg-muted text-lg sm:text-xl max-w-xl mb-12 lg:mb-14 motion-safe:animate-slide-up motion-safe:animate-fill-both delay-100"
>
{{ $t('tagline') }}
</p>
</div>
</template>
12 changes: 6 additions & 6 deletions app/composables/useRepoMeta.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ProviderId, RepoRef } from '#shared/utils/git-providers'
import { parseRepoUrl, GITLAB_HOSTS } from '#shared/utils/git-providers'
import { GIT_PROVIDER_API_ORIGINS, parseRepoUrl, GITLAB_HOSTS } from '#shared/utils/git-providers'

// TTL for git repo metadata (10 minutes - repo stats don't change frequently)
const REPO_META_TTL = 60 * 10
Expand Down Expand Up @@ -134,7 +134,7 @@ const githubAdapter: ProviderAdapter = {
let res: UnghRepoResponse | null = null
try {
const { data } = await cachedFetch<UnghRepoResponse>(
`https://ungh.cc/repos/${ref.owner}/${ref.repo}`,
`${GIT_PROVIDER_API_ORIGINS.github}/repos/${ref.owner}/${ref.repo}`,
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
UNGH_REPO_META_TTL,
)
Expand Down Expand Up @@ -256,7 +256,7 @@ const bitbucketAdapter: ProviderAdapter = {
let res: BitbucketRepoResponse | null = null
try {
const { data } = await cachedFetch<BitbucketRepoResponse>(
`https://api.bitbucket.org/2.0/repositories/${ref.owner}/${ref.repo}`,
`${GIT_PROVIDER_API_ORIGINS.bitbucket}/2.0/repositories/${ref.owner}/${ref.repo}`,
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
REPO_META_TTL,
)
Expand Down Expand Up @@ -314,7 +314,7 @@ const codebergAdapter: ProviderAdapter = {
let res: GiteaRepoResponse | null = null
try {
const { data } = await cachedFetch<GiteaRepoResponse>(
`https://codeberg.org/api/v1/repos/${ref.owner}/${ref.repo}`,
`${GIT_PROVIDER_API_ORIGINS.codeberg}/api/v1/repos/${ref.owner}/${ref.repo}`,
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
REPO_META_TTL,
)
Expand Down Expand Up @@ -372,7 +372,7 @@ const giteeAdapter: ProviderAdapter = {
let res: GiteeRepoResponse | null = null
try {
const { data } = await cachedFetch<GiteeRepoResponse>(
`https://gitee.com/api/v5/repos/${ref.owner}/${ref.repo}`,
`${GIT_PROVIDER_API_ORIGINS.gitee}/api/v5/repos/${ref.owner}/${ref.repo}`,
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
REPO_META_TTL,
)
Expand Down Expand Up @@ -625,7 +625,7 @@ const radicleAdapter: ProviderAdapter = {
let res: RadicleProjectResponse | null = null
try {
const { data } = await cachedFetch<RadicleProjectResponse>(
`https://seed.radicle.at/api/v1/projects/${ref.repo}`,
`${GIT_PROVIDER_API_ORIGINS.radicle}/api/v1/projects/${ref.repo}`,
{ headers: { 'User-Agent': 'npmx', ...options.headers }, ...options },
REPO_META_TTL,
)
Expand Down
30 changes: 30 additions & 0 deletions app/pages/accessibility.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Accessibility from './accessibility.vue'
import type { Meta, StoryObj } from '@storybook-vue/nuxt'
import AppHeader from '~/components/AppHeader.vue'
import AppFooter from '~/components/AppFooter.vue'

const meta = {
component: Accessibility,
parameters: {
layout: 'fullscreen',
},
decorators: [
() => ({
components: { AppHeader, AppFooter },
template: `
<div class="min-h-screen flex flex-col bg-bg text-fg">
<AppHeader :show-logo="true" />
<div id="main-content" class="flex-1 flex flex-col" tabindex="-1">
<story />
</div>
<AppFooter />
</div>
`,
}),
],
} satisfies Meta<typeof Accessibility>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {}
22 changes: 1 addition & 21 deletions app/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ async function search() {
startSearch()
}
const { env } = useAppConfig().buildInfo
useSeoMeta({
title: () => $t('seo.home.title'),
ogTitle: () => $t('seo.home.title'),
Expand All @@ -32,25 +30,7 @@ defineOgImageComponent('Default', {
<header
class="flex-1 flex flex-col items-center justify-center text-center pt-20 pb-4 md:pb-8 lg:pb-20"
>
<h1
dir="ltr"
class="relative flex items-center justify-center gap-2 header-logo font-mono text-5xl sm:text-7xl md:text-8xl font-medium tracking-tight mb-6 motion-safe:animate-fade-in motion-safe:animate-fill-both"
>
<AppLogo class="w-42 h-auto sm:w-58 md:w-70" />
<span
aria-hidden="true"
class="text-sm sm:text-base md:text-lg transform-origin-br font-mono tracking-widest text-accent absolute -bottom-4 -inset-ie-1.5"
>
{{ env === 'release' ? 'alpha' : env }}
</span>
</h1>

<p
class="text-fg-muted text-lg sm:text-xl max-w-xl mb-12 lg:mb-14 motion-safe:animate-slide-up motion-safe:animate-fill-both"
style="animation-delay: 0.1s"
>
{{ $t('tagline') }}
</p>
<LandingLogo class="w-42 h-auto sm:w-58 md:w-70" />
<search
class="w-full max-w-2xl motion-safe:animate-slide-up motion-safe:animate-fill-both"
style="animation-delay: 0.2s"
Expand Down
2 changes: 1 addition & 1 deletion docs/content/1.getting-started/2.quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,5 @@ Replace `npmjs.com` with `npmx.dev` in any npm URL:
| `npmjs.com/~sindresorhus` | `npmx.dev/~sindresorhus` |

::tip
Install the [npmx-replace browser extension](https://github.com/tylersayshi/npmx-replace-extension) for automatic redirects from npmjs.com.
Install the [npmx-redirect extension](https://github.com/iaverages/npmx-redirect) for automatic redirects from npmjs.com.
::
2 changes: 1 addition & 1 deletion docs/content/2.guide/3.url-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Replace `npmjs.com` with `npmx.dev` (or `xnpmjs.com`) in any npm URL:
| `npmjs.com/org/nuxt` | [`npmx.dev/org/nuxt`](https://npmx.dev/org/nuxt) |

::tip
Install the [npmx-replace browser extension](https://github.com/tylersayshi/npmx-replace-extension) for automatic redirects.
Install the [npmx-redirect extension](https://github.com/iaverages/npmx-redirect) for automatic redirects from npmjs.com.
::

## Use simpler URLs
Expand Down
Loading
Loading