Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ packages/node-modules-inspector/src/app/public/fonts
packages/node-modules-inspector/src/public/fonts
packages/node-modules-inspector/runtime
.vite-inspect
storybook-static
.ghfs
test/e2e/.fixtures
test/e2e/.results
Expand Down
21 changes: 21 additions & 0 deletions packages/node-modules-inspector/.storybook/docs-dark.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* When the preview is dark, recolor the autodocs/MDX surfaces to match the app
tokens (Storybook's docs theme is otherwise static light). */
.dark .sbdocs-wrapper,
.dark .sbdocs.sbdocs-content {
background: #111;
}
.dark .sbdocs-preview,
.dark .docs-story {
background: #111;
border-color: #8882;
}
.dark .sbdocs h1,
.dark .sbdocs h2,
.dark .sbdocs h3,
.dark .sbdocs p,
.dark .sbdocs a {
color: #ccc;
}
.dark .sbdocs h2 {
border-color: #8882;
}
34 changes: 34 additions & 0 deletions packages/node-modules-inspector/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { StorybookConfig } from '@storybook/vue3-vite'
import { fileURLToPath } from 'node:url'
import Vue from '@vitejs/plugin-vue'
import Unocss from 'unocss/vite'
import { mergeConfig } from 'vite'

const config: StorybookConfig = {
// Stories are co-located next to the app's presentational components; the
// Overview is one MDX page that references the others via doc blocks.
stories: [
'../src/app/components/**/*.mdx',
'../src/app/components/**/*.stories.@(ts|js)',
],
addons: ['@storybook/addon-docs'],
framework: {
name: '@storybook/vue3-vite',
// The app + `@antfu/design` ship raw `.vue`; disable Storybook's Vue docgen
// so it doesn't re-parse plugin-vue-compiled output ("missing end tag").
options: { docgen: false },
},
async viteFinal(base) {
return mergeConfig(base, {
// Storybook runs its own Vite (not Nuxt): add plugin-vue to compile SFCs
// and reuse the app's UnoCSS config so tokens/fonts match the app exactly.
plugins: [
Vue(),
Unocss({ configFile: fileURLToPath(new URL('../src/uno.config.ts', import.meta.url)) }),
],
optimizeDeps: { exclude: ['@antfu/design'] },
})
},
}

export default config
11 changes: 11 additions & 0 deletions packages/node-modules-inspector/.storybook/manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { GLOBALS_UPDATED } from 'storybook/internal/core-events'
import { addons } from 'storybook/manager-api'
import { themes } from 'storybook/theming'

// Sync the Storybook manager (chrome) theme with the preview's `theme` global so
// the toolbar toggle flips both, instead of leaving the chrome mismatched.
addons.register('nmi/theme-sync', (api) => {
const apply = (theme?: string): void => api.setOptions({ theme: theme === 'dark' ? themes.dark : themes.light })
apply(api.getGlobals().theme)
api.getChannel()?.on(GLOBALS_UPDATED, ({ globals }: { globals?: { theme?: string } }) => apply(globals?.theme))
})
50 changes: 50 additions & 0 deletions packages/node-modules-inspector/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Preview } from '@storybook/vue3-vite'
import { GLOBALS_UPDATED } from 'storybook/internal/core-events'
import { addons } from 'storybook/preview-api'
import { h } from 'vue'
import 'virtual:uno.css'
import 'floating-vue/dist/style.css'
import '@antfu/design/styles.css'
import './docs-dark.css'

// The manager (chrome) and preview (iframe) are separate documents. Toggle the
// preview root's `dark` class straight from the `theme` global so the whole
// surface — docs/MDX pages included, not just decorated stories — follows along.
addons.getChannel().on(GLOBALS_UPDATED, ({ globals }: { globals?: { theme?: string } }) => {
document.documentElement.classList.toggle('dark', globals?.theme === 'dark')
})

const preview: Preview = {
parameters: {
layout: 'centered',
controls: { expanded: true },
options: {
// Overview lands first; the rest fall back to alphabetical.
storySort: { order: ['Overview', 'Display', 'UI'] },
},
},
globalTypes: {
theme: {
description: 'Color scheme',
defaultValue: 'light',
toolbar: {
title: 'Theme',
icon: 'circlehollow',
items: [
{ value: 'light', title: 'Light', icon: 'sun' },
{ value: 'dark', title: 'Dark', icon: 'moon' },
],
dynamicTitle: true,
},
},
},
decorators: [
(story, context) => {
const dark = context.globals.theme === 'dark'
document.documentElement.classList.toggle('dark', dark)
return () => h('div', { class: 'p-8 bg-base color-base font-sans' }, [h(story())])
},
],
}

