Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cf847d8
feat(inspector): adopt @antfu/design and set up Storybook
antfubot Jul 2, 2026
9c106f7
feat(inspector): replace in-house components with @antfu/design equiv…
antfubot Jul 2, 2026
3cfdd98
docs(inspector): generate the Storybook Overview from stories on disk
antfubot Jul 2, 2026
9774f01
fix(inspector): restore font fallback stacks lost to the design preset
antfubot Jul 2, 2026
501001f
refactor(inspector): let presetWebFonts own the DM Sans/DM Mono stacks
antfubot Jul 3, 2026
fb2145f
chore: remove stray file
antfubot Jul 3, 2026
727645e
chore(inspector): upgrade @antfu/design to 0.2.2, drop the font worka…
antfubot Jul 13, 2026
81e8270
chore: merge main, resolve conflicts
antfubot Jul 21, 2026
8c5b37d
chore(inspector): upgrade @antfu/design to 0.3.2
antfubot Jul 21, 2026
a31ed46
chore: merge main (exclude-dev-deps filter, e2e fix), resolve conflicts
antfubot Jul 21, 2026
59d5c05
chore: merge main (config-file export, deps), resolve conflicts
antfubot Jul 29, 2026
acb5003
refactor(inspector): migrate SettingsConfigDialog to OverlayModal
antfubot Jul 29, 2026
5182ba7
chore: merge main into branch (fix ancestry — content already reconci…
antfubot Jul 29, 2026
c80185e
chore: merge main (report nav fix, deps), resolve conflicts
antfubot Aug 3, 2026
06108de
chore: adjust
antfu Aug 3, 2026
505c3c8
chore: update design
antfu Aug 3, 2026
0d0f60f
fix(inspector): use DisplaySafeImage with fallback for author avatars
antfubot Aug 3, 2026
38a876b
fix(inspector): restate color-base on body for dark mode
antfubot Aug 3, 2026
1f58021
fix(inspector): raise badge-color contrast to WCAG AA
antfubot Aug 3, 2026
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 @@ -61,12 +64,17 @@
"verkit": "catalog:deps"
},
"devDependencies": {
"@antfu/design": "catalog:dev",
"@storybook/addon-docs": "catalog:storybook",
"@storybook/vue3-vite": "catalog:storybook",
"@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,7 +83,9 @@
"idb-keyval": "catalog:frontend",
"modern-screenshot": "catalog:frontend",
"nanovis": "catalog:frontend",
"reka-ui": "catalog:frontend",
"rollup": "catalog:bundling",
"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
Expand Up @@ -29,7 +29,7 @@ const parentStack = computed(() => {
<template v-for="node, idx of parentStack" :key="node.id">
<div v-if="idx > 0" i-ph-arrow-right-bold text-sm op-fade />
<button
hover="bg-active" rounded px1
hover="bg-hover" rounded px1
@click="emit('select', node)"
>
<DisplayPackageSpec v-if="node.meta" :pkg="node.meta" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const emit = defineEmits<{
<template v-for="child of selected.children" :key="child.id">
<button
ws-nowrap text-nowrap text-left overflow-hidden text-ellipsis text-sm
hover="bg-active" rounded px2
hover="bg-hover" rounded px2
@click="emit('select', child)"
>
<span v-if="child.meta && child.meta === selected?.meta" text-primary>(self)</span>
Expand All @@ -36,7 +36,7 @@ const emit = defineEmits<{

<button
relative flex="~ gap-1 items-center"
hover="bg-active" rounded
hover="bg-hover" rounded
@click="emit('select', child)"
>
<div
Expand Down
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 All @@ -28,22 +27,22 @@ const href = computed(() => {
:target="href ? '_blank' : undefined"
flex="~ gap-1.5 items-center" of-hidden text-ellipsis
:style="{ fontSize: `${props.size * 0.5}px`, height: `${props.size}px` }"
:class="href ? 'hover:bg-active' : ''"
:class="href ? 'hover:bg-hover' : ''"
border="y r base rounded-full" pr-2
>
<template
v-if="author.type === 'github'"
>
<SafeImage
<DisplaySafeImage
:src="author.avatar"
bg-active border="~ base rounded-full"
:alt="author.github"
class="rounded-full bg-ambient border border-base object-cover"
: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>
</DisplaySafeImage>
<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>`,
}),
}
Loading
Loading