Skip to content

Commit b6d4a9c

Browse files
authored
Merge branch 'main' into compare-scatter-with-selectable-axes
2 parents 92d3844 + 925bf16 commit b6d4a9c

File tree

10 files changed

+510
-32
lines changed

10 files changed

+510
-32
lines changed

.coderabbit.yaml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
language: en-GB
2+
3+
reviews:
4+
profile: chill
5+
# Keep the high-level summary enabled (default), but place it in the
6+
# walkthrough comment instead of relying on PR description updates.
7+
high_level_summary_in_walkthrough: true
8+
review_status: true
9+
review_details: false
10+
changed_files_summary: true
11+
sequence_diagrams: true
12+
estimate_code_review_effort: false
13+
assess_linked_issues: true
14+
related_issues: true
15+
related_prs: true
16+
suggested_labels: false
17+
suggested_reviewers: true
18+
in_progress_fortune: false
19+
poem: false
20+
21+
slop_detection:
22+
enabled: true
23+
label: '007'
24+
25+
auto_review:
26+
auto_pause_after_reviewed_commits: 5
27+
labels:
28+
- '!release'
29+
ignore_title_keywords:
30+
- 'WIP'
31+
- '[skip-review]'
32+
- 'chore(release)'
33+
ignore_usernames:
34+
- 'renovate[bot]'
35+
- 'dependabot[bot]'
36+
- 'github-actions[bot]'
37+
38+
# Built-in PR metadata/content checks. Modes are: off, warning, error.
39+
pre_merge_checks:
40+
docstrings:
41+
mode: off
42+
title:
43+
mode: error
44+
description:
45+
mode: warning
46+
issue_assessment:
47+
mode: warning
48+
49+
tools:
50+
gitleaks:
51+
enabled: true
52+
osvScanner:
53+
enabled: true
54+
actionlint:
55+
enabled: true
56+
yamllint:
57+
enabled: true
58+
shellcheck:
59+
enabled: true
60+
dotenvLint:
61+
enabled: true
62+
63+
# Disable tools redundant with our own CI
64+
eslint:
65+
enabled: false
66+
biome:
67+
enabled: false
68+
oxc:
69+
enabled: false
70+
markdownlint:
71+
enabled: false
72+
languagetool:
73+
enabled: false
74+
github-checks:
75+
enabled: false
76+
77+
# Security-related, but not a good fit for this repo
78+
checkov:
79+
enabled: false
80+
trivy:
81+
enabled: false
82+
opengrep:
83+
enabled: false

.storybook/main.ts

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type { StorybookConfig } from '@storybook-vue/nuxt'
2+
import { readFileSync } from 'node:fs'
3+
import { resolve } from 'node:path'
24

