Skip to content

Commit cbc45f9

Browse files
authored
Merge branch 'main' into feat/og-image-v7
2 parents 224e32f + a1f6487 commit cbc45f9

38 files changed

+2362
-1809
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/Chart/SplitSparkline.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ const configs = computed(() => {
244244

245245
<template #skeleton>
246246
<!-- This empty div overrides the default built-in scanning animation on load -->
247-
<div />
247+
<div></div>
248248
</template>
249249
</VueUiSparkline>
250250
</div>

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/Compare/FacetBarChart.vue

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
170170
style: {
171171
chart: {
172172
backgroundColor: colors.value.bg,
173+
legend: {
174+
show: false,
175+
},
173176
},
174177
},
175178
},
@@ -278,7 +281,7 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
278281

279282
<template>
280283
<div class="font-mono facet-bar">
281-
<ClientOnly v-if="dataset.length">
284+
<ClientOnly v-if="packages.length">
282285
<VueUiHorizontalBar :key="chartKey" :dataset :config class="[direction:ltr]">
283286
<template #hint="{ isVisible }">
284287
<p v-if="isVisible" class="text-accent text-xs pt-2" aria-hidden="true">
@@ -339,40 +342,25 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
339342
aria-hidden="true"
340343
/>
341344
</template>
342-
</VueUiHorizontalBar>
343345

344-
<template #fallback>
345-
<div class="flex flex-col gap-2 justify-center items-center mb-2">
346-
<SkeletonInline class="h-4 w-16" />
347-
<SkeletonInline class="h-4 w-28" />
348-
</div>
349-
<div class="flex flex-col gap-1">
350-
<SkeletonInline class="h-7 w-full" v-for="pkg in packages" :key="pkg" />
351-
</div>
352-
</template>
346+
<template #skeleton>
347+
<!-- This empty div overrides the default built-in scanning animation on load -->
348+
<div></div>
349+
</template>
350+
</VueUiHorizontalBar>
353351
</ClientOnly>
354-
355-
<template v-else>
356-
<div class="flex flex-col gap-2 justify-center items-center mb-2">
357-
<SkeletonInline class="h-4 w-16" />
358-
<SkeletonInline class="h-4 w-28" />
359-
</div>
360-
<div class="flex flex-col gap-1">
361-
<SkeletonInline class="h-7 w-full" v-for="pkg in packages" :key="pkg" />
362-
</div>
363-
</template>
364352
</div>
365353
</template>
366354

367355
<style scoped>
368356
:deep(.vue-data-ui-component svg:focus-visible) {
369-
outline: 1px solid var(--accent-color) !important;
357+
outline: 1px solid var(--accent) !important;
370358
border-radius: 0.1rem;
371359
outline-offset: 3px !important;
372360
}
373361
:deep(.vue-ui-user-options-button:focus-visible),
374362
:deep(.vue-ui-user-options :first-child:focus-visible) {
375-
outline: 0.1rem solid var(--accent-color) !important;
363+
outline: 0.1rem solid var(--accent) !important;
376364
border-radius: 0.25rem;
377365
}
378366
</style>

0 commit comments

Comments
 (0)