@@ -17,7 +17,6 @@ import { detectPublishSecurityDowngradeForVersion } from '~/utils/publish-securi
1717import { useModal } from ' ~/composables/useModal'
1818import { useAtproto } from ' ~/composables/atproto/useAtproto'
1919import { togglePackageLike } from ' ~/utils/atproto/likes'
20- import { LinkBase } from ' #components'
2120
2221defineOgImageComponent (' Package' , {
2322 name : () => packageName .value ,
@@ -205,10 +204,7 @@ if (import.meta.client) {
205204 )
206205}
207206
208- const provenanceBadgeMounted = shallowRef (false )
209- onMounted (() => {
210- provenanceBadgeMounted .value = true
211- })
207+ const isMounted = useMounted ()
212208
213209// Keep latestVersion for comparison (to show "(latest)" badge)
214210const latestVersion = computed (() => {
@@ -450,7 +446,7 @@ const { data: likesData, status: likeStatus } = useFetch(
450446 },
451447)
452448const isLoadingLikeData = computed (
453- () => likeStatus .value !== ' error ' && likeStatus .value !== ' success ' ,
449+ () => likeStatus .value === ' pending ' || likeStatus .value === ' idle ' ,
454450)
455451
456452const isLikeActionPending = shallowRef (false )
@@ -617,7 +613,7 @@ onKeyStroke(
617613 >
618614 <span dir =" ltr" v-else >v{{ resolvedVersion }}</span >
619615
620- <template v-if =" hasProvenance (displayVersion ) && provenanceBadgeMounted " >
616+ <template v-if =" hasProvenance (displayVersion )" >
621617 <TooltipApp
622618 :text ="
623619 provenanceData && provenanceStatus !== 'pending'
@@ -680,62 +676,53 @@ onKeyStroke(
680676 </ButtonGroup >
681677
682678 <!-- Package metrics -->
683- <div class =" basis-full flex gap-2 sm:gap-3 flex-wrap" >
684- <ClientOnly >
685- <PackageMetricsBadges
686- v-if =" resolvedVersion"
687- :package-name =" pkg.name"
688- :version =" resolvedVersion"
689- :is-binary =" isBinaryOnly"
690- class =" self-baseline"
691- />
679+ <div class =" basis-full flex gap-2 sm:gap-3 flex-wrap items-stretch" >
680+ <PackageMetricsBadges
681+ v-if =" resolvedVersion"
682+ :package-name =" pkg.name"
683+ :version =" resolvedVersion"
684+ :is-binary =" isBinaryOnly"
685+ class =" self-baseline"
686+ />
692687
693- <!-- Package likes -->
694- <TooltipApp
695- :text ="
696- isLoadingLikeData
697- ? $t('common.loading')
698- : likesData?.userHasLiked
699- ? $t('package.likes.unlike')
700- : $t('package.likes.like')
688+ <!-- Package likes -->
689+ <TooltipApp
690+ :text ="
691+ isLoadingLikeData
692+ ? $t('common.loading')
693+ : likesData?.userHasLiked
694+ ? $t('package.likes.unlike')
695+ : $t('package.likes.like')
696+ "
697+ position =" bottom"
698+ class =" items-center"
699+ >
700+ <ButtonBase
701+ @click =" likeAction"
702+ size =" small"
703+ :title ="
704+ likesData?.userHasLiked ? $t('package.likes.unlike') : $t('package.likes.like')
705+ "
706+ :aria-label ="
707+ likesData?.userHasLiked ? $t('package.likes.unlike') : $t('package.likes.like')
708+ "
709+ :aria-pressed =" likesData?.userHasLiked"
710+ :classicon ="
711+ likesData?.userHasLiked
712+ ? 'i-lucide-heart-minus text-red-500'
713+ : 'i-lucide-heart-plus'
701714 "
702- position =" bottom"
703- class =" items-center"
704715 >
705716 <span
706717 v-if =" isLoadingLikeData"
707- class =" i-carbon-circle-dash w-3 h-3 motion-safe:animate-spin"
718+ class =" i-carbon-circle-dash w-3 h-3 motion-safe:animate-spin my-0.5 "
708719 aria-hidden =" true"
709720 />
710- <ButtonBase
711- v-else
712- @click =" likeAction"
713- size =" small"
714- :title ="
715- likesData?.userHasLiked ? $t('package.likes.unlike') : $t('package.likes.like')
716- "
717- :aria-label ="
718- likesData?.userHasLiked ? $t('package.likes.unlike') : $t('package.likes.like')
719- "
720- :aria-pressed =" likesData?.userHasLiked"
721- :classicon ="
722- likesData?.userHasLiked
723- ? 'i-lucide-heart-minus text-red-500'
724- : 'i-lucide-heart-plus'
725- "
726- >
721+ <span v-else >
727722 {{ compactNumberFormatter.format(likesData?.totalLikes ?? 0) }}
728- </ButtonBase >
729- </TooltipApp >
730- <template #fallback >
731- <div class =" flex items-center gap-1.5 list-none m-0 p-0 self-baseline" >
732- <SkeletonBlock class =" w-16 h-5.5 rounded" />
733- <SkeletonBlock class =" w-13 h-5.5 rounded" />
734- <SkeletonBlock class =" w-13 h-5.5 rounded" />
735- <SkeletonBlock class =" w-13 h-5.5 rounded bg-bg-subtle" />
736- </div >
737- </template >
738- </ClientOnly >
723+ </span >
724+ </ButtonBase >
725+ </TooltipApp >
739726 </div >
740727 </div >
741728 </header >
@@ -744,7 +731,7 @@ onKeyStroke(
744731 <section :class =" $style.areaDetails" >
745732 <div class =" mb-4" >
746733 <!-- Description container with min-height to prevent CLS -->
747- <div class =" max-w-2xl min-h-[4.5rem] " >
734+ <div class =" max-w-2xl" >
748735 <p v-if =" pkgDescription" class =" text-fg-muted text-base m-0" >
749736 <span v-html =" pkgDescription" />
750737 </p >
@@ -775,6 +762,7 @@ onKeyStroke(
775762 {{ compactNumberFormatter.format(forks) }}
776763 </LinkBase >
777764 </li >
765+ <li class =" basis-full sm:hidden" />
778766 <li v-if =" homepageUrl" >
779767 <LinkBase :to =" homepageUrl" classicon =" i-carbon:link" >
780768 {{ $t('package.links.homepage') }}
@@ -949,42 +937,32 @@ onKeyStroke(
949937 </div >
950938
951939 <!-- Vulnerabilities count -->
952- <ClientOnly >
953- <div class =" space-y-1 sm:col-span-2" >
954- <dt class =" text-xs text-fg-subtle uppercase tracking-wider" >
955- {{ $t('package.stats.vulns') }}
956- </dt >
957- <dd class =" font-mono text-sm text-fg" >
940+ <div class =" space-y-1 sm:col-span-2" >
941+ <dt class =" text-xs text-fg-subtle uppercase tracking-wider" >
942+ {{ $t('package.stats.vulns') }}
943+ </dt >
944+ <dd class =" font-mono text-sm text-fg" >
945+ <span
946+ v-if =" vulnTreeStatus === 'pending' || vulnTreeStatus === 'idle'"
947+ class =" inline-flex items-center gap-1 text-fg-subtle"
948+ >
958949 <span
959- v-if = " vulnTreeStatus === 'pending' || vulnTreeStatus === 'idle' "
960- class = " inline-flex items-center gap-1 text-fg-subtle "
961- >
962- < span
963- class = " i-carbon:circle-dash w-3 h-3 motion-safe:animate-spin "
964- aria-hidden = " true "
965- />
950+ class = " i-carbon:circle-dash w-3 h-3 motion-safe:animate-spin "
951+ aria-hidden = " true "
952+ / >
953+ </ span >
954+ < span v-else-if = " vulnTreeStatus === 'success' " >
955+ < span v-if = " hasVulnerabilities " class = " text-amber-500 " >
956+ {{ numberFormatter.format(vulnCount) }}
966957 </span >
967- <span v-else-if =" vulnTreeStatus === 'success'" >
968- <span v-if =" hasVulnerabilities" class =" text-amber-500" >
969- {{ numberFormatter.format(vulnCount) }}
970- </span >
971- <span v-else class =" inline-flex items-center gap-1 text-fg-muted" >
972- <span class =" i-carbon:checkmark w-3 h-3" aria-hidden =" true" />
973- {{ numberFormatter.format(0) }}
974- </span >
958+ <span v-else class =" inline-flex items-center gap-1 text-fg-muted" >
959+ <span class =" i-carbon:checkmark w-3 h-3" aria-hidden =" true" />
960+ {{ numberFormatter.format(0) }}
975961 </span >
976- <span v-else class =" text-fg-subtle" >-</span >
977- </dd >
978- </div >
979- <template #fallback >
980- <div class =" space-y-1 sm:col-span-2" >
981- <dt class =" text-xs text-fg-subtle uppercase tracking-wider" >
982- {{ $t('package.stats.vulns') }}
983- </dt >
984- <dd class =" font-mono text-sm text-fg-subtle" >-</dd >
985- </div >
986- </template >
987- </ClientOnly >
962+ </span >
963+ <span v-else class =" text-fg-subtle" >-</span >
964+ </dd >
965+ </div >
988966
989967 <div
990968 v-if =" resolvedVersion && pkg.time?.[resolvedVersion]"
@@ -1183,32 +1161,30 @@ onKeyStroke(
11831161 {{ $t('package.readme.title') }}
11841162 </LinkBase >
11851163 </h2 >
1186- <ClientOnly >
1187- <div class =" flex gap-2" >
1188- <!-- Copy readme as Markdown button -->
1189- <TooltipApp
1190- v-if =" readmeData?.md"
1191- :text =" $t('package.readme.copy_as_markdown')"
1192- position =" bottom"
1164+ <div class =" flex gap-2" >
1165+ <!-- Copy readme as Markdown button -->
1166+ <TooltipApp
1167+ v-if =" readmeData?.md"
1168+ :text =" $t('package.readme.copy_as_markdown')"
1169+ position =" bottom"
1170+ >
1171+ <ButtonBase
1172+ @click =" copyReadme()"
1173+ :aria-pressed =" copiedReadme"
1174+ :aria-label ="
1175+ copiedReadme ? $t('common.copied') : $t('package.readme.copy_as_markdown')
1176+ "
1177+ :classicon =" copiedReadme ? 'i-carbon:checkmark' : 'i-simple-icons:markdown'"
11931178 >
1194- <ButtonBase
1195- @click =" copyReadme()"
1196- :aria-pressed =" copiedReadme"
1197- :aria-label ="
1198- copiedReadme ? $t('common.copied') : $t('package.readme.copy_as_markdown')
1199- "
1200- :classicon =" copiedReadme ? 'i-carbon:checkmark' : 'i-simple-icons:markdown'"
1201- >
1202- {{ copiedReadme ? $t('common.copied') : $t('common.copy') }}
1203- </ButtonBase >
1204- </TooltipApp >
1205- <ReadmeTocDropdown
1206- v-if =" readmeData?.toc && readmeData.toc.length > 1"
1207- :toc =" readmeData.toc"
1208- :active-id =" activeTocId"
1209- />
1210- </div >
1211- </ClientOnly >
1179+ {{ copiedReadme ? $t('common.copied') : $t('common.copy') }}
1180+ </ButtonBase >
1181+ </TooltipApp >
1182+ <ReadmeTocDropdown
1183+ v-if =" readmeData?.toc && readmeData.toc.length > 1"
1184+ :toc =" readmeData.toc"
1185+ :active-id =" activeTocId"
1186+ />
1187+ </div >
12121188 </div >
12131189
12141190 <!-- eslint-disable vue/no-v-html -- HTML is sanitized server-side -->
@@ -1226,7 +1202,7 @@ onKeyStroke(
12261202 </p >
12271203
12281204 <section
1229- v-if =" hasProvenance(displayVersion) && provenanceBadgeMounted "
1205+ v-if =" hasProvenance(displayVersion) && isMounted "
12301206 id =" provenance"
12311207 class =" scroll-mt-20"
12321208 >
@@ -1261,6 +1237,9 @@ onKeyStroke(
12611237 <!-- Team access controls (for scoped packages when connected) -->
12621238 <ClientOnly >
12631239 <PackageAccessControls :package-name =" pkg.name" />
1240+ <template #fallback >
1241+ <!-- Show skeleton loaders when SSR or access controls are loading -->
1242+ </template >
12641243 </ClientOnly >
12651244
12661245 <!-- Agent Skills -->
@@ -1271,6 +1250,9 @@ onKeyStroke(
12711250 :package-name =" pkg.name"
12721251 :version =" resolvedVersion || undefined"
12731252 />
1253+ <template #fallback >
1254+ <!-- Show skeleton loaders when SSR or access controls are loading -->
1255+ </template >
12741256 </ClientOnly >
12751257
12761258 <!-- Download stats -->
0 commit comments