@shohojdhara/atomix 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/atomix.config.ts +12 -0
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +230 -83
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +24 -23
  9. package/dist/charts.js +271 -369
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +624 -0
  12. package/dist/config.js +59 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +3 -2
  15. package/dist/core.js +342 -382
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +4 -6
  18. package/dist/forms.js +233 -334
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +11 -2
  21. package/dist/heavy.js +406 -445
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +109 -65
  24. package/dist/index.esm.js +654 -748
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +621 -717
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.js +4 -4
  33. package/dist/theme.js.map +1 -1
  34. package/package.json +24 -9
  35. package/scripts/atomix-cli.js +15 -1
  36. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  37. package/scripts/cli/__tests__/detector.test.js +50 -0
  38. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  39. package/scripts/cli/__tests__/test-setup.js +1 -133
  40. package/scripts/cli/commands/doctor.js +15 -3
  41. package/scripts/cli/commands/generate.js +113 -51
  42. package/scripts/cli/internal/ai-engine.js +30 -10
  43. package/scripts/cli/internal/complexity-utils.js +60 -0
  44. package/scripts/cli/internal/component-validator.js +49 -16
  45. package/scripts/cli/internal/generator.js +89 -36
  46. package/scripts/cli/internal/hook-generator.js +5 -2
  47. package/scripts/cli/internal/itcss-generator.js +16 -12
  48. package/scripts/cli/templates/next-templates.js +81 -30
  49. package/scripts/cli/templates/storybook-templates.js +12 -2
  50. package/scripts/cli/utils/detector.js +45 -7
  51. package/scripts/cli/utils/diagnostics.js +78 -0
  52. package/scripts/cli/utils/telemetry.js +13 -0
  53. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  54. package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
  55. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
  56. package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
  58. package/src/components/AtomixGlass/glass-utils.ts +51 -1
  59. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
  60. package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
  61. package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
  62. package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
  63. package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
  64. package/src/components/AtomixGlass/stories/types.ts +3 -3
  65. package/src/components/Button/Button.tsx +114 -57
  66. package/src/components/Callout/Callout.tsx +4 -4
  67. package/src/components/Chart/ChartRenderer.tsx +1 -1
  68. package/src/components/Chart/DonutChart.tsx +11 -8
  69. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  70. package/src/components/Form/Select.tsx +4 -4
  71. package/src/components/List/List.tsx +4 -4
  72. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  73. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  74. package/src/components/ProductReview/ProductReview.tsx +4 -2
  75. package/src/components/Rating/Rating.tsx +4 -2
  76. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  77. package/src/components/Steps/Steps.tsx +1 -1
  78. package/src/components/Tabs/Tabs.tsx +5 -5
  79. package/src/components/Testimonial/Testimonial.tsx +4 -2
  80. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  81. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  82. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  83. package/src/layouts/CssGrid/index.ts +8 -0
  84. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  85. package/src/layouts/CssGrid/scripts/index.js +43 -0
  86. package/src/layouts/Grid/scripts/Container.js +139 -0
  87. package/src/layouts/Grid/scripts/Grid.js +184 -0
  88. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  89. package/src/layouts/Grid/scripts/Row.js +154 -0
  90. package/src/layouts/Grid/scripts/index.js +48 -0
  91. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  92. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  93. package/src/lib/composables/useAccordion.ts +5 -5
  94. package/src/lib/composables/useAtomixGlass.ts +111 -74
  95. package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
  96. package/src/lib/composables/useBarChart.ts +2 -2
  97. package/src/lib/composables/useChart.ts +3 -2
  98. package/src/lib/composables/useChartToolbar.ts +48 -66
  99. package/src/lib/composables/useDataTable.ts +1 -1
  100. package/src/lib/composables/useDatePicker.ts +2 -2
  101. package/src/lib/composables/useEdgePanel.ts +45 -54
  102. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  103. package/src/lib/composables/usePhotoViewer.ts +2 -3
  104. package/src/lib/composables/usePieChart.ts +1 -1
  105. package/src/lib/composables/usePopover.ts +151 -139
  106. package/src/lib/composables/useSideMenu.ts +28 -41
  107. package/src/lib/composables/useSlider.ts +2 -6
  108. package/src/lib/composables/useTooltip.ts +2 -2
  109. package/src/lib/config/index.ts +39 -0
  110. package/src/lib/constants/components.ts +1 -0
  111. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  112. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  113. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  114. package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
  115. package/src/lib/types/components.ts +1 -0
  116. package/src/styles/01-settings/_index.scss +1 -0
  117. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  118. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  119. package/src/styles/02-tools/_tools.glass.scss +6 -0
  120. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  121. package/src/styles/06-components/_components.atomix-glass.scss +160 -99
  122. package/scripts/cli/__tests__/README.md +0 -81
  123. package/scripts/cli/__tests__/basic.test.js +0 -116
  124. package/scripts/cli/__tests__/clean.test.js +0 -278
  125. package/scripts/cli/__tests__/component-generator.test.js +0 -332
  126. package/scripts/cli/__tests__/component-validator.test.js +0 -433
  127. package/scripts/cli/__tests__/generator.test.js +0 -613
  128. package/scripts/cli/__tests__/glass-motion.test.js +0 -256
  129. package/scripts/cli/__tests__/integration.test.js +0 -938
  130. package/scripts/cli/__tests__/migrate.test.js +0 -74
  131. package/scripts/cli/__tests__/security.test.js +0 -206
  132. package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
  133. package/scripts/cli/__tests__/token-manager.test.js +0 -251
  134. package/scripts/cli/__tests__/token-provider.test.js +0 -361
  135. package/scripts/cli/__tests__/utils.test.js +0 -165
  136. package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
  137. package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
  138. package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
  139. package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
  140. package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
  141. package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
  142. package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
  143. package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
  144. package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
  145. package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
  146. package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
  147. package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
  148. package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
  149. package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
  150. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
  151. package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
  152. package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
  153. package/src/components/TypedButton/TypedButton.tsx +0 -39
  154. package/src/components/TypedButton/index.ts +0 -2
  155. package/src/lib/composables/useBreadcrumb.ts +0 -81
  156. package/src/lib/composables/useChartInteractions.ts +0 -123
  157. package/src/lib/composables/useChartPerformance.ts +0 -347
  158. package/src/lib/composables/useDropdown.ts +0 -338
  159. package/src/lib/composables/useModal.ts +0 -110
  160. package/src/lib/composables/useTypedButton.ts +0 -66
  161. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  162. package/src/lib/utils/displacement-generator.ts +0 -92
  163. package/src/lib/utils/memoryMonitor.ts +0 -191
  164. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  165. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  166. package/src/styles/06-components/_components.testbutton.scss +0 -212
  167. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  168. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -3,22 +3,40 @@
