diff --git a/app/components/Package/Playgrounds.vue b/app/components/Package/Playgrounds.vue index e498d7433..5d1db8070 100644 --- a/app/components/Package/Playgrounds.vue +++ b/app/components/Package/Playgrounds.vue @@ -20,6 +20,7 @@ const providerIcons: Record = { 'solid-playground': 'i-simple-icons:solid', 'svelte-playground': 'i-simple-icons:svelte', 'tailwind-playground': 'i-simple-icons:tailwindcss', + 'storybook': 'i-simple-icons:storybook', } // Map provider id to color class @@ -37,6 +38,7 @@ const providerColors: Record = { 'solid-playground': 'text-provider-solid', 'svelte-playground': 'text-provider-svelte', 'tailwind-playground': 'text-provider-tailwind', + 'storybook': 'text-provider-storybook', } function getIcon(provider: string): string { diff --git a/app/composables/npm/usePackage.ts b/app/composables/npm/usePackage.ts index 1bfef0198..96bab1592 100644 --- a/app/composables/npm/usePackage.ts +++ b/app/composables/npm/usePackage.ts @@ -120,6 +120,14 @@ export function transformPackument( license = license.type } + // Extract storybook field from the requested version (custom package.json field) + const requestedPkgVersion = requestedVersion ? pkg.versions[requestedVersion] : null + const rawStorybook = requestedPkgVersion?.storybook + const storybook = + rawStorybook && typeof rawStorybook === 'object' && 'url' in rawStorybook + ? ({ url: rawStorybook.url } as { url: string }) + : undefined + return { '_id': pkg._id, '_rev': pkg._rev, @@ -134,6 +142,7 @@ export function transformPackument( 'keywords': pkg.keywords, 'repository': pkg.repository, 'bugs': pkg.bugs, + ...(storybook && { storybook }), 'requestedVersion': versionData, 'versions': filteredVersions, 'securityVersions': securityVersions, diff --git a/app/pages/package/[[org]]/[name].vue b/app/pages/package/[[org]]/[name].vue index 92b18e4ec..8744e2e52 100644 --- a/app/pages/package/[[org]]/[name].vue +++ b/app/pages/package/[[org]]/[name].vue @@ -109,6 +109,21 @@ const { data: readmeData } = useLazyFetch( { default: () => ({ html: '', mdExists: false, playgroundLinks: [], toc: [] }) }, ) +const playgroundLinks = computed(() => [ + ...readmeData.value.playgroundLinks, + // Libraries with a storybook field in package.json contain a link to their deployed playground + ...(pkg.value?.storybook?.url + ? [ + { + url: pkg.value.storybook.url, + provider: 'storybook', + providerName: 'Storybook', + label: 'Storybook', + }, + ] + : []), +]) + const { data: readmeMarkdownData, status: readmeMarkdownStatus, @@ -1357,10 +1372,7 @@ const showSkeleton = shallowRef(false) /> - + diff --git a/shared/types/npm-registry.ts b/shared/types/npm-registry.ts index 00558913c..f7d7cdd12 100644 --- a/shared/types/npm-registry.ts +++ b/shared/types/npm-registry.ts @@ -83,6 +83,7 @@ export interface SlimPackument { 'keywords'?: string[] 'repository'?: { type?: string; url?: string; directory?: string } 'bugs'?: { url?: string; email?: string } + 'storybook'?: { url: string } /** current version */ 'requestedVersion': SlimPackumentVersion | null /** Only includes dist-tag versions (with installScripts info added per version) */ diff --git a/uno.config.ts b/uno.config.ts index d0821dc7f..506c8977d 100644 --- a/uno.config.ts +++ b/uno.config.ts @@ -104,6 +104,7 @@ export default defineConfig({ solid: '#2C4F7C', svelte: '#FF3E00', tailwind: '#06B6D4', + storybook: '#FF4785', }, }, animation: {