35
const config = {
46
stories: [
@@ -21,29 +23,95 @@ const config = {
2123
async viteFinal(newConfig) {
2224
newConfig.plugins ??= []
2325

26+
// Fix: nuxt:components:imports-alias relies on internal Nuxt state that is
27+
// cleaned up after nuxt.close() in @storybook-vue/nuxt's loadNuxtViteConfig.
28+
// When that state is gone, `import X from '#components'` is left unresolved
29+
// and Vite 8 falls through to package-subpath resolution, which fails with
30+
// "Missing '#components' specifier in 'nuxt' package".
31+
// This plugin intercepts #components first and serves a virtual module built
32+
// from the components.d.ts written during the same Nuxt boot.
33+
// Resolve the Nuxt build dir from Vite's alias map, which can be either a
34+
// plain-object (Record<string, string>) or Vite's resolved array form
35+
// (readonly Alias[] where find is string | RegExp). We must handle both
36+
// without casting to Record<string, string>, which would be unsound for the
37+
// array form.
38+
const aliases = newConfig.resolve?.alias
39+
const buildDir = (() => {
40+
if (!aliases) return undefined
41+
if (Array.isArray(aliases)) {
42+
const entry = aliases.find(a => a.find === '#build')
43+
return typeof entry?.replacement === 'string' ? entry.replacement : undefined
44+
}
45+
const value = (aliases as Record<string, unknown>)['#build']
46+
return typeof value === 'string' ? value : undefined
47+
})()
48+
newConfig.plugins.unshift({
49+
name: 'storybook-nuxt-components',
50+
enforce: 'pre',
51+
resolveId(id) {
52+
if (id === '#components') return '\0virtual:#components'
53+
return null
54+
},
55+
load(id) {
56+
if (id !== '\0virtual:#components') return
57+
if (!buildDir) {
58+
throw new Error('[storybook-nuxt-components] Could not resolve the `#build` alias.')
59+
}
60+
const dtsPath = resolve(buildDir, 'components.d.ts')
61+
// Wire the generated declaration file into Vite's file-watch graph so
62+
// that the virtual module is invalidated when Nuxt regenerates it.
63+
this.addWatchFile(dtsPath)
64+
const dts = readFileSync(dtsPath, 'utf-8')
65+
const lines: string[] = []
66+
// Match only the direct `typeof import("…").default` form.
67+
// Lazy/island wrappers (LazyComponent<T>, IslandComponent<T>) are
68+
// excluded intentionally — Storybook only needs the concrete type.
69+
// The format has been stable across all Nuxt 3 releases.
70+
const re =
71+
/^export const (\w+): typeof import\("([^"]+)"\)(?:\.default|\[['"]default['"]\])\s*;?$/gm
72+
let match: RegExpExecArray | null
73+
while ((match = re.exec(dts)) !== null) {
74+
const [, name, rel] = match
75+
if (!name || !rel) continue
76+
const abs = resolve(buildDir, rel).replaceAll('\\', '/')
77+
lines.push(`export { default as ${name} } from ${JSON.stringify(abs)}`)
78+
}
79+
if (lines.length === 0) {
80+
throw new Error(
81+
`[storybook-nuxt-components] No component exports were found in ${dtsPath}.`,
82+
)
83+
}
84+
return lines.join('\n')
85+
},
86+
})
87+
2488
// Bridge compatibility between Storybook v10 core and v9 @storybook-vue/nuxt
2589
// v10 expects module federation globals that v9 doesn't provide
2690
newConfig.plugins.push({
2791
name: 'storybook-v10-compat',
2892
transformIndexHtml: {
2993
order: 'pre',
30-
handler(html) {
31-
const script = `
32-
<script>
33-
// Minimal shims for Storybook v10 module federation system
34-
// These will be replaced when Storybook runtime loads
35-
window.__STORYBOOK_MODULE_GLOBAL__ = { global: window };
36-
window.__STORYBOOK_MODULE_CLIENT_LOGGER__ = {
37-
deprecate: console.warn.bind(console, '[deprecated]'),
38-
once: console.log.bind(console),
39-
logger: console
40-
};
41-
window.__STORYBOOK_MODULE_CHANNELS__ = {
42-
Channel: class { on() {} off() {} emit() {} once() {} },
43-
createBrowserChannel: () => new window.__STORYBOOK_MODULE_CHANNELS__.Channel()
44-
};
45-
</script>`
46-
return html.replace(/<script>/, script + '<script>')
94+
handler() {
95+
return [
96+
{
97+
tag: 'script',
98+
injectTo: 'head-prepend' as const,
99+
children: [
100+
'// Minimal shims for Storybook v10 module federation system',
101+
'// These will be replaced when Storybook runtime loads',
102+
'window.__STORYBOOK_MODULE_GLOBAL__ = { global: window };',
103+
'window.__STORYBOOK_MODULE_CLIENT_LOGGER__ = {',
104+
" deprecate: console.warn.bind(console, '[deprecated]'),",
105+
' once: console.log.bind(console),',
106+
' logger: console',
107+
'};',
108+
'window.__STORYBOOK_MODULE_CHANNELS__ = {',
109+
' Channel: class { on() {} off() {} emit() {} once() {} },',
110+
' createBrowserChannel: () => new window.__STORYBOOK_MODULE_CHANNELS__.Channel()',
111+
'};',
112+
].join('\n'),
113+
},
114+
]
47115
},
48116
},
49117
})
@@ -73,7 +141,12 @@ const config = {
73141
const wrapped = async function (this: unknown, ...args: unknown[]) {
74142
try {
75143
return await originalFn.apply(this, args)
76-
} catch {
144+
} catch (err) {
145+
// oxlint-disable-next-line no-console -- Log and swallow errors to avoid breaking the Storybook build when vue-docgen-api encounters an unparseable component.
146+
console.warn(
147+
'[storybook:vue-docgen-plugin] Suppressed docgen error (component docs may be missing):',
148+
err,
149+
)
77150
return undefined
78151
}
79152
}

app/components/Code/FileTree.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const { toggleDir, isExpanded, autoExpandAncestors } = useFileTreeState(props.ba
3939
watch(
4040
() => props.currentPath,
4141
path => {
42-
if (path) {
42+
if (depth.value === 0 && path) {
4343
autoExpandAncestors(path)
4444
}
4545
},

app/components/CollapsibleSection.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
<script setup lang="ts">
22
import { shallowRef, computed } from 'vue'
33
import { LinkBase } from '#components'
4+
import type { IconClass } from '~/types/icon'
45
56
interface Props {
67
title: string
78
subtitle?: string
89
isLoading?: boolean
910
headingLevel?: `h${number}`
1011
id: string
11-
icon?: string
12+
icon?: IconClass
1213
}
1314
1415
const props = withDefaults(defineProps<Props>(), {

app/components/Diff/SidebarPanel.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script setup lang="ts">
22
import { packageRoute } from '~/utils/router'
3+
import type { IconClass } from '~/types/icon'
34
45
const props = defineProps<{
56
compare: CompareResponse
@@ -19,7 +20,7 @@ const fileFilter = defineModel<'all' | 'added' | 'removed' | 'modified'>('fileFi
1920
2021
const sectionOrder = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']
2122
const { t } = useI18n()
22-
const sectionMeta = computed<Record<string, { label: string; icon: string }>>(() => ({
23+
const sectionMeta = computed<Record<string, { label: string; icon: IconClass }>>(() => ({
2324
dependencies: { label: t('compare.dependencies'), icon: 'i-lucide:box' },
2425
devDependencies: { label: t('compare.dev_dependencies'), icon: 'i-lucide:wrench' },
2526
peerDependencies: { label: t('compare.peer_dependencies'), icon: 'i-lucide:users' },

app/components/Package/ExternalLinks.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ useCommandPaletteContextCommands(
117117
})
118118
}
119119
120+
commands.push({
121+
id: 'package-link-socket.dev',
122+
group: 'links',
123+
label: 'socket.dev',
124+
keywords: [...packageKeywords, 'socket.dev'],
125+
iconClass: 'i-simple-icons:socket',
126+
href: `https://socket.dev/npm/package/${props.pkg.name}`,
127+
})
128+
120129
commands.push({
121130
id: 'package-link-npm',
122131
group: 'links',

app/composables/usePlatformModifierKey.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ function detectApplePlatform() {
1616
}
1717

1818
export function usePlatformModifierKey() {
19+
const { t } = useI18n()
1920
const isApplePlatform = useState('platform:is-apple', detectApplePlatform)
21+
const ctrlKeyLabel = computed(() => t('shortcuts.ctrl_key'))
2022

2123
if (import.meta.client) {
2224
onMounted(() => {
@@ -26,7 +28,9 @@ export function usePlatformModifierKey() {
2628

2729
return {
2830
isApplePlatform: computed(() => isApplePlatform.value),
29-
primaryModifierKeyLabel: computed(() => (isApplePlatform.value ? '⌘' : 'Ctrl')),
30-
commandPaletteShortcutLabel: computed(() => (isApplePlatform.value ? '⌘ K' : 'Ctrl+K')),
31+
primaryModifierKeyLabel: computed(() => (isApplePlatform.value ? '⌘' : ctrlKeyLabel.value)),
32+
commandPaletteShortcutLabel: computed(() =>
33+
isApplePlatform.value ? '⌘ K' : `${ctrlKeyLabel.value}+K`,
34+
),
3135
}
3236
}

app/pages/brand.stories.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Brand from './brand.vue'
2+
import type { Meta, StoryObj } from '@storybook-vue/nuxt'
3+
import { pageDecorator } from '../../.storybook/decorators'
4+
5+
const meta = {
6+
component: Brand,
7+
parameters: {
8+
layout: 'fullscreen',
9+
},
10+
decorators: [pageDecorator],
11+
} satisfies Meta<typeof Brand>
12+
13+
export default meta
14+
type Story = StoryObj<typeof meta>
15+
16+
export const Default: Story = {}

0 commit comments

Comments
 (0)