3
3
  * Optimized for Server Components and App Router
4
4
  */
5
5
 
6
+ /**
7
+ * Simple Server Component — valid displayName, Props, JSDoc, a11y; no forwardRef (server).
8
+ */
6
9
  export const nextTemplates = {
7
- /**
8
- * Simple Server Component
9
- */
10
- simple: (name) => `/**
11
- * ${name} Component (Server Component)
10
+ simple: (name) => `import React from 'react';
11
+
12
+ export interface ${name}Props {
13
+ /** Optional heading override */
14
+ title?: string;
15
+ children?: React.ReactNode;
16
+ }
17
+
18
+ /**
19
+ * ${name} — presentational server component (Atomix scaffold).
20
+ *
21
+ * @param props - ${name} properties
12
22
  */
13
- import React from 'react';
23
+ export default function ${name}(props: ${name}Props) {
24
+ const { title = '${name}', children } = props;
14
25
 
15
- export default function ${name}() {
16
26
  return (
17
- <div className="${name.toLowerCase()}">
18
- <h1>${name} Component</h1>
19
- </div>
27
+ <section
28
+ className={\`c-${name.toLowerCase()}\`}
29
+ role="region"
30
+ aria-label={title}
31
+ data-testid="${name.toLowerCase()}-root"
32
+ >
33
+ <h1 className={\`c-${name.toLowerCase()}__title\`}>{title}</h1>
34
+ {children}
35
+ </section>
20
36
  );
21
37
  }
38
+
39
+ ${name}.displayName = '${name}';
22
40
  `,
23
41
 
24
42
  /**
@@ -26,47 +44,80 @@ export default function ${name}() {
26
44
  */
27
45
  client: (name) => `'use client';
28
46
 
47
+ import React, { forwardRef, useState } from 'react';
48
+
49
+ export interface ${name}Props {
50
+ title?: string;
51
+ children?: React.ReactNode;
52
+ className?: string;
53
+ }
54
+
29
55
  /**
30
- * ${name} Component (Client Component)
56
+ * ${name} client component with local state (Atomix scaffold).
31
57
  */
32
- import React, { useState } from 'react';
33
-
34
- export default function ${name}() {
58
+ const ${name}Inner = forwardRef<HTMLDivElement, ${name}Props>(function ${name}Inner(
59
+ { title = '${name}', children, className = '' },
60
+ ref
61
+ ) {
35
62
  const [active, setActive] = useState(false);
36
63
 
37
64
  return (
38
- <div className="${name.toLowerCase()}">
39
- <h1>${name} Component</h1>
40
- <button onClick={() => setActive(!active)}>
65
+ <section
66
+ ref={ref}
67
+ className={[\`c-${name.toLowerCase()}\`, className].filter(Boolean).join(' ')}
68
+ role="region"
69
+ aria-label={title}
70
+ data-testid="${name.toLowerCase()}-root"
71
+ >
72
+ <h1 className={\`c-${name.toLowerCase()}__title\`}>{title}</h1>
73
+ {children}
74
+ <button type="button" onClick={() => setActive(!active)} aria-pressed={active}>
41
75
  Toggle: {active ? 'On' : 'Off'}
42
76
  </button>
43
- </div>
77
+ </section>
44
78
  );
45
- }
79
+ });
80
+
81
+ ${name}Inner.displayName = '${name}';
82
+
83
+ export default ${name}Inner;
46
84
  `,
47
85
 
48
86
  /**
49
87
  * Complex Server Component with async data
50
88
  */
51
- complex: (name) => `/**
52
- * ${name} Component (Server Component with Data)
53
- */
54
- import React from 'react';
89
+ complex: (name) => `import React from 'react';
55
90
 
56
- async function getData() {
57
- // Simulating data fetch
91
+ export interface ${name}Props {
92
+ title?: string;
93
+ }
94
+
95
+ async function getData(): Promise<{ title: string }> {
58
96
  return { title: '${name}' };
59
97
  }
60
98
 
61
- export default async function ${name}() {
99
+ /**
100
+ * ${name} — async server component (Atomix scaffold).
101
+ */
102
+ async function ${name}(props: ${name}Props) {
62
103
  const data = await getData();
104
+ const title = props.title ?? data.title;
63
105
 
64
106
  return (
65
- <div className="${name.toLowerCase()}">
66
- <h1>{data.title}</h1>
67
- <p>This is a complex Next.js server component.</p>
68
- </div>
107
+ <section
108
+ className={\`c-${name.toLowerCase()}\`}
109
+ role="region"
110
+ aria-label={title}
111
+ data-testid="${name.toLowerCase()}-root"
112
+ >
113
+ <h1>{title}</h1>
114
+ <p>This is a complex Next.js server component scaffold.</p>
115
+ </section>
69
116
  );
70
117
  }
118
+
119
+ ${name}.displayName = '${name}';
120
+
121
+ export default ${name};
71
122
  `
72
123
  };
@@ -5,8 +5,13 @@
5
5
 
6
6
  /**
7
7
  * Basic Storybook story template
8
+ * @param {string} name - Component name
9
+ * @param {{ storybookCssImport?: string }} [opts]
8
10
  */
9
- export const basicStoryTemplate = (name) => `import type { Meta, StoryObj } from '@storybook/react';
11
+ export const basicStoryTemplate = (name, opts = {}) => {
12
+ const cssImport = opts.storybookCssImport || '@shohojdhara/atomix/scss';
13
+ return `import '${cssImport}';
14
+ import type { Meta, StoryObj } from '@storybook/react';
10
15
  import { ${name} } from './${name}';
11
16
 
12
17
  const meta: Meta<typeof ${name}> = {
@@ -66,11 +71,15 @@ export const Glass: Story = {
66
71
  },
67
72
  };
68
73
  `;
74
+ };
69
75
 
70
76
  /**
71
77
  * Enhanced Storybook story template with detailed documentation
72
78
  */
73
- export const enhancedStoryTemplate = (name) => `import type { Meta, StoryObj } from '@storybook/react';
79
+ export const enhancedStoryTemplate = (name, opts = {}) => {
80
+ const cssImport = opts.storybookCssImport || '@shohojdhara/atomix/scss';
81
+ return `import '${cssImport}';
82
+ import type { Meta, StoryObj } from '@storybook/react';
74
83
  import { ${name} } from './${name}';
75
84
 
76
85
  const meta: Meta<typeof ${name}> = {
@@ -226,6 +235,7 @@ export const CustomContent: Story = {
226
235
  },
227
236
  };
228
237
  `;
238
+ };
229
239
 
230
240
  /**
231
241
  * Storybook templates object
@@ -7,15 +7,52 @@ import { readFile } from 'fs/promises';
7
7
  import { join } from 'path';
8
8
  import { existsSync } from 'fs';
9
9
 
10
+ const NEXT_CONFIG_FILES = [
11
+ 'next.config.js',
12
+ 'next.config.mjs',
13
+ 'next.config.ts',
14
+ 'next.config.cjs'
15
+ ];
16
+
17
+ const VITE_CONFIG_FILES = [
18
+ 'vite.config.js',
19
+ 'vite.config.mjs',
20
+ 'vite.config.ts',
21
+ 'vite.config.cjs'
22
+ ];
23
+
24
+ function hasFileInRoot(projectRoot, filenames) {
25
+ return filenames.some((name) => existsSync(join(projectRoot, name)));
26
+ }
27
+
28
+ function hasNextSignals(projectRoot, allDeps) {
29
+ if (allDeps.next) return true;
30
+ return hasFileInRoot(projectRoot, NEXT_CONFIG_FILES);
31
+ }
32
+
33
+ function hasViteSignals(projectRoot, allDeps) {
34
+ if (allDeps.vite) return true;
35
+ return hasFileInRoot(projectRoot, VITE_CONFIG_FILES);
36
+ }
37
+
10
38
  /**
11
39
  * Detect the framework from package.json and project structure
12
40
  * @param {string} projectRoot - The root directory of the project
41
+ * @param {{ framework?: string }} [options] - Optional override: 'react' | 'next' | 'vanilla'
13
42
  * @returns {Promise<'react' | 'next' | 'vanilla'>}
14
43
  */
15
- export async function detectFramework(projectRoot = process.cwd()) {
44
+ export async function detectFramework(projectRoot = process.cwd(), options = {}) {
45
+ const override = options.framework;
46
+ if (override) {
47
+ const o = String(override).toLowerCase();
48
+ if (o === 'react' || o === 'next' || o === 'vanilla') {
49
+ return o;
50
+ }
51
+ }
52
+
16
53
  try {
17
54
  const packageJsonPath = join(projectRoot, 'package.json');
18
-
55
+
19
56
  if (!existsSync(packageJsonPath)) {
20
57
  return 'vanilla';
21
58
  }
@@ -26,17 +63,18 @@ export async function detectFramework(projectRoot = process.cwd()) {
26
63
  ...(packageJson.devDependencies || {})
27
64
  };
28
65
 
29
- if (allDeps.next) {
66
+ if (hasNextSignals(projectRoot, allDeps)) {
30
67
  return 'next';
31
68
  }
32
69
 
33
- if (allDeps.react) {
70
+ const hasReact = Boolean(allDeps.react);
71
+
72
+ if (hasViteSignals(projectRoot, allDeps) && hasReact) {
34
73
  return 'react';
35
74
  }
36
75
 
37
- // Check for framework specific files
38
- if (existsSync(join(projectRoot, 'next.config.js')) || existsSync(join(projectRoot, 'next.config.mjs'))) {
39
- return 'next';
76
+ if (hasReact) {
77
+ return 'react';
40
78
  }
41
79
 
42
80
  return 'vanilla';
@@ -9,6 +9,7 @@ import { join } from 'path';
9
9
  import { readFile } from 'fs/promises';
10
10
  import { logger } from './logger.js';
11
11
  import { configLoader } from '../internal/config-loader.js';
12
+ import { templateEngine } from '../internal/template-engine.js';
12
13
 
13
14
  /**
14
15
  * Check Node.js and NPM versions
@@ -256,6 +257,83 @@ export async function checkTokens(projectRoot = process.cwd()) {
256
257
  return results;
257
258
  }
258
259
 
260
+ /**
261
+ * Generator: writable output path, optional Storybook, template registry
262
+ */
263
+ export async function checkGenerator(projectRoot = process.cwd()) {
264
+ const results = [];
265
+
266
+ const config = await configLoader.load(projectRoot);
267
+ const gen = config.generator || {};
268
+ const out = gen.outputPath || './src/components';
269
+ const outPath = join(projectRoot, out);
270
+
271
+ if (existsSync(outPath)) {
272
+ try {
273
+ accessSync(outPath, constants.W_OK);
274
+ results.push({
275
+ name: 'Generator: output path',
276
+ status: 'pass',
277
+ message: `Writable: ${out}`,
278
+ suggestion: null
279
+ });
280
+ } catch {
281
+ results.push({
282
+ name: 'Generator: output path',
283
+ status: 'fail',
284
+ message: `Not writable: ${out}`,
285
+ suggestion: 'Fix permissions or set generator.outputPath in atomix.config.'
286
+ });
287
+ }
288
+ } else {
289
+ results.push({
290
+ name: 'Generator: output path',
291
+ status: 'warn',
292
+ message: `Missing (will be created): ${out}`,
293
+ suggestion: 'Create the directory or run generate to scaffold components.'
294
+ });
295
+ }
296
+
297
+ const storybookHints = ['@storybook/react', '@storybook/react-vite', '@storybook/nextjs', 'storybook'];
298
+ let hasStorybook = false;
299
+ const pkgPath = join(projectRoot, 'package.json');
300
+ if (existsSync(pkgPath)) {
301
+ try {
302
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
303
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
304
+ hasStorybook = storybookHints.some((d) => Boolean(deps[d]));
305
+ } catch {
306
+ /* ignore */
307
+ }
308
+ }
309
+
310
+ results.push({
311
+ name: 'Storybook (optional)',
312
+ status: hasStorybook ? 'pass' : 'warn',
313
+ message: hasStorybook ? 'Storybook-related dependency found' : 'No Storybook in package.json',
314
+ suggestion: hasStorybook ? null : 'Add Storybook if you rely on generated .stories.tsx files.'
315
+ });
316
+
317
+ try {
318
+ const frameworks = templateEngine.getSupportedFrameworks();
319
+ results.push({
320
+ name: 'Generator: template registry',
321
+ status: frameworks.length ? 'pass' : 'fail',
322
+ message: frameworks.length ? `Frameworks: ${frameworks.join(', ')}` : 'Empty registry',
323
+ suggestion: frameworks.length ? null : 'CLI template registry failed to load.'
324
+ });
325
+ } catch (error) {
326
+ results.push({
327
+ name: 'Generator: template registry',
328
+ status: 'fail',
329
+ message: error.message,
330
+ suggestion: 'Reinstall the Atomix package or check scripts/cli installation.'
331
+ });
332
+ }
333
+
334
+ return results;
335
+ }
336
+
259
337
  /**
260
338
  * Check directory permissions
261
339
  */
@@ -14,6 +14,16 @@ class Telemetry {
14
14
  this.logs = [];
15
15
  this.startTime = null;
16
16
  this.currentCommand = null;
17
+ /** @type {Record<string, unknown>} */
18
+ this.pendingExtra = {};
19
+ }
20
+
21
+ /**
22
+ * Merge fields into the next stop() log entry (e.g. generate framework).
23
+ * @param {Record<string, unknown>} extra
24
+ */
25
+ recordExtra(extra) {
26
+ this.pendingExtra = { ...this.pendingExtra, ...extra };
17
27
  }
18
28
 
19
29
  /**
@@ -43,9 +53,12 @@ class Telemetry {
43
53
  duration: parseFloat(duration.toFixed(2)),
44
54
  timestamp: new Date().toISOString(),
45
55
  success,
56
+ ...this.pendingExtra,
46
57
  ...extra
47
58
  };
48
59
 
60
+ this.pendingExtra = {};
61
+
49
62
  // Anonymize if needed
50
63
  if (config.anonymize !== false) {
51
64
  delete logEntry.path;
@@ -263,6 +263,10 @@ export const WithAllProps: Story = {
263
263
  };
264
264
 
265
265
  export const CompoundUsage: Story = {
266
+ args: {
267
+ title: 'Compound Accordion Example',
268
+ children: <p>Compound pattern content</p>
269
+ },
266
270
  render: args => (
267
271
  <Accordion {...args}>
268
272
  <Accordion.Header>