Skip to content

Commit 499fc7e

Browse files
committed
chore: progress
1 parent 7821938 commit 499fc7e

15 files changed

+253
-219
lines changed

app/components/OgImage/Package.takumi.vue

Lines changed: 100 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ const { repoRef, stars, refresh: refreshRepoMeta } = useRepoMeta(repositoryUrl)
5858
5959
const formattedStars = computed(() => (stars.value > 0 ? compactFormat.format(stars.value) : ''))
6060
61-
const weeklyDownloads = shallowRef(0)
62-
const formattedDownloads = computed(() =>
63-
weeklyDownloads.value ? compactFormat.format(weeklyDownloads.value) : '',
64-
)
61+
const formattedDownloads = computed(() => {
62+
const last = weeklyValues.value.at(-1)
63+
return last ? compactFormat.format(last) : ''
64+
})
6565
6666
const totalLikes = shallowRef(0)
6767
const formattedLikes = computed(() =>
@@ -177,34 +177,25 @@ async function fetchFunctionTree() {
177177
symbolRows.value = rows
178178
}
179179
180-
function fetchVariantData() {
181-
if (variant === 'code-tree') return fetchCodeTree()
182-
if (variant === 'function-tree') return fetchFunctionTree()
183-
return fetchWeeklyEvolution()
184-
}
185-
186-
const fetchLikes = $fetch<{ totalLikes: number }>(`/api/social/likes/${name}`)
187-
.then(d => {
188-
totalLikes.value = d?.totalLikes ?? 0
189-
})
190-
.catch(() => {})
191-
192-
const downloadUrl = `https://api.npmjs.org/downloads/point/last-week/${encodePackageName(name)}`
193-
const fetchDownloads = $fetch<{ downloads: number }>(downloadUrl)
194-
.then(d => {
195-
console.error('[og-image-package] downloads response:', JSON.stringify(d))
196-
weeklyDownloads.value = d?.downloads ?? 0
197-
})
198-
.catch(err => {
199-
console.error('[og-image-package] downloads fetch failed:', downloadUrl, err?.message || err)
200-
})
180+
const fetchLikes = import.meta.test
181+
? // need deterministic likes for testing
182+
Promise.resolve().then(() => {
183+
totalLikes.value = 83
184+
})
185+
: $fetch<{ totalLikes: number }>(`/api/social/likes/${name}`).then(d => {
186+
totalLikes.value = d?.totalLikes ?? 0
187+
})
201188
202189
try {
203190
await Promise.all([
204191
refreshPkg().then(() => refreshRepoMeta()),
205-
fetchVariantData(),
192+
variant === 'code-tree'
193+
? fetchCodeTree()
194+
: variant === 'function-tree'
195+
? fetchFunctionTree()
196+
: undefined,
197+
fetchWeeklyEvolution(),
206198
fetchLikes,
207-
fetchDownloads,
208199
])
209200
} catch (err) {
210201
console.warn('[og-image-package] Failed to load data server-side:', err)
@@ -238,11 +229,11 @@ const sparklineSrc = computed(() => {
238229
const svg = [
239230
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" fill="none" preserveAspectRatio="none">`,
240231
`<defs><linearGradient id="af" x1="0" y1="0" x2="0" y2="1">`,
241-
`<stop offset="0%" stop-color="white" stop-opacity="0.07"/>`,
242-
`<stop offset="100%" stop-color="white" stop-opacity="0.005"/>`,
232+
`<stop offset="0%" stop-color="white" stop-opacity="0.018"/>`,
233+
`<stop offset="100%" stop-color="white" stop-opacity="0.001"/>`,
243234
`</linearGradient></defs>`,
244235
`<path d="M ${firstX},${height} L ${pathData} L ${lastX},${height} Z" fill="url(#af)"/>`,
245-
`<path d="M ${pathData}" stroke="rgba(255,255,255,0.18)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>`,
236+
`<path d="M ${pathData}" stroke="rgba(255,255,255,0.045)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>`,
246237
`</svg>`,
247238
].join('')
248239
@@ -251,15 +242,15 @@ const sparklineSrc = computed(() => {
251242
</script>
252243

253244
<template>
254-
<OgLayout>
245+
<OgLayout data-theme="dark">
255246
<div class="px-15 py-12 flex flex-col justify-center gap-12 h-full">
256247
<OgBrand :height="48" />
257248

258249
<div class="flex flex-col max-w-full gap-3">
259250
<div
260251
v-if="pkgNameParts.org"
261-
class="lg:text-5xl text-3xl opacity-50 font-mono tracking-tight leading-none"
262-
:style="{ textOverflow: 'ellipsis', lineClamp: 1 }"
252+
class="lg:text-5xl text-3xl font-mono tracking-tight leading-none"
253+
style="opacity: 0.5; text-overflow: ellipsis; line-clamp: 1"
263254
>
264255
{{ pkgNameParts.org }}
265256
</div>
@@ -268,65 +259,67 @@ const sparklineSrc = computed(() => {
268259
:class="
269260
(pkgNameParts.short?.length ?? 0) > 20 ? 'lg:text-6xl text-4xl' : 'lg:text-7xl text-5xl'
270261
"
271-
:style="{ textOverflow: 'ellipsis', lineClamp: 1, wordBreak: 'break-all' }"
262+
style="text-overflow: ellipsis; line-clamp: 1; word-break: break-all"
272263
>
273264
{{ pkgNameParts.short }}
274265
</div>
275266
<div
276267
v-if="version"
277-
class="pt-3 lg:text-4xl text-3xl opacity-70 font-mono tracking-tight leading-none"
278-
:style="{ textOverflow: 'ellipsis', lineClamp: 1 }"
268+
class="pt-3 lg:text-4xl text-3xl font-mono tracking-tight leading-none"
269+
style="opacity: 0.7; text-overflow: ellipsis; line-clamp: 1"
279270
>
280271
v{{ version }}
281272
</div>
282273
</div>
283274

284-
<div class="flex items-center gap-5 text-4xl text-fg-muted">
275+
<div class="flex flex-col gap-3 text-4xl text-fg-muted">
285276
<div v-if="repositoryUrl" class="flex items-center gap-2">
286277
<div
287278
class="i-simple-icons:github shrink-0 text-fg-muted"
288-
:style="{ width: '32px', height: '32px' }"
279+
style="width: 24px; height: 24px"
289280
/>
290-
<span v-if="repoRef" class="max-w-[500px]" :style="{ textOverflow: 'ellipsis' }">
281+
<span v-if="repoRef" class="max-w-[500px]" style="text-overflow: ellipsis">
291282
{{ repoRef.owner }}<span class="opacity-50">/</span>{{ repoRef.repo }}
292283
</span>
293284
<span v-else>{{ $t('package.links.repo') }}</span>
294285
</div>
295286

296-
<span v-if="formattedDownloads" class="flex items-center gap-2" data-testid="downloads">
297-
<div
298-
class="i-lucide:download shrink-0 text-fg-muted"
299-
:style="{ width: '32px', height: '32px' }"
300-
/>
301-
<span>{{ formattedDownloads }}/wk</span>
302-
</span>
287+
<div class="flex items-center gap-5">
288+
<span v-if="formattedDownloads" class="flex items-center gap-2" data-testid="downloads">
289+
<div
290+
class="i-lucide:download shrink-0 text-fg-muted"
291+
style="width: 24px; height: 24px"
292+
/>
293+
<span>{{ formattedDownloads }}/wk</span>
294+
</span>
303295

304-
<span v-if="formattedStars" class="flex items-center gap-2" data-testid="stars">
305-
<div
306-
class="i-lucide:star shrink-0 text-fg-muted"
307-
:style="{ width: '32px', height: '32px', fill: 'white' }"
308-
/>
309-
<span>{{ formattedStars }}</span>
310-
</span>
296+
<span v-if="formattedStars" class="flex items-center gap-2" data-testid="stars">
297+
<div
298+
class="i-lucide:star shrink-0 text-fg-muted"
299+
style="width: 24px; height: 24px; fill: white"
300+
/>
301+
<span>{{ formattedStars }}</span>
302+
</span>
311303

312-
<span v-if="formattedLikes" class="flex items-center gap-2" data-testid="likes">
313-
<div
314-
class="i-lucide:heart shrink-0 text-fg-muted"
315-
:style="{ width: '32px', height: '32px', fill: 'white' }"
316-
/>
317-
<span>{{ formattedLikes }}</span>
318-
</span>
304+
<span v-if="formattedLikes" class="flex items-center gap-2" data-testid="likes">
305+
<div
306+
class="i-lucide:heart shrink-0 text-fg-muted"
307+
style="width: 24px; height: 24px; fill: white"
308+
/>
309+
<span>{{ formattedLikes }}</span>
310+
</span>
319311

320-
<div
321-
v-if="pkg?.license && !pkg.license.includes(' ')"
322-
class="flex items-center gap-2"
323-
data-testid="license"
324-
>
325312
<div
326-
class="i-lucide:scale shrink-0 text-fg-subtle self-center"
327-
:style="{ width: '32px', height: '32px' }"
328-
/>
329-
<span>{{ pkg.license }}</span>
313+
v-if="pkg?.license && !pkg.license.includes(' ')"
314+
class="flex items-center gap-2"
315+
data-testid="license"
316+
>
317+
<div
318+
class="i-lucide:scale shrink-0 text-fg-subtle self-center"
319+
style="width: 24px; height: 24px"
320+
/>
321+
<span>{{ pkg.license }}</span>
322+
</div>
330323
</div>
331324
</div>
332325
</div>
@@ -335,71 +328,84 @@ const sparklineSrc = computed(() => {
335328
<img
336329
v-if="variant === 'download-chart' && sparklineSrc"
337330
:src="sparklineSrc"
338-
class="absolute force-left-0 bottom-0 w-full h-[65%] opacity-30"
331+
class="absolute force-left-0 bottom-0 w-full h-[65%]"
339332
/>
340333

341334
<!-- Code tree variant -->
342335
<div
343336
v-else-if="variant === 'code-tree' && treeRows.length"
344-
class="text-fg-muted absolute force-right-8 top-8 bottom-8 w-[340px] flex flex-col gap-0 opacity-30 overflow-hidden font-mono text-4.5 leading-7"
337+
class="text-fg-muted absolute force-right-8 top-8 bottom-8 w-[340px] flex flex-col gap-0 opacity-30 overflow-hidden font-mono text-4.5 leading-8"
345338
>
346339
<div
347340
v-for="(row, i) in treeRows"
348341
:key="i"
349-
class="flex items-center whitespace-nowrap text-fg"
342+
class="flex items-center gap-3 whitespace-nowrap text-fg"
350343
:style="{ paddingLeft: `${row.depth * 20}px` }"
351344
>
352-
<span
345+
<div
353346
v-if="row.isDir"
354-
class="w-5 h-5 text-fg-muted shrink-0 force-mr-1.5 i-lucide:folder"
347+
class="text-fg-muted shrink-0 i-lucide:folder"
348+
style="width: 20px; height: 20px"
349+
/>
350+
<div
351+
v-else
352+
class="text-fg-muted shrink-0 i-lucide:file"
353+
style="width: 20px; height: 20px"
355354
/>
356-
<span v-else class="w-5 h-5 text-fg-muted shrink-0 force-mr-1.5 i-lucide:file" />
357355
<span class="text-fg-muted">{{ row.name }}</span>
358356
</div>
359357
</div>
360358

361359
<!-- Function tree variant (API symbols) -->
362360
<div
363361
v-else-if="variant === 'function-tree' && symbolRows.length"
364-
class="absolute force-right-8 top-8 bottom-8 w-[340px] flex flex-col gap-0 opacity-30 overflow-hidden font-mono text-4.5 leading-7"
362+
class="absolute force-right-8 top-8 bottom-8 w-[340px] flex flex-col gap-0 opacity-30 overflow-hidden font-mono text-4.5 leading-8"
365363
>
366364
<div
367365
v-for="(row, i) in symbolRows"
368366
:key="i"
369-
class="flex items-center whitespace-nowrap text-fg"
367+
class="flex items-center gap-3 whitespace-nowrap text-fg"
370368
:style="{ paddingLeft: row.kind === 'symbol' ? '20px' : '0' }"
371369
>
372-
<span
370+
<div
373371
v-if="row.icon === 'i-lucide:parentheses'"
374-
class="w-5 h-5 shrink-0 text-fg-muted force-mr-1.5 i-lucide:parentheses"
372+
class="shrink-0 text-fg-muted i-lucide:parentheses"
373+
style="width: 20px; height: 20px"
375374
/>
376-
<span
375+
<div
377376
v-else-if="row.icon === 'i-lucide:box'"
378-
class="w-5 h-5 shrink-0 text-fg-muted force-mr-1.5 i-lucide:box"
377+
class="shrink-0 text-fg-muted i-lucide:box"
378+
style="width: 20px; height: 20px"
379379
/>
380-
<span
380+
<div
381381
v-else-if="row.icon === 'i-lucide:puzzle'"
382-
class="w-5 h-5 shrink-0 text-fg-muted force-mr-1.5 i-lucide:puzzle"
382+
class="shrink-0 text-fg-muted i-lucide:puzzle"
383+
style="width: 20px; height: 20px"
383384
/>
384-
<span
385+
<div
385386
v-else-if="row.icon === 'i-lucide:type'"
386-
class="w-5 h-5 shrink-0 text-fg-muted force-mr-1.5 i-lucide:type"
387+
class="shrink-0 text-fg-muted i-lucide:type"
388+
style="width: 20px; height: 20px"
387389
/>
388-
<span
390+
<div
389391
v-else-if="row.icon === 'i-lucide:variable'"
390-
class="w-5 h-5 shrink-0 text-fg-muted force-mr-1.5 i-lucide:variable"
392+
class="shrink-0 text-fg-muted i-lucide:variable"
393+
style="width: 20px; height: 20px"
391394
/>
392-
<span
395+
<div
393396
v-else-if="row.icon === 'i-lucide:list'"
394-
class="w-5 h-5 shrink-0 text-fg-muted force-mr-1.5 i-lucide:list"
397+
class="shrink-0 text-fg-muted i-lucide:list"
398+
style="width: 20px; height: 20px"
395399
/>
396-
<span
400+
<div
397401
v-else-if="row.icon === 'i-lucide:package'"
398-
class="w-5 h-5 shrink-0 text-fg-muted force-mr-1.5 i-lucide:package"
402+
class="shrink-0 text-fg-muted i-lucide:package"
403+
style="width: 20px; height: 20px"
399404
/>
400-
<span
401-
v-else-if="row.kind === 'symbol'"
402-
class="w-5 h-5 shrink-0 text-fg-muted force-mr-1.5 i-lucide:code"
405+
<div
406+
v-else
407+
class="shrink-0 text-fg-muted i-lucide:code"
408+
style="width: 20px; height: 20px"
403409
/>
404410
<span class="text-fg-muted" :class="row.kind === 'section' ? 'text-4 mt-1' : ''">{{
405411
row.name

nuxt.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ export default defineNuxtConfig({
294294

295295
ogImage: {
296296
enabled: !isStorybook,
297+
cacheMaxAgeSeconds: 60 * 60 * 24, // 1 day, download counts change daily
297298
security: {
298299
// Reuse image-proxy HMAC secret to avoid managing a second secret.
299300
// Strict mode only activates when a secret is present (CI builds without one).

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@
6969
"@shikijs/langs": "4.0.2",
7070
"@shikijs/markdown-exit": "4.0.2",
7171
"@shikijs/themes": "4.0.2",
72-
"@takumi-rs/core": "1.0.0-rc.17",
73-
"@takumi-rs/wasm": "1.0.0-rc.17",
72+
"@takumi-rs/core": "1.0.1",
73+
"@takumi-rs/wasm": "1.0.1",
7474
"@unocss/nuxt": "66.6.7",
7575
"@unocss/preset-wind4": "66.6.7",
7676
"@upstash/redis": "1.37.0",
@@ -91,7 +91,7 @@
9191
"marked": "17.0.4",
9292
"module-replacements": "2.11.0",
9393
"nuxt": "4.3.1",
94-
"nuxt-og-image": "6.3.7",
94+
"nuxt-og-image": "6.3.8",
9595
"ofetch": "1.5.1",
9696
"ohash": "2.0.11",
9797
"perfect-debounce": "2.1.0",

0 commit comments

Comments
 (0)