Skip to content

Commit 16bbc12

Browse files
committed
Merge branch 'main' into feat/org-packages-fancy
2 parents 65b14fb + bb5e5c1 commit 16bbc12

13 files changed

Lines changed: 485 additions & 21 deletions

File tree

app/components/PackageDownloadAnalytics.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,12 @@ const config = computed(() => {
467467
table: false,
468468
tooltip: false,
469469
},
470+
buttonTitles: {
471+
csv: $t('package.downloads.download_file', { fileType: 'CSV' }),
472+
img: $t('package.downloads.download_file', { fileType: 'PNG' }),
473+
svg: $t('package.downloads.download_file', { fileType: 'SVG' }),
474+
annotator: $t('package.downloads.toggle_annotator'),
475+
},
470476
callbacks: {
471477
img: ({ imageUri }: { imageUri: string }) => {
472478
loadFile(

app/composables/useSettings.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ export interface AppSettings {
1414
includeTypesInInstall: boolean
1515
/** Accent color theme */
1616
accentColorId: AccentColorId | null
17+
/** Hide platform-specific packages (e.g., @scope/pkg-linux-x64) from search results */
18+
hidePlatformPackages: boolean
1719
}
1820

1921
const DEFAULT_SETTINGS: AppSettings = {
2022
relativeDates: false,
2123
includeTypesInInstall: true,
2224
accentColorId: null,
25+
hidePlatformPackages: true,
2326
}
2427

2528
const STORAGE_KEY = 'npmx-settings'

app/pages/[...package].vue

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -820,14 +820,6 @@ defineOgImageComponent('Package', {
820820
</dl>
821821
</header>
822822

823-
<!-- Security vulnerabilities warning -->
824-
<PackageVulnerabilities
825-
v-if="displayVersion"
826-
:package-name="pkg.name"
827-
:version="displayVersion.version"
828-
class="area-vulns"
829-
/>
830-
831823
<!-- Binary-only packages: Show only execute command (no install) -->
832824
<section v-if="isBinaryOnly" aria-labelledby="run-heading" class="area-install scroll-mt-20">
833825
<div class="flex flex-wrap items-center justify-between mb-3">

app/pages/search.vue

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { formatNumber } from '#imports'
33
import type { FilterChip, SortOption } from '#shared/types/preferences'
44
import { debounce } from 'perfect-debounce'
55
import { isValidNewPackageName, checkPackageExists } from '~/utils/package-name'
6+
import { isPlatformSpecificPackage } from '~/utils/platform-packages'
67
78
const route = useRoute()
89
const router = useRouter()
@@ -131,22 +132,37 @@ const rawVisibleResults = computed(() => {
131132
return results.value
132133
})
133134
135+
// Settings for platform package filtering
136+
const { settings } = useSettings()
137+
134138
/**
135-
* Reorder results to put exact package name match at the top
139+
* Reorder results to put exact package name match at the top,
140+
* and optionally filter out platform-specific packages.
136141
*/
137142
const visibleResults = computed(() => {
138143
const raw = rawVisibleResults.value
139144
if (!raw) return raw
140145
146+
let objects = raw.objects
147+
148+
// Filter out platform-specific packages if setting is enabled
149+
if (settings.value.hidePlatformPackages) {
150+
objects = objects.filter(r => !isPlatformSpecificPackage(r.package.name))
151+
}
152+
141153
const q = query.value.trim().toLowerCase()
142-
if (!q) return raw
154+
if (!q) {
155+
return objects === raw.objects ? raw : { ...raw, objects }
156+
}
143157
144158
// Find exact match index
145-
const exactIdx = raw.objects.findIndex(r => r.package.name.toLowerCase() === q)
146-
if (exactIdx <= 0) return raw // Already at top or not found
159+
const exactIdx = objects.findIndex(r => r.package.name.toLowerCase() === q)
160+
if (exactIdx <= 0) {
161+
return objects === raw.objects ? raw : { ...raw, objects }
162+
}
147163
148164
// Move exact match to top
149-
const reordered = [...raw.objects]
165+
const reordered = [...objects]
150166
const [exactMatch] = reordered.splice(exactIdx, 1)
151167
if (exactMatch) {
152168
reordered.unshift(exactMatch)

app/pages/settings.vue

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,43 @@ defineOgImageComponent('Default', {
163163
{{ $t('settings.include_types_description') }}
164164
</p>
165165
</div>
166+
167+
<!-- Divider -->
168+
<div class="border-t border-border" />
169+
170+
<!-- Hide platform-specific packages toggle -->
171+
<div class="space-y-2">
172+
<button
173+
type="button"
174+
class="w-full flex items-center justify-between gap-4 group"
175+
role="switch"
176+
:aria-checked="settings.hidePlatformPackages"
177+
@click="settings.hidePlatformPackages = !settings.hidePlatformPackages"
178+
>
179+
<span class="text-sm text-fg font-medium text-left">
180+
{{ $t('settings.hide_platform_packages') }}
181+
</span>
182+
<span
183+
class="relative inline-flex h-6 w-11 shrink-0 items-center rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out motion-reduce:transition-none shadow-sm cursor-pointer"
184+
:class="
185+
settings.hidePlatformPackages ? 'bg-accent' : 'bg-bg border border-border'
186+
"
187+
aria-hidden="true"
188+
>
189+
<span
190+
class="pointer-events-none inline-block h-5 w-5 rounded-full shadow-sm ring-0 transition-transform duration-200 ease-in-out motion-reduce:transition-none"
191+
:class="
192+
settings.hidePlatformPackages
193+
? 'translate-x-5 bg-bg'
194+
: 'translate-x-0 bg-fg-muted'
195+
"
196+
/>
197+
</span>
198+
</button>
199+
<p class="text-sm text-fg-muted">
200+
{{ $t('settings.hide_platform_packages_description') }}
201+
</p>
202+
</div>
166203
</div>
167204
</section>
168205

app/utils/platform-packages.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* Detects if a package name is a platform-specific native binary package.
3+
* These are typically optional dependencies that contain native binaries
4+
* for specific OS/architecture combinations (e.g., @oxlint/win32-x64, esbuild-darwin-arm64).
5+
* Sourced from searches for esbuild, and the napi-rs build triplets support matrix.
6+
*/
7+
8+
const PLATFORMS = new Set([
9+
'win32',
10+
'darwin',
11+
'linux',
12+
'android',
13+
'freebsd',
14+
'openbsd',
15+
'netbsd',
16+
'sunos',
17+
'aix',
18+
])
19+
20+
const ARCHITECTURES = new Set([
21+
'x64',
22+
'arm64',
23+
'arm',
24+
'ia32',
25+
'ppc64',
26+
'ppc64le',
27+
's390x',
28+
'riscv64',
29+
'mips64el',
30+
'loong64',
31+
])
32+
33+
const ABI_SUFFIXES = new Set(['gnu', 'musl', 'msvc', 'gnueabihf'])
34+
35+
/**
36+
* Checks if a package name is a platform-specific native binary package.
37+
* Matches patterns like:
38+
* - @scope/pkg-win32-x64
39+
* - @scope/pkg-linux-arm64-gnu
40+
* - pkg-darwin-arm64
41+
* - @rollup/rollup-linux-x64-musl
42+
*
43+
* @param name - The full package name (including scope if present)
44+
* @returns true if the package appears to be a platform-specific binary
45+
*/
46+
export function isPlatformSpecificPackage(name: string): boolean {
47+
const unscopedName = name.startsWith('@') ? (name.split('/')[1] ?? '') : name
48+
if (!unscopedName) return false
49+
50+
const parts = unscopedName.split('-')
51+
if (parts.length < 2) return false
52+
53+
// Look for OS-arch pattern anywhere in the name as suffix parts
54+
// e.g., "pkg-linux-x64-gnu" -> ["pkg", "linux", "x64", "gnu"]
55+
for (let i = 0; i < parts.length - 1; i++) {
56+
const os = parts[i]
57+
const arch = parts[i + 1]
58+
59+
if (os && arch && PLATFORMS.has(os) && ARCHITECTURES.has(arch)) {
60+
// Optional ABI suffix check (next part if exists)
61+
const abiSuffix = parts[i + 2]
62+
if (abiSuffix && !ABI_SUFFIXES.has(abiSuffix)) {
63+
// NOTE: Has an extra part after arch but it's not a known ABI - might be a false positive??
64+
// but still consider it a match if OS+arch pattern is found at the end
65+
if (i + 2 === parts.length - 1) {
66+
// Extra unknown suffix at the end - be conservative
67+
continue
68+
}
69+
}
70+
return true
71+
}
72+
}
73+
74+
return false
75+
}

i18n/locales/de-DE.json

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
"non_affiliation_disclaimer": "nicht verbunden mit npm, Inc.",
1010
"trademark_disclaimer": "npm ist eine eingetragene Marke von npm, Inc. Diese Seite ist nicht mit npm, Inc. verbunden.",
1111
"footer": {
12+
"site_footer": "Fusszeile",
1213
"about": "über uns",
1314
"docs": "Doku",
1415
"source": "Quellcode",
1516
"social": "Social",
1617
"chat": "Chat"
1718
},
1819
"search": {
20+
"results": "Suchergebnisse",
1921
"label": "npm-Pakete durchsuchen",
2022
"placeholder": "Pakete suchen...",
2123
"button": "Suchen",
@@ -38,22 +40,46 @@
3840
}
3941
},
4042
"nav": {
43+
"main_navigation": "Hauptnavigation",
44+
"back": "Zurück",
4145
"popular_packages": "Beliebte Pakete",
4246
"search": "Suche",
4347
"settings": "Einstellungen"
4448
},
4549
"settings": {
50+
"title": "Einstellungen",
51+
"tagline": "Passe npmx an deine Vorlieben an",
52+
"meta_description": "Einstellungen für npmx konfigurieren - Design, Sprache, Datumsformat und mehr anpassen",
53+
"sections": {
54+
"appearance": "Erscheinungsbild",
55+
"display": "Anzeige",
56+
"language": "Sprache"
57+
},
4658
"relative_dates": "Relative Datumsangaben",
59+
"relative_dates_description": "Zeige Daten relativ zur aktuellen Zeit an (z.B. 'vor 2 Tagen')",
4760
"include_types": "{'@'}types bei Installation einschließen",
61+
"include_types_description": "TypeScript-Typdefinitionen ({'@'}types-Pakete) automatisch bei Installationsbefehlen einschließen",
4862
"theme": "Design",
4963
"theme_light": "Hell",
5064
"theme_dark": "Dunkel",
5165
"theme_system": "System",
5266
"language": "Sprache",
5367
"help_translate": "Hilf bei der Übersetzung von npmx",
54-
"accent_colors": "Akzentfarben"
68+
"accent_colors": "Akzentfarben",
69+
"clear_accent": "Akzentfarbe zurücksetzen",
70+
"translation_progress": "Übersetzungsfortschritt: {progress}%"
71+
},
72+
"i18n": {
73+
"missing_keys": "{count} fehlende Übersetzung | {count} fehlende Übersetzungen",
74+
"copy_keys": "Schlüssel kopieren",
75+
"show_more_keys": "{count} weitere Schlüssel anzeigen",
76+
"contribute_hint": "Hilf mit, npmx in deine Sprache zu übersetzen!",
77+
"edit_on_github": "Auf GitHub bearbeiten",
78+
"view_guide": "Übersetzungsleitfaden anzeigen"
5579
},
5680
"common": {
81+
"skip_link": "Zum Hauptinhalt springen",
82+
"close_modal": "Modal schließen",
5783
"loading": "Lädt...",
5884
"loading_more": "Lädt mehr...",
5985
"loading_packages": "Pakete werden geladen...",
@@ -76,6 +102,7 @@
76102
"scroll_to_top": "Nach oben scrollen"
77103
},
78104
"package": {
105+
"navigation": "Paketnavigation",
79106
"not_found": "Paket nicht gefunden",
80107
"not_found_message": "Das Paket konnte nicht gefunden werden.",
81108
"no_description": "Keine Beschreibung vorhanden",
@@ -105,6 +132,10 @@
105132
"docs": "Doku",
106133
"fund": "Spenden"
107134
},
135+
"docs": {
136+
"not_available": "Dokumentation nicht verfügbar",
137+
"not_available_detail": "Für dieses Paket ist keine Dokumentation verfügbar"
138+
},
108139
"create": {
109140
"title": "Neues Projekt erstellen",
110141
"copy_command": "Erstellungsbefehl kopieren"
@@ -113,6 +144,12 @@
113144
"title": "Ausführen",
114145
"locally": "Lokal ausführen"
115146
},
147+
"get_started": {
148+
"title": "Erste Schritte",
149+
"pm_label": "Paketmanager",
150+
"copy_command": "Installationsbefehl kopieren",
151+
"view_types": "Typdefinitionen anzeigen"
152+
},
116153
"readme": {
117154
"title": "Readme",
118155
"no_readme": "Kein README vorhanden.",
@@ -144,7 +181,8 @@
144181
"title": "Abhängigkeiten ({count})",
145182
"list_label": "Paketabhängigkeiten",
146183
"show_all": "alle {count} Deps anzeigen",
147-
"optional": "optional"
184+
"optional": "optional",
185+
"view_vulnerabilities": "Sicherheitslücken anzeigen"
148186
},
149187
"peer_dependencies": {
150188
"title": "Peer-Abhängigkeiten ({count})",
@@ -197,16 +235,39 @@
197235
"license": {
198236
"view_spdx": "Lizenztext auf SPDX ansehen"
199237
},
238+
"metrics": {
239+
"esm": "ESM",
240+
"cjs": "CJS",
241+
"no_esm": "Kein ESM",
242+
"types_included": "Typen enthalten",
243+
"types_available": "Typen verfügbar",
244+
"no_types": "Keine Typen"
245+
},
200246
"vulnerabilities": {
201247
"no_description": "Keine Beschreibung verfügbar",
202248
"found": "{count} Sicherheitslücke gefunden | {count} Sicherheitslücken gefunden",
203249
"no_summary": "Keine Zusammenfassung",
204250
"view_details": "Details zur Sicherheitslücke anzeigen",
251+
"deps_found": "{count} Abhängigkeit mit Sicherheitslücken gefunden | {count} Abhängigkeiten mit Sicherheitslücken gefunden",
252+
"deps_affected": "{count} betroffene Abhängigkeit | {count} betroffene Abhängigkeiten",
253+
"tree_found": "{count} Sicherheitslücke im Abhängigkeitsbaum gefunden | {count} Sicherheitslücken im Abhängigkeitsbaum gefunden",
254+
"scanning_tree": "Abhängigkeitsbaum wird gescannt...",
255+
"show_all_packages": "Alle {count} Pakete anzeigen",
256+
"path": "Pfad",
257+
"more": "+{count} weitere",
258+
"packages_failed": "Einige Pakete konnten nicht gescannt werden",
259+
"no_known": "Keine bekannten Sicherheitslücken",
260+
"scan_failed": "Scan fehlgeschlagen",
205261
"severity": {
206262
"critical": "kritisch",
207263
"high": "hoch",
208264
"moderate": "mittel",
209265
"low": "niedrig"
266+
},
267+
"depth": {
268+
"root": "Root-Paket",
269+
"direct": "Direkte Abhängigkeit",
270+
"transitive": "Transitive Abhängigkeit"
210271
}
211272
},
212273
"access": {
@@ -505,9 +566,35 @@
505566
"package": "Paketmanager",
506567
"managers": "bauen"
507568
}
569+
},
570+
"contributors": {
571+
"title": "Mitwirkende",
572+
"description": "npmx wird von einer wachsenden Community von Mitwirkenden entwickelt",
573+
"loading": "Mitwirkende werden geladen...",
574+
"error": "Mitwirkende konnten nicht geladen werden",
575+
"view_profile": "Profil von {name} anzeigen"
576+
},
577+
"get_involved": {
578+
"title": "Mitmachen",
579+
"contribute": {
580+
"title": "Beitragen",
581+
"description": "Hilf mit, npmx zu verbessern - melde Bugs, schlage Features vor oder trage Code bei",
582+
"cta": "Auf GitHub beitragen"
583+
},
584+
"community": {
585+
"title": "Community",
586+
"description": "Tritt unserer Community bei, stelle Fragen und tausche dich mit anderen Nutzern aus",
587+
"cta": "Community beitreten"
588+
},
589+
"follow": {
590+
"title": "Folgen",
591+
"description": "Bleib auf dem Laufenden über neue Features und Updates",
592+
"cta": "Folgen"
593+
}
508594
}
509595
},
510596
"header": {
597+
"site_header": "Seitenkopf",
511598
"home": "npmx Startseite",
512599
"github": "GitHub",
513600
"packages": "Pakete",

0 commit comments

Comments
 (0)