1+ <script lang="ts">
2+ const compactFormat = new Intl .NumberFormat (' en' , { notation: ' compact' , maximumFractionDigits: 1 })
3+
4+ const KIND_ICONS: Record <string , string > = {
5+ function: ' i-lucide:parentheses' ,
6+ class: ' i-lucide:box' ,
7+ interface: ' i-lucide:puzzle' ,
8+ typeAlias: ' i-lucide:type' ,
9+ variable: ' i-lucide:variable' ,
10+ enum: ' i-lucide:list' ,
11+ namespace: ' i-lucide:package' ,
12+ }
13+ </script >
14+
115<script setup lang="ts">
216import type { JsDelivrFileNode } from ' #shared/types'
317import { joinURL } from ' ufo'
4- import { buildRollingWeeklyEvolutionFromDaily , smoothPath } from ' ~/composables/useCharts'
5- import { fetchNpmDownloadsRange } from ' ~/utils/npm/api'
18+ import { smoothPath , useCharts } from ' ~/composables/useCharts'
619import { useSiteConfig } from ' #site-config/app/composables'
720
821const { name, version, variant } = defineProps <{
@@ -44,45 +57,29 @@ const repositoryUrl = computed(() => {
4457
4558const { repoRef, stars, refresh : refreshRepoMeta } = useRepoMeta (repositoryUrl )
4659
47- const compactFormat = new Intl .NumberFormat (' en' , { notation: ' compact' , maximumFractionDigits: 1 })
4860const formattedStars = computed (() => (stars .value > 0 ? compactFormat .format (stars .value ) : ' ' ))
4961
5062const { name : siteName } = useSiteConfig ()
5163
52- const pkgOrg = computed (() => {
53- const n = pkg .value ?.name
54- if (! n ?.startsWith (' @' )) return null
55- return n .slice (0 , n .indexOf (' /' ))
56- })
57- const pkgShortName = computed (() => {
64+ const pkgNameParts = computed (() => {
5865 const n = pkg .value ?.name
59- if (! n ?.startsWith (' @' )) return n
60- return n .slice (n .indexOf (' /' ) + 1 )
66+ if (! n ?.startsWith (' @' )) return { org: null , short: n }
67+ const slashIdx = n .indexOf (' /' )
68+ return { org: n .slice (0 , slashIdx ), short: n .slice (slashIdx + 1 ) }
6169})
6270
6371// Fetch 52 weeks of download evolution for sparkline
72+ const { fetchPackageDownloadEvolution } = useCharts ()
6473const weeklyValues = shallowRef <number []>([])
6574
6675async function fetchWeeklyEvolution() {
67- const pkgName = name
68- if (! pkgName ) return
69-
70- const today = new Date ()
71- const end = new Date (
72- Date .UTC (today .getUTCFullYear (), today .getUTCMonth (), today .getUTCDate () - 1 ),
73- )
74- const start = new Date (end .getTime () - (52 * 7 - 1 ) * 86400000 )
75-
76- const startIso = start .toISOString ().slice (0 , 10 )
77- const endIso = end .toISOString ().slice (0 , 10 )
78-
79- const resp = await fetchNpmDownloadsRange (pkgName , startIso , endIso ).catch (() => null )
80-
81- if (! resp ?.downloads ?.length ) return
82-
83- const daily = resp .downloads .map (d => ({ day: d .day , value: d .downloads }))
84- const weekly = buildRollingWeeklyEvolutionFromDaily (daily , startIso , endIso )
85- weeklyValues .value = weekly .map (w => w .value )
76+ const evolution = await fetchPackageDownloadEvolution (name , null , {
77+ granularity: ' week' ,
78+ weeks: 52 ,
79+ }).catch (() => null )
80+ if (evolution ?.length ) {
81+ weeklyValues .value = evolution .map (w => w .value )
82+ }
8683}
8784
8885// Flatten file tree into renderable rows for code-tree variant
@@ -95,7 +92,7 @@ const treeRows = shallowRef<TreeRow[]>([])
9592
9693async function fetchCodeTree() {
9794 const ver = resolvedVersion .value ?? version
98- if (! name || ! ver ) return
95+ if (! ver ) return
9996
10097 // Call jsDelivr directly — $fetch to internal API can deadlock in OG image island context
10198 const resp = await $fetch <{ files: JsDelivrFileNode [] }>(
@@ -117,7 +114,7 @@ async function fetchCodeTree() {
117114 function walk(nodes : JsDelivrFileNode [], depth : number ) {
118115 for (const node of sorted (nodes )) {
119116 if (rows .length >= MAX_ROWS ) return
120- rows .push ({ name: node .name , depth , isDir: node . type === ' directory ' })
117+ rows .push ({ name: node .name , depth })
121118 if (node .files ) walk (node .files , depth + 1 )
122119 }
123120 }
@@ -133,19 +130,9 @@ interface SymbolRow {
133130}
134131const symbolRows = shallowRef <SymbolRow []>([])
135132
136- const KIND_ICONS: Record <string , string > = {
137- function: ' i-lucide:parentheses' ,
138- class: ' i-lucide:box' ,
139- interface: ' i-lucide:puzzle' ,
140- typeAlias: ' i-lucide:type' ,
141- variable: ' i-lucide:variable' ,
142- enum: ' i-lucide:list' ,
143- namespace: ' i-lucide:package' ,
144- }
145-
146133async function fetchFunctionTree() {
147134 const ver = resolvedVersion .value ?? version
148- if (! name || ! ver ) return
135+ if (! ver ) return
149136
150137 const resp = await $fetch <{ toc: string | null }>(` /api/registry/docs/${name }/v/${ver } ` ).catch (
151138 () => null ,
@@ -245,20 +232,20 @@ const sparklineSrc = computed(() => {
245232
246233 <div class =" flex flex-col max-w-full gap-3" >
247234 <div
248- v-if =" pkgOrg "
235+ v-if =" pkgNameParts.org "
249236 class =" lg:text-5xl text-3xl opacity-50 font-mono tracking-tight leading-none"
250237 :style =" { textOverflow: 'ellipsis', lineClamp: 1 }"
251238 >
252- {{ pkgOrg }}
239+ {{ pkgNameParts.org }}
253240 </div >
254241 <div
255242 class =" tracking-tighter font-mono leading-none overflow-hidden"
256243 :class ="
257- (pkgShortName ?.length ?? 0) > 20 ? 'lg:text-6xl text-4xl' : 'lg:text-7xl text-5xl'
244+ (pkgNameParts.short ?.length ?? 0) > 20 ? 'lg:text-6xl text-4xl' : 'lg:text-7xl text-5xl'
258245 "
259246 :style =" { textOverflow: 'ellipsis', lineClamp: 1, wordBreak: 'break-all' }"
260247 >
261- {{ pkgShortName }}
248+ {{ pkgNameParts.short }}
262249 </div >
263250 <div
264251 v-if =" version"
@@ -271,15 +258,15 @@ const sparklineSrc = computed(() => {
271258
272259 <div class =" flex items-center gap-5 text-4xl text-fg-muted" >
273260 <div v-if =" repositoryUrl" class =" flex items-center gap-2" >
274- <div class =" i-simple-icons:github w-8 h-8 text-white " />
261+ <div class =" i-simple-icons:github w-8 h-8 text-fg-muted " />
275262 <span v-if =" repoRef" class =" max-w-[500px]" :style =" { textOverflow: 'ellipsis' }" >
276263 {{ repoRef.owner }}<span class =" opacity-50" >/</span >{{ repoRef.repo }}
277264 </span >
278265 <span v-else >{{ $t('package.links.repo') }}</span >
279266 </div >
280267
281268 <span v-if =" formattedStars" class =" flex items-center gap-2" data-testid =" stars" >
282- <div class =" i-lucide:star w-8 h-8 text-white " :style =" { fill: 'white' }" />
269+ <div class =" i-lucide:star w-8 h-8 text-fg-muted " :style =" { fill: 'white' }" />
283270 <span >{{ formattedStars }}</span >
284271 </span >
285272
@@ -312,10 +299,8 @@ const sparklineSrc = computed(() => {
312299 class =" flex items-center whitespace-nowrap text-fg"
313300 :style =" { paddingLeft: `${row.depth * 20}px` }"
314301 >
315- <span
316- class =" w-5 h-5 shrink-0 force-mr-1.5"
317- :class =" row.isDir ? 'i-lucide:folder' : 'i-lucide:file'"
318- />
302+ <span v-if =" row.isDir" class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:folder" />
303+ <span v-else class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:file" />
319304 <span >{{ row.name }}</span >
320305 </div >
321306 </div >
@@ -332,9 +317,36 @@ const sparklineSrc = computed(() => {
332317 :style =" { paddingLeft: row.kind === 'symbol' ? '20px' : '0' }"
333318 >
334319 <span
335- v-if =" row.kind === 'symbol'"
336- class =" w-5 h-5 shrink-0 force-mr-1.5"
337- :class =" row.icon"
320+ v-if =" row.icon === 'i-lucide:parentheses'"
321+ class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:parentheses"
322+ />
323+ <span
324+ v-else-if =" row.icon === 'i-lucide:box'"
325+ class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:box"
326+ />
327+ <span
328+ v-else-if =" row.icon === 'i-lucide:puzzle'"
329+ class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:puzzle"
330+ />
331+ <span
332+ v-else-if =" row.icon === 'i-lucide:type'"
333+ class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:type"
334+ />
335+ <span
336+ v-else-if =" row.icon === 'i-lucide:variable'"
337+ class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:variable"
338+ />
339+ <span
340+ v-else-if =" row.icon === 'i-lucide:list'"
341+ class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:list"
342+ />
343+ <span
344+ v-else-if =" row.icon === 'i-lucide:package'"
345+ class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:package"
346+ />
347+ <span
348+ v-else-if =" row.kind === 'symbol'"
349+ class =" w-5 h-5 shrink-0 force-mr-1.5 i-lucide:code"
338350 />
339351 <span :class =" row.kind === 'section' ? 'opacity-60 text-4 mt-1' : ''" >{{ row.name }}</span >
340352 </div >
0 commit comments