[READ-ONLY] a fast, modern browser for the npm registry
at main 479 lines 18 kB view raw
1<script setup lang="ts"></script> 2 3<template> 4 <article 5 aria-busy="true" 6 :aria-label="$t('package.skeleton.loading')" 7 class="package-page motion-safe:animate-fade-in" 8 > 9 <!-- Package header matches area-header in [...name].vue --> 10 <header class="area-header sticky top-14 z-1 bg-[--bg] py-2"> 11 <div class="flex items-baseline gap-x-2 gap-y-1 sm:gap-x-3 flex-wrap min-w-0"> 12 <!-- Package name --> 13 <div class="min-w-0"> 14 <h1 class="font-mono text-2xl sm:text-3xl font-medium"> 15 <SkeletonInline class="h-9 w-48" /> 16 </h1> 17 </div> 18 <!-- Version --> 19 <span class="inline-flex items-baseline font-mono text-base sm:text-lg shrink-0"> 20 <SkeletonInline class="h-6 w-20" /> 21 </span> 22 23 <!-- Docs + Code + Compare nav placeholder (hidden on mobile) --> 24 <div 25 class="hidden sm:flex items-center gap-0.5 p-0.5 bg-bg-subtle border border-border-subtle rounded-md shrink-0 ms-auto self-center" 26 > 27 <SkeletonInline class="h-7 w-22 rounded" /> 28 <SkeletonInline class="h-7 w-20 rounded" /> 29 <SkeletonInline class="h-7 w-26 rounded" /> 30 </div> 31 32 <!-- Metrics badges + likes matches basis-full flex row --> 33 <div class="basis-full flex gap-2 sm:gap-3 flex-wrap items-stretch"> 34 <!-- Badge row (Types, ESM, CJS, CJS-ghost) --> 35 <div class="flex items-center gap-1.5 self-baseline"> 36 <SkeletonBlock class="w-16 h-5.5 rounded" /> 37 <SkeletonBlock class="w-13 h-5.5 rounded" /> 38 <SkeletonBlock class="w-13 h-5.5 rounded" /> 39 <SkeletonBlock class="w-13 h-5.5 rounded bg-bg-subtle" /> 40 </div> 41 <!-- Likes button placeholder --> 42 <SkeletonBlock class="w-14 h-5.5 rounded self-baseline" /> 43 </div> 44 </div> 45 </header> 46 47 <!-- Package details matches area-details in [...name].vue --> 48 <section class="area-details"> 49 <div class="mb-4"> 50 <!-- Description --> 51 <div class="max-w-2xl"> 52 <div class="space-y-2"> 53 <SkeletonBlock class="h-5 w-full" /> 54 <SkeletonBlock class="h-5 w-4/5" /> 55 <SkeletonBlock class="h-5 w-3/5" /> 56 </div> 57 </div> 58 59 <!-- External links --> 60 <ul 61 class="flex flex-wrap items-center gap-x-3 gap-y-1.5 sm:gap-4 list-none m-0 p-0 mt-3 text-sm" 62 > 63 <li><SkeletonInline class="h-5 w-28" /></li> 64 <li><SkeletonInline class="h-5 w-14" /></li> 65 <li><SkeletonInline class="h-5 w-16" /></li> 66 <li><SkeletonInline class="h-5 w-10" /></li> 67 </ul> 68 </div> 69 70 <!-- Stats grid matches dl in [...name].vue --> 71 <dl 72 class="grid grid-cols-2 sm:grid-cols-7 md:grid-cols-11 gap-3 sm:gap-4 py-4 sm:py-6 mt-4 sm:mt-6 border-t border-b border-border" 73 > 74 <!-- License --> 75 <div class="space-y-1 sm:col-span-2"> 76 <dt class="text-xs text-fg-subtle uppercase tracking-wider"> 77 {{ $t('package.stats.license') }} 78 </dt> 79 <dd class="font-mono text-sm"> 80 <SkeletonInline class="h-5 w-12" /> 81 </dd> 82 </div> 83 84 <!-- Deps --> 85 <div class="space-y-1 sm:col-span-2"> 86 <dt class="text-xs text-fg-subtle uppercase tracking-wider"> 87 {{ $t('package.stats.deps') }} 88 </dt> 89 <dd class="font-mono text-sm"> 90 <SkeletonInline class="h-5 w-12" /> 91 </dd> 92 </div> 93 94 <!-- Install Size --> 95 <div class="space-y-1 sm:col-span-3"> 96 <dt class="text-xs text-fg-subtle uppercase tracking-wider"> 97 {{ $t('package.stats.install_size') }} 98 </dt> 99 <dd class="font-mono text-sm"> 100 <SkeletonInline class="h-5 w-16" /> 101 </dd> 102 </div> 103 104 <!-- Vulns --> 105 <div class="space-y-1 sm:col-span-2"> 106 <dt class="text-xs text-fg-subtle uppercase tracking-wider"> 107 {{ $t('package.stats.vulns') }} 108 </dt> 109 <dd class="font-mono text-sm text-fg-subtle">-</dd> 110 </div> 111 112 <!-- Published --> 113 <div class="space-y-1 sm:col-span-2"> 114 <dt class="text-xs text-fg-subtle uppercase tracking-wider"> 115 {{ $t('package.stats.published') }} 116 </dt> 117 <dd class="font-mono text-sm"> 118 <SkeletonInline class="h-5 w-28" /> 119 </dd> 120 </div> 121 </dl> 122 </section> 123 124 <!-- Install section matches area-install in [...name].vue --> 125 <section class="area-install scroll-mt-20"> 126 <div class="flex flex-wrap items-center justify-between mb-3"> 127 <h2 class="text-xs font-mono text-fg-subtle uppercase tracking-wider"> 128 {{ $t('package.get_started.title') }} 129 </h2> 130 <!-- Package manager select placeholder --> 131 <SkeletonInline class="h-7 w-24 rounded" /> 132 </div> 133 <!-- Terminal-style install command matches TerminalInstall.vue --> 134 <div class="bg-bg-subtle border border-border rounded-lg overflow-hidden"> 135 <div class="flex gap-1.5 px-3 pt-2 sm:px-4 sm:pt-3"> 136 <span class="w-2.5 h-2.5 rounded-full bg-fg-subtle" /> 137 <span class="w-2.5 h-2.5 rounded-full bg-fg-subtle" /> 138 <span class="w-2.5 h-2.5 rounded-full bg-fg-subtle" /> 139 </div> 140 <div class="px-3 pt-2 pb-3 sm:px-4 sm:pt-3 sm:pb-4 space-y-1"> 141 <!-- $ install command --> 142 <div class="flex items-center gap-2"> 143 <span class="text-fg-subtle font-mono text-sm select-none shrink-0">$</span> 144 <SkeletonInline class="h-5 w-40" /> 145 </div> 146 <!-- # Run locally --> 147 <div class="flex items-center gap-2 pt-1"> 148 <SkeletonInline class="h-4 w-24" /> 149 </div> 150 <!-- $ run command --> 151 <div class="flex items-center gap-2"> 152 <span class="text-fg-subtle font-mono text-sm select-none shrink-0">$</span> 153 <SkeletonInline class="h-5 w-28" /> 154 </div> 155 <!-- # Create new project --> 156 <div class="flex items-center gap-2 pt-1"> 157 <SkeletonInline class="h-4 w-36" /> 158 </div> 159 <!-- $ create command --> 160 <div class="flex items-center gap-2"> 161 <span class="text-fg-subtle font-mono text-sm select-none shrink-0">$</span> 162 <SkeletonInline class="h-5 w-32" /> 163 </div> 164 </div> 165 </div> 166 </section> 167 168 <!-- Vulns area (empty placeholder to hold grid space) --> 169 <div class="area-vulns" /> 170 171 <!-- README matches area-readme in [...name].vue --> 172 <section class="area-readme min-w-0 scroll-mt-20"> 173 <div class="flex flex-wrap items-center justify-between mb-3 px-1"> 174 <h2 class="text-xs font-mono text-fg-subtle uppercase tracking-wider"> 175 {{ $t('package.readme.title') }} 176 </h2> 177 </div> 178 <!-- Simulated README content --> 179 <div class="space-y-4"> 180 <!-- Heading --> 181 <SkeletonBlock class="h-7 w-2/3" /> 182 <!-- Paragraphs --> 183 <SkeletonBlock class="h-4 w-full" /> 184 <SkeletonBlock class="h-4 w-full" /> 185 <SkeletonBlock class="h-4 w-4/5" /> 186 <!-- Gap for section break --> 187 <SkeletonBlock class="h-6 w-1/2 mt-6" /> 188 <SkeletonBlock class="h-4 w-full" /> 189 <SkeletonBlock class="h-4 w-full" /> 190 <SkeletonBlock class="h-4 w-3/4" /> 191 <!-- Code block placeholder --> 192 <SkeletonBlock class="h-24 w-full rounded-lg mt-4" /> 193 <SkeletonBlock class="h-4 w-full" /> 194 <SkeletonBlock class="h-4 w-5/6" /> 195 </div> 196 </section> 197 198 <!-- Sidebar matches area-sidebar in [...name].vue --> 199 <div class="area-sidebar"> 200 <div 201 class="sticky top-30 xl:top-14 space-y-6 sm:space-y-8 min-w-0 overflow-y-auto pe-2.5 lg:(max-h-[calc(100dvh-8.5rem)] overscroll-contain) xl:(pt-2 max-h-[calc(100dvh-6rem)])" 202 > 203 <div class="flex flex-col gap-4 sm:gap-6 xl:(pt-2)"> 204 <!-- Download stats matches CollapsibleSection + sparkline skeleton --> 205 <section> 206 <div class="flex items-center justify-between mb-3 px-1"> 207 <h2 208 class="text-xs font-mono text-fg-subtle uppercase tracking-wider flex items-center gap-2" 209 > 210 <span class="w-4 h-4 flex items-center justify-center shrink-0"> 211 <span class="i-lucide:chevron-down w-3 h-3" aria-hidden="true" /> 212 </span> 213 {{ $t('package.downloads.title') }} 214 </h2> 215 </div> 216 <div class="ms-6 max-w-xs"> 217 <!-- Title row: fontSize * 2 = 24px --> 218 <div class="h-6 flex items-center ps-3"> 219 <SkeletonInline class="h-3 w-36" /> 220 </div> 221 <!-- Chart area: matches SVG viewBox 500:80 --> 222 <div class="aspect-[500/80] flex items-center"> 223 <div class="w-[42%] flex items-center ps-0.5"> 224 <SkeletonInline class="h-7 w-24" /> 225 </div> 226 <div class="flex-1 flex items-end pe-3"> 227 <SkeletonInline class="h-px w-full" /> 228 </div> 229 </div> 230 </div> 231 </section> 232 233 <!-- Playgrounds matches PackagePlaygrounds (not CollapsibleSection) --> 234 <section class="px-1"> 235 <h2 class="text-xs font-mono text-fg-subtle uppercase tracking-wider mb-3"> 236 {{ $t('package.playgrounds.title') }} 237 </h2> 238 <SkeletonBlock class="w-full h-9 rounded-md" /> 239 </section> 240 241 <!-- Compatibility matches CollapsibleSection --> 242 <section> 243 <div class="flex items-center justify-between mb-3 px-1"> 244 <h2 245 class="text-xs font-mono text-fg-subtle uppercase tracking-wider flex items-center gap-2" 246 > 247 <span class="w-4 h-4 flex items-center justify-center shrink-0"> 248 <span class="i-lucide:chevron-down w-3 h-3" aria-hidden="true" /> 249 </span> 250 {{ $t('package.compatibility') }} 251 </h2> 252 </div> 253 <div class="ms-6 space-y-2"> 254 <div class="flex justify-between gap-4 py-1"> 255 <SkeletonInline class="h-4 w-16" /> 256 <SkeletonInline class="h-4 w-20" /> 257 </div> 258 </div> 259 </section> 260 261 <!-- Versions matches CollapsibleSection + PackageVersions --> 262 <section> 263 <div class="flex items-center justify-between mb-3 px-1"> 264 <h2 265 class="text-xs font-mono text-fg-subtle uppercase tracking-wider flex items-center gap-2" 266 > 267 <span class="w-4 h-4 flex items-center justify-center shrink-0"> 268 <span class="i-lucide:chevron-down w-3 h-3" aria-hidden="true" /> 269 </span> 270 {{ $t('package.skeleton.versions') }} 271 </h2> 272 </div> 273 <div class="ms-6 space-y-0.5 min-w-0"> 274 <!-- Version rows with expand chevron + version + tag + date --> 275 <div v-for="i in 4" :key="i" class="flex items-center gap-2 px-1"> 276 <span class="w-4 h-4 flex items-center justify-center shrink-0"> 277 <span class="i-lucide:chevron-right w-3 h-3 text-fg-subtle" aria-hidden="true" /> 278 </span> 279 <div class="flex-1 py-1.5 min-w-0 flex gap-2 justify-between items-center"> 280 <div> 281 <SkeletonInline 282 class="h-4" 283 :class="i === 1 ? 'w-12' : i === 2 ? 'w-22' : i === 3 ? 'w-26' : 'w-14'" 284 /> 285 <SkeletonInline class="h-2.5 w-10 mt-0.5" /> 286 </div> 287 <SkeletonInline class="h-3 w-20 shrink-0" /> 288 </div> 289 </div> 290 <!-- Other versions row --> 291 <div class="flex items-center gap-2 p-1"> 292 <span class="w-4 h-4 flex items-center justify-center shrink-0"> 293 <span class="i-lucide:chevron-right w-3 h-3 text-fg-subtle" aria-hidden="true" /> 294 </span> 295 <SkeletonInline class="h-3 w-28" /> 296 </div> 297 </div> 298 </section> 299 300 <!-- Dependencies matches CollapsibleSection --> 301 <section> 302 <div class="flex items-center justify-between mb-3 px-1"> 303 <h2 304 class="text-xs font-mono text-fg-subtle uppercase tracking-wider flex items-center gap-2" 305 > 306 <span class="w-4 h-4 flex items-center justify-center shrink-0"> 307 <span class="i-lucide:chevron-down w-3 h-3" aria-hidden="true" /> 308 </span> 309 {{ $t('package.skeleton.dependencies') }} 310 </h2> 311 </div> 312 <ul class="ms-6 px-1 space-y-1 list-none m-0 p-0"> 313 <li class="flex items-center justify-between py-1 text-sm"> 314 <SkeletonInline class="h-4 w-24" /> 315 <SkeletonInline class="h-4 w-12" /> 316 </li> 317 <li class="flex items-center justify-between py-1 text-sm"> 318 <SkeletonInline class="h-4 w-32" /> 319 <SkeletonInline class="h-4 w-10" /> 320 </li> 321 <li class="flex items-center justify-between py-1 text-sm"> 322 <SkeletonInline class="h-4 w-20" /> 323 <SkeletonInline class="h-4 w-14" /> 324 </li> 325 <li class="flex items-center justify-between py-1 text-sm"> 326 <SkeletonInline class="h-4 w-28" /> 327 <SkeletonInline class="h-4 w-12" /> 328 </li> 329 </ul> 330 </section> 331 332 <!-- Keywords matches CollapsibleSection --> 333 <section> 334 <div class="flex items-center justify-between mb-3 px-1"> 335 <h2 336 class="text-xs font-mono text-fg-subtle uppercase tracking-wider flex items-center gap-2" 337 > 338 <span class="w-4 h-4 flex items-center justify-center shrink-0"> 339 <span class="i-lucide:chevron-down w-3 h-3" aria-hidden="true" /> 340 </span> 341 {{ $t('package.skeleton.keywords') }} 342 </h2> 343 </div> 344 <ul class="ms-6 flex flex-wrap gap-1.5 list-none m-0 p-1"> 345 <li><SkeletonInline class="h-6 w-16 rounded" /></li> 346 <li><SkeletonInline class="h-6 w-12 rounded" /></li> 347 <li><SkeletonInline class="h-6 w-20 rounded" /></li> 348 <li><SkeletonInline class="h-6 w-14 rounded" /></li> 349 <li><SkeletonInline class="h-6 w-18 rounded" /></li> 350 <li><SkeletonInline class="h-6 w-10 rounded" /></li> 351 </ul> 352 </section> 353 354 <!-- Maintainers matches CollapsibleSection --> 355 <section> 356 <div class="flex items-center justify-between mb-3 px-1"> 357 <h2 358 class="text-xs font-mono text-fg-subtle uppercase tracking-wider flex items-center gap-2" 359 > 360 <span class="w-4 h-4 flex items-center justify-center shrink-0"> 361 <span class="i-lucide:chevron-down w-3 h-3" aria-hidden="true" /> 362 </span> 363 {{ $t('package.skeleton.maintainers') }} 364 </h2> 365 </div> 366 <ul class="ms-6 space-y-2 list-none my-1 px-1"> 367 <li><SkeletonInline class="h-5 w-28" /></li> 368 <li><SkeletonInline class="h-5 w-24" /></li> 369 </ul> 370 </section> 371 </div> 372 </div> 373 </div> 374 </article> 375</template> 376 377<style scoped> 378.package-page { 379 display: grid; 380 gap: 2rem; 381 382 /* Mobile: single column, sidebar above readme */ 383 grid-template-columns: minmax(0, 1fr); 384 grid-template-areas: 385 'header' 386 'details' 387 'install' 388 'vulns' 389 'sidebar' 390 'readme'; 391} 392 393/* Tablet/medium: header/install/vulns full width, readme+sidebar side by side */ 394@media (min-width: 1024px) { 395 .package-page { 396 grid-template-columns: 2fr 1fr; 397 grid-template-areas: 398 'header header' 399 'details details' 400 'install install' 401 'vulns vulns' 402 'readme sidebar'; 403 grid-template-rows: auto auto auto auto 1fr; 404 } 405} 406 407/* Desktop: floating sidebar alongside all content */ 408@media (min-width: 1280px) { 409 .package-page { 410 grid-template-columns: 1fr 20rem; 411 grid-template-areas: 412 'header sidebar' 413 'details sidebar' 414 'install sidebar' 415 'vulns sidebar' 416 'readme sidebar'; 417 } 418} 419 420.area-header { 421 grid-area: header; 422} 423 424.area-details { 425 grid-area: details; 426} 427 428.area-install { 429 grid-area: install; 430} 431 432.area-vulns { 433 grid-area: vulns; 434} 435 436.area-readme { 437 grid-area: readme; 438 overflow-x: hidden; 439} 440 441.area-sidebar { 442 grid-area: sidebar; 443} 444 445/* Sidebar scrollbar: hidden by default, shown on hover/focus */ 446@media (min-width: 1024px) { 447 .sidebar-scroll { 448 scrollbar-gutter: stable; 449 scrollbar-width: 8px; 450 scrollbar-color: transparent transparent; 451 } 452 453 .sidebar-scroll::-webkit-scrollbar { 454 width: 8px; 455 height: 8px; 456 } 457 458 .sidebar-scroll::-webkit-scrollbar-track, 459 .sidebar-scroll::-webkit-scrollbar-thumb { 460 background: transparent; 461 } 462 463 .sidebar-scroll:hover, 464 .sidebar-scroll:focus-within { 465 scrollbar-color: var(--border) transparent; 466 } 467 468 .sidebar-scroll:hover::-webkit-scrollbar-thumb, 469 .sidebar-scroll:focus-within::-webkit-scrollbar-thumb { 470 background-color: var(--border); 471 border-radius: 9999px; 472 } 473 474 .sidebar-scroll:hover::-webkit-scrollbar-track, 475 .sidebar-scroll:focus-within::-webkit-scrollbar-track { 476 background: transparent; 477 } 478} 479</style>