export default preview
2 changes: 2 additions & 0 deletions packages/node-modules-inspector/.storybook/shims.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare module 'virtual:uno.css'
declare module '*.css'
18 changes: 18 additions & 0 deletions packages/node-modules-inspector/.storybook/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"types": ["node"],
"allowImportingTsExtensions": true,
"strict": true,
"noEmit": true,
"skipLibCheck": true
},
"include": [
"**/*.ts",
"**/*.d.ts"
]
}
10 changes: 10 additions & 0 deletions packages/node-modules-inspector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"skills"
],
"scripts": {
"storybook": "storybook dev -p 6006 --no-open",
"storybook:build": "storybook build",
"docs:overview": "node scripts/gen-overview.mjs",
"dev": "pnpm run -r stub && (cd src && ROLLDOWN_OPTIONS_VALIDATION=loose nuxi dev)",
"stub": "unbuild --stub",
"build": "pnpm run wc:prepare && (cd src && ROLLDOWN_OPTIONS_VALIDATION=loose nuxi build) && unbuild",
Expand Down Expand Up @@ -60,13 +63,18 @@
"valibot": "catalog:deps"
},
"devDependencies": {
"@antfu/design": "catalog:dev",
"@storybook/addon-docs": "catalog:storybook",
"@storybook/vue3-vite": "catalog:storybook",
"@types/semver": "catalog:types",
"@unocss/nuxt": "catalog:bundling",
"@valibot/to-json-schema": "catalog:testing",
"@vitejs/plugin-vue": "catalog:frontend",
"@vueuse/nuxt": "catalog:bundling",
"@webcontainer/api": "catalog:frontend",
"@xterm/addon-fit": "catalog:frontend",
"@xterm/xterm": "catalog:frontend",
"colorjs.io": "catalog:dev",
"d3": "catalog:frontend",
"d3-hierarchy": "catalog:frontend",
"d3-shape": "catalog:frontend",
Expand All @@ -75,8 +83,10 @@
"idb-keyval": "catalog:frontend",
"modern-screenshot": "catalog:frontend",
"nanovis": "catalog:frontend",
"reka-ui": "catalog:frontend",
"rollup": "catalog:bundling",
"semver": "catalog:deps",
"storybook": "catalog:storybook",
"theme-vitesse": "catalog:frontend",
"vite-hot-client": "catalog:frontend"
}
Expand Down
55 changes: 55 additions & 0 deletions packages/node-modules-inspector/scripts/gen-overview.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Regenerates the Storybook Overview page (src/app/components/Overview.mdx)
// from the stories on disk: one linked `### [Name](…)` title + a <Canvas> per
// variation, grouped by the category in each story's `title`.
// Run from the package root: `node scripts/gen-overview.mjs` (pnpm docs:overview)
import { readdirSync, readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'

const base = 'src/app/components'

const files = []
for (const dir of readdirSync(base, { withFileTypes: true })) {
if (!dir.isDirectory())
continue
for (const f of readdirSync(join(base, dir.name)).filter(f => f.endsWith('.stories.ts')).sort()) {
const name = f.replace('.stories.ts', '')
const src = readFileSync(join(base, dir.name, f), 'utf8')
const title = src.match(/title:\s*'([^']+)'/)?.[1] ?? `${dir.name}/${name}`
const category = title.split('/')[0]
const exps = [...src.matchAll(/export const (\w+)/g)].map(m => m[1])
files.push({
dir: dir.name,
name,
category,
exps,
id: `${title.toLowerCase().replaceAll('/', '-')}--docs`,
})
}
}
files.sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name))

let out = `import { Canvas, Meta } from '@storybook/addon-docs/blocks'\n`
for (const f of files) out += `import * as ${f.category}${f.name} from './${f.dir}/${f.name}.stories'\n`
out += `
<Meta title="Overview" />

# Node Modules Inspector — Components

App-specific presentational building blocks, themed end-to-end by the
[\`@antfu/design\`](https://github.com/antfu/design) UnoCSS preset. Generic
primitives (badges, versions, avatars, checkboxes, drawers) come from
\`@antfu/design\` directly and are documented in that package's own Storybook —
only components unique to this app live here. Toggle the theme from the
toolbar to check both light and dark. Each tile is a live story; click a
component name to open its full page.
`
let cur = ''
for (const f of files) {
if (f.category !== cur) {
out += `\n## ${f.category}\n`
cur = f.category
}
out += `\n### [${f.name}](/?path=/docs/${f.id})\n\n${f.exps.map(e => `<Canvas of={${f.category}${f.name}.${e}} />`).join('\n')}\n`
}
writeFileSync(join(base, 'Overview.mdx'), out)
console.log(`Overview.mdx: ${files.length} components`)
1 change: 1 addition & 0 deletions packages/node-modules-inspector/src/app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Entry from './entries/index'
import { setupQuery } from './state/query'

import 'floating-vue/dist/style.css'
import '@antfu/design/styles.css'
import './styles/global.css'
import './composables/dark'

Expand Down
30 changes: 30 additions & 0 deletions packages/node-modules-inspector/src/app/components/Overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Canvas, Meta } from '@storybook/addon-docs/blocks'
import * as DisplayClusterBadge from './display/ClusterBadge.stories'
import * as UILogo from './ui/Logo.stories'

<Meta title="Overview" />

# Node Modules Inspector — Components

App-specific presentational building blocks, themed end-to-end by the
[`@antfu/design`](https://github.com/antfu/design) UnoCSS preset. Generic
primitives (badges, versions, avatars, checkboxes, drawers) come from
`@antfu/design` directly and are documented in that package's own Storybook —
only components unique to this app live here. Toggle the theme from the
toolbar to check both light and dark. Each tile is a live story; click a
component name to open its full page.

## Display

### [ClusterBadge](/?path=/docs/display-clusterbadge--docs)

<Canvas of={DisplayClusterBadge.Namespaced} />
<Canvas of={DisplayClusterBadge.Plain} />
<Canvas of={DisplayClusterBadge.Gallery} />

## UI

### [Logo](/?path=/docs/ui-logo--docs)

<Canvas of={UILogo.Default} />
<Canvas of={UILogo.Sizes} />
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import type { ParsedAuthor } from 'node-modules-tools/utils'
import { computed } from 'vue'
import SafeImage from './SafeImage.vue'

const props = withDefaults(defineProps<{
author: ParsedAuthor
Expand Down Expand Up @@ -34,16 +33,12 @@ const href = computed(() => {
<template
v-if="author.type === 'github'"
>
<SafeImage
<DisplayAvatar
:src="author.avatar"
bg-active border="~ base rounded-full"
:style="{ width: `${props.size}px`, height: `${props.size}px` }"
crossorigin="anonymous"
>
<template #fallback>
<div i-ph-user-circle-duotone :style="{ width: `${props.size}px`, height: `${props.size}px` }" op-fade />
</template>
</SafeImage>
:name="author.github"
:size="props.size"
class="bg-active border border-base"
/>
<span font-mono>{{ author.github }}</span>
</template>
<template v-else>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import ClusterBadge from './ClusterBadge.vue'

const meta = {
title: 'Display/ClusterBadge',
component: ClusterBadge,
tags: ['autodocs'],
args: { cluster: 'catalog:frontend' },
} satisfies Meta<typeof ClusterBadge>

export default meta
type Story = StoryObj<typeof meta>

export const Namespaced: Story = { args: { cluster: 'catalog:frontend' } }
export const Plain: Story = { args: { cluster: 'workspace' } }

export const Gallery: Story = {
render: () => ({
components: { ClusterBadge },
template: `<div class="flex flex-wrap items-center gap-2">
<ClusterBadge cluster="catalog:frontend" />
<ClusterBadge cluster="catalog:deps" />
<ClusterBadge cluster="workspace" />
<ClusterBadge cluster="prod" />
</div>`,
}),
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type { PackageNode } from 'node-modules-tools'
import { computed } from 'vue'
import { getPublishTime } from '../../state/payload'
import { settings } from '../../state/settings'

const props = withDefaults(
defineProps<{
Expand All @@ -20,22 +21,10 @@ const date = computed(() => props.time
? getPublishTime(props.pkg)
: undefined,
)

const formatter = Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
const dateTitle = computed(() => date.value ? formatter.format(date.value) : null)
</script>

<template>
<DisplayDurationBadge
v-if="date"
v-tooltip="dateTitle"
:title="dateTitle"
:ms="Date.now() - +date"
:colorize="props.colorize"
mode="day"
/>
<div v-if="date" class="px-0.4em py-0.2em line-height-none bg-gray:5 text-sm w-max">
<DisplayDate :date="date" :colorize="settings.colorizePackageSize || props.colorize" />
</div>
</template>
Loading