@@ -8,14 +8,11 @@ const props = defineProps<{
88}>()
99
1010// Fetch README for specific version if requested, otherwise latest
11- const { data } = useLazyFetch <ReadmeResponse >(
12- () => {
13- const base = ` /api/registry/readme/${props .packageName } `
14- const version = props .requestedVersion
15- return version ? ` ${base }/v/${version } ` : base
16- },
17- { default : () => ({ html: ' ' , md: ' ' , playgroundLinks: [], toc: [] }) },
18- )
11+ const { data, status, error } = useLazyFetch <ReadmeResponse >(() => {
12+ const base = ` /api/registry/readme/${props .packageName } `
13+ const version = props .requestedVersion
14+ return version ? ` ${base }/v/${version } ` : base
15+ })
1916
2017// Track active TOC item based on scroll position
2118const tocItems = computed (() => data .value ?.toc ?? [])
@@ -37,12 +34,17 @@ const { copied: copiedReadme, copy: copyReadme } = useClipboard({
3734 </h2 >
3835 <div class =" flex gap-2" >
3936 <!-- Copy readme as Markdown button -->
40- <TooltipApp v-if =" data?.md" :text =" $t('package.readme.copy_as_markdown')" position =" bottom" >
37+ <TooltipApp
38+ v-if =" data?.md || status === 'pending' || status === 'idle'"
39+ :text =" $t('package.readme.copy_as_markdown')"
40+ position =" bottom"
41+ >
4142 <ButtonBase
4243 @click =" copyReadme()"
4344 :aria-pressed =" copiedReadme"
4445 :aria-label =" copiedReadme ? $t('common.copied') : $t('package.readme.copy_as_markdown')"
4546 :classicon =" copiedReadme ? 'i-carbon:checkmark' : 'i-simple-icons:markdown'"
47+ :disabled =" status === 'pending' || status === 'idle'"
4648 >
4749 {{ copiedReadme ? $t('common.copied') : $t('common.copy') }}
4850 </ButtonBase >
@@ -51,12 +53,41 @@ const { copied: copiedReadme, copy: copyReadme } = useClipboard({
5153 v-if =" data?.toc && data.toc.length > 1"
5254 :toc =" data.toc"
5355 :active-id =" activeTocId"
56+ :disabled =" !data || !data.toc"
5457 />
58+ <ButtonBase
59+ v-else-if =" status === 'pending' || status === 'idle'"
60+ disabled
61+ classicon =" i-carbon:list"
62+ block
63+ >
64+ <span class =" i-carbon:chevron-down w-3 h-3" aria-hidden =" true" />
65+ </ButtonBase >
5566 </div >
5667 </div >
5768
5869 <!-- eslint-disable vue/no-v-html -- HTML is sanitized server-side -->
59- <Readme v-if =" data?.html" :html =" data.html" />
70+ <Readme v-if =" status === 'success' && data?.html" :html =" data.html" />
71+ <div class =" space-y-4" v-else-if =" status === 'pending' || status === 'idle'" >
72+ <!-- Heading -->
73+ <SkeletonBlock class =" h-7 w-2/3" />
74+ <!-- Paragraphs -->
75+ <SkeletonBlock class =" h-4 w-full" />
76+ <SkeletonBlock class =" h-4 w-full" />
77+ <SkeletonBlock class =" h-4 w-4/5" />
78+ <!-- Gap for section break -->
79+ <SkeletonBlock class =" h-6 w-1/2 mt-6" />
80+ <SkeletonBlock class =" h-4 w-full" />
81+ <SkeletonBlock class =" h-4 w-full" />
82+ <SkeletonBlock class =" h-4 w-3/4" />
83+ <!-- Code block placeholder -->
84+ <SkeletonBlock class =" h-24 w-full rounded-lg mt-4" />
85+ <SkeletonBlock class =" h-4 w-full" />
86+ <SkeletonBlock class =" h-4 w-5/6" />
87+ </div >
88+ <div v-else-if =" status === 'error'" class =" text-red-500" >
89+ {{ $t('package.readme.load_error') }}
90+ </div >
6091 <p v-else class =" text-fg-muted italic" >
6192 {{ $t('package.readme.no_readme') }}
6293 <a
0 commit comments