@fragments-sdk/cli 0.10.1 → 0.12.1

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 (223) hide show
  1. package/dist/ai-client-I6MDWNYA.js +21 -0
  2. package/dist/bin.js +292 -367
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-PW7QTQA6.js → chunk-4OC7FTJB.js} +2 -2
  5. package/dist/{chunk-HRFUSSZI.js → chunk-AM4MRTMN.js} +2 -2
  6. package/dist/{chunk-5G3VZH43.js → chunk-GVDSFQ4E.js} +281 -351
  7. package/dist/chunk-GVDSFQ4E.js.map +1 -0
  8. package/dist/chunk-JJ2VRTBU.js +626 -0
  9. package/dist/chunk-JJ2VRTBU.js.map +1 -0
  10. package/dist/{chunk-D5PYOXEI.js → chunk-LVWFOLUZ.js} +148 -13
  11. package/dist/{chunk-D5PYOXEI.js.map → chunk-LVWFOLUZ.js.map} +1 -1
  12. package/dist/{chunk-WXSR2II7.js → chunk-OQKMEFOS.js} +58 -6
  13. package/dist/chunk-OQKMEFOS.js.map +1 -0
  14. package/dist/chunk-SXTKFDCR.js +104 -0
  15. package/dist/chunk-SXTKFDCR.js.map +1 -0
  16. package/dist/chunk-T5OMVL7E.js +443 -0
  17. package/dist/chunk-T5OMVL7E.js.map +1 -0
  18. package/dist/{chunk-ZM4ZQZWZ.js → chunk-TPWGL2XS.js} +39 -37
  19. package/dist/chunk-TPWGL2XS.js.map +1 -0
  20. package/dist/{chunk-OQO55NKV.js → chunk-WFS63PCW.js} +85 -11
  21. package/dist/chunk-WFS63PCW.js.map +1 -0
  22. package/dist/core/index.js +9 -1
  23. package/dist/{discovery-NEOY4MPN.js → discovery-ZJQSXF56.js} +3 -3
  24. package/dist/{generate-FBHSXR3D.js → generate-RJFS2JWA.js} +4 -4
  25. package/dist/index.js +7 -6
  26. package/dist/index.js.map +1 -1
  27. package/dist/init-ZSX3NRCZ.js +636 -0
  28. package/dist/init-ZSX3NRCZ.js.map +1 -0
  29. package/dist/mcp-bin.js +2 -2
  30. package/dist/{scan-CJF2DOQW.js → scan-3PMCJ4RB.js} +6 -6
  31. package/dist/scan-generate-SYU4PYZD.js +1115 -0
  32. package/dist/scan-generate-SYU4PYZD.js.map +1 -0
  33. package/dist/{service-TQYWY65E.js → service-VMGNJZ42.js} +3 -3
  34. package/dist/snapshot-XOISO2IS.js +139 -0
  35. package/dist/snapshot-XOISO2IS.js.map +1 -0
  36. package/dist/{static-viewer-NUBFPKWH.js → static-viewer-5GXH2MGE.js} +3 -3
  37. package/dist/static-viewer-5GXH2MGE.js.map +1 -0
  38. package/dist/{test-Z5LVO724.js → test-SI4NSHQX.js} +4 -4
  39. package/dist/{tokens-CE46OTMD.js → tokens-T6SIVUT5.js} +5 -5
  40. package/dist/{viewer-DNMNC5VS.js → viewer-7ZEAFBVN.js} +80 -58
  41. package/dist/viewer-7ZEAFBVN.js.map +1 -0
  42. package/package.json +6 -14
  43. package/src/ai-client.ts +156 -0
  44. package/src/bin.ts +74 -2
  45. package/src/build.ts +95 -33
  46. package/src/commands/__tests__/drift-sync.test.ts +252 -0
  47. package/src/commands/__tests__/scan-generate.test.ts +497 -45
  48. package/src/commands/enhance.ts +11 -35
  49. package/src/commands/init.ts +296 -193
  50. package/src/commands/scan-generate.ts +740 -139
  51. package/src/commands/scan.ts +37 -32
  52. package/src/commands/setup.ts +143 -52
  53. package/src/commands/snapshot.ts +197 -0
  54. package/src/commands/sync.ts +357 -0
  55. package/src/commands/validate.ts +43 -1
  56. package/src/core/component-extractor.test.ts +282 -0
  57. package/src/core/component-extractor.ts +1030 -0
  58. package/src/core/discovery.ts +93 -7
  59. package/src/service/enhance/props-extractor.ts +235 -13
  60. package/src/validators.ts +236 -0
  61. package/src/viewer/__tests__/viewer-integration.test.ts +85 -74
  62. package/src/viewer/server.ts +37 -22
  63. package/src/viewer/vite-plugin.ts +25 -9
  64. package/dist/chunk-5G3VZH43.js.map +0 -1
  65. package/dist/chunk-OQO55NKV.js.map +0 -1
  66. package/dist/chunk-WXSR2II7.js.map +0 -1
  67. package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
  68. package/dist/init-NDQXUWDU.js +0 -796
  69. package/dist/init-NDQXUWDU.js.map +0 -1
  70. package/dist/scan-generate-SJAN5MVI.js +0 -691
  71. package/dist/scan-generate-SJAN5MVI.js.map +0 -1
  72. package/dist/viewer-DNMNC5VS.js.map +0 -1
  73. package/src/ai.ts +0 -266
  74. package/src/commands/init-framework.ts +0 -414
  75. package/src/mcp/bin.ts +0 -36
  76. package/src/migrate/bin.ts +0 -114
  77. package/src/theme/index.ts +0 -77
  78. package/src/viewer/__tests__/a11y-fixes.test.ts +0 -358
  79. package/src/viewer/__tests__/jsx-parser.test.ts +0 -502
  80. package/src/viewer/__tests__/render-utils.test.ts +0 -232
  81. package/src/viewer/__tests__/style-utils.test.ts +0 -404
  82. package/src/viewer/assets/fragments-logo.ts +0 -4
  83. package/src/viewer/assets/fragments_logo.png +0 -0
  84. package/src/viewer/bin.ts +0 -86
  85. package/src/viewer/cli/health.ts +0 -256
  86. package/src/viewer/cli/index.ts +0 -33
  87. package/src/viewer/cli/scan.ts +0 -124
  88. package/src/viewer/cli/utils.ts +0 -174
  89. package/src/viewer/components/AccessibilityPanel.tsx +0 -1457
  90. package/src/viewer/components/ActionCapture.tsx +0 -172
  91. package/src/viewer/components/ActionsPanel.tsx +0 -332
  92. package/src/viewer/components/AllVariantsPreview.tsx +0 -78
  93. package/src/viewer/components/App.tsx +0 -582
  94. package/src/viewer/components/BottomPanel.tsx +0 -288
  95. package/src/viewer/components/CodePanel.naming.test.tsx +0 -59
  96. package/src/viewer/components/CodePanel.tsx +0 -118
  97. package/src/viewer/components/CommandPalette.tsx +0 -392
  98. package/src/viewer/components/ComponentDocView.tsx +0 -164
  99. package/src/viewer/components/ComponentGraph.tsx +0 -380
  100. package/src/viewer/components/ComponentHeader.tsx +0 -88
  101. package/src/viewer/components/ContractPanel.tsx +0 -241
  102. package/src/viewer/components/EmptyVariantMessage.tsx +0 -54
  103. package/src/viewer/components/ErrorBoundary.tsx +0 -97
  104. package/src/viewer/components/FigmaEmbed.tsx +0 -238
  105. package/src/viewer/components/FragmentEditor.tsx +0 -525
  106. package/src/viewer/components/FragmentRenderer.tsx +0 -61
  107. package/src/viewer/components/HeaderSearch.tsx +0 -24
  108. package/src/viewer/components/HealthDashboard.tsx +0 -441
  109. package/src/viewer/components/HmrStatusIndicator.tsx +0 -61
  110. package/src/viewer/components/Icons.tsx +0 -479
  111. package/src/viewer/components/InteractionsPanel.tsx +0 -757
  112. package/src/viewer/components/IsolatedPreviewFrame.tsx +0 -346
  113. package/src/viewer/components/IsolatedRender.tsx +0 -113
  114. package/src/viewer/components/KeyboardShortcutsHelp.tsx +0 -53
  115. package/src/viewer/components/LandingPage.tsx +0 -421
  116. package/src/viewer/components/Layout.tsx +0 -27
  117. package/src/viewer/components/LeftSidebar.tsx +0 -472
  118. package/src/viewer/components/LoadErrorMessage.tsx +0 -102
  119. package/src/viewer/components/MultiViewportPreview.tsx +0 -522
  120. package/src/viewer/components/NoVariantsMessage.tsx +0 -59
  121. package/src/viewer/components/PanelShell.tsx +0 -161
  122. package/src/viewer/components/PerformancePanel.tsx +0 -304
  123. package/src/viewer/components/PreviewArea.tsx +0 -472
  124. package/src/viewer/components/PreviewAside.tsx +0 -168
  125. package/src/viewer/components/PreviewFrameHost.tsx +0 -303
  126. package/src/viewer/components/PreviewPane.tsx +0 -149
  127. package/src/viewer/components/PreviewToolbar.tsx +0 -80
  128. package/src/viewer/components/PropsEditor.tsx +0 -506
  129. package/src/viewer/components/PropsTable.tsx +0 -111
  130. package/src/viewer/components/RelationsSection.tsx +0 -88
  131. package/src/viewer/components/ResizablePanel.tsx +0 -271
  132. package/src/viewer/components/RightSidebar.tsx +0 -102
  133. package/src/viewer/components/RuntimeToolsRegistrar.tsx +0 -17
  134. package/src/viewer/components/ScreenshotButton.tsx +0 -90
  135. package/src/viewer/components/Sidebar.tsx +0 -169
  136. package/src/viewer/components/SkeletonLoader.tsx +0 -161
  137. package/src/viewer/components/ThemeProvider.tsx +0 -42
  138. package/src/viewer/components/Toast.tsx +0 -3
  139. package/src/viewer/components/TokenStylePanel.tsx +0 -699
  140. package/src/viewer/components/TopToolbar.tsx +0 -159
  141. package/src/viewer/components/UsageSection.tsx +0 -95
  142. package/src/viewer/components/VariantMatrix.tsx +0 -388
  143. package/src/viewer/components/VariantRenderer.tsx +0 -131
  144. package/src/viewer/components/VariantTabs.tsx +0 -40
  145. package/src/viewer/components/ViewerHeader.tsx +0 -69
  146. package/src/viewer/components/ViewerStateSync.tsx +0 -52
  147. package/src/viewer/components/ViewportSelector.tsx +0 -172
  148. package/src/viewer/components/WebMCPDevTools.tsx +0 -503
  149. package/src/viewer/components/WebMCPIntegration.tsx +0 -47
  150. package/src/viewer/components/WebMCPStatusIndicator.tsx +0 -60
  151. package/src/viewer/components/_future/CreatePage.tsx +0 -836
  152. package/src/viewer/components/viewer-utils.ts +0 -16
  153. package/src/viewer/composition-renderer.ts +0 -381
  154. package/src/viewer/constants/index.ts +0 -1
  155. package/src/viewer/constants/ui.ts +0 -166
  156. package/src/viewer/entry.tsx +0 -335
  157. package/src/viewer/hooks/index.ts +0 -2
  158. package/src/viewer/hooks/useA11yCache.ts +0 -383
  159. package/src/viewer/hooks/useA11yService.ts +0 -364
  160. package/src/viewer/hooks/useActions.ts +0 -138
  161. package/src/viewer/hooks/useAppState.ts +0 -147
  162. package/src/viewer/hooks/useCompiledFragments.ts +0 -42
  163. package/src/viewer/hooks/useFigmaIntegration.ts +0 -132
  164. package/src/viewer/hooks/useHmrStatus.ts +0 -109
  165. package/src/viewer/hooks/useKeyboardShortcuts.ts +0 -270
  166. package/src/viewer/hooks/usePreviewBridge.ts +0 -347
  167. package/src/viewer/hooks/useScrollSpy.ts +0 -78
  168. package/src/viewer/hooks/useUrlState.ts +0 -318
  169. package/src/viewer/hooks/useViewSettings.ts +0 -111
  170. package/src/viewer/index.html +0 -28
  171. package/src/viewer/intelligence/healthReport.ts +0 -505
  172. package/src/viewer/intelligence/styleDrift.ts +0 -340
  173. package/src/viewer/intelligence/usageScanner.ts +0 -309
  174. package/src/viewer/jsx-parser.ts +0 -486
  175. package/src/viewer/preview-frame-entry.tsx +0 -25
  176. package/src/viewer/preview-frame.html +0 -125
  177. package/src/viewer/public/favicon.ico +0 -0
  178. package/src/viewer/render-template.html +0 -68
  179. package/src/viewer/styles/globals.css +0 -278
  180. package/src/viewer/types/a11y.ts +0 -197
  181. package/src/viewer/utils/a11y-fixes.ts +0 -509
  182. package/src/viewer/utils/actionExport.ts +0 -372
  183. package/src/viewer/utils/colorSchemes.ts +0 -201
  184. package/src/viewer/utils/detectRelationships.ts +0 -256
  185. package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +0 -10
  186. package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +0 -2
  187. package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +0 -274
  188. package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +0 -129
  189. package/src/viewer/vendor/shared/src/DocsPageAsideHost.tsx +0 -89
  190. package/src/viewer/vendor/shared/src/DocsPageShell.tsx +0 -124
  191. package/src/viewer/vendor/shared/src/DocsSearchCommand.tsx +0 -99
  192. package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +0 -66
  193. package/src/viewer/vendor/shared/src/PropsTable.module.scss +0 -68
  194. package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +0 -2
  195. package/src/viewer/vendor/shared/src/PropsTable.tsx +0 -76
  196. package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +0 -114
  197. package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +0 -2
  198. package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +0 -137
  199. package/src/viewer/vendor/shared/src/docs-data/index.ts +0 -32
  200. package/src/viewer/vendor/shared/src/docs-data/mcp-configs.ts +0 -72
  201. package/src/viewer/vendor/shared/src/docs-data/palettes.ts +0 -75
  202. package/src/viewer/vendor/shared/src/docs-data/setup-examples.ts +0 -55
  203. package/src/viewer/vendor/shared/src/docs-layout.scss +0 -28
  204. package/src/viewer/vendor/shared/src/docs-layout.scss.d.ts +0 -2
  205. package/src/viewer/vendor/shared/src/index.ts +0 -34
  206. package/src/viewer/vendor/shared/src/types.ts +0 -53
  207. package/src/viewer/webmcp/__tests__/analytics.test.ts +0 -108
  208. package/src/viewer/webmcp/analytics.ts +0 -165
  209. package/src/viewer/webmcp/index.ts +0 -3
  210. package/src/viewer/webmcp/posthog-bridge.ts +0 -39
  211. package/src/viewer/webmcp/runtime-tools.ts +0 -152
  212. package/src/viewer/webmcp/scan-utils.ts +0 -135
  213. package/src/viewer/webmcp/use-tool-analytics.ts +0 -69
  214. package/src/viewer/webmcp/viewer-state.ts +0 -45
  215. /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
  216. /package/dist/{chunk-PW7QTQA6.js.map → chunk-4OC7FTJB.js.map} +0 -0
  217. /package/dist/{chunk-HRFUSSZI.js.map → chunk-AM4MRTMN.js.map} +0 -0
  218. /package/dist/{scan-CJF2DOQW.js.map → discovery-ZJQSXF56.js.map} +0 -0
  219. /package/dist/{generate-FBHSXR3D.js.map → generate-RJFS2JWA.js.map} +0 -0
  220. /package/dist/{service-TQYWY65E.js.map → scan-3PMCJ4RB.js.map} +0 -0
  221. /package/dist/{static-viewer-NUBFPKWH.js.map → service-VMGNJZ42.js.map} +0 -0
  222. /package/dist/{test-Z5LVO724.js.map → test-SI4NSHQX.js.map} +0 -0
  223. /package/dist/{tokens-CE46OTMD.js.map → tokens-T6SIVUT5.js.map} +0 -0
@@ -1,256 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Auto-detect component relationships from rendered elements
4
- *
5
- * Analyzes React elements to discover:
6
- * - Composition relationships (components used within other components)
7
- * - Sibling relationships (components in the same category)
8
- * - Common usage patterns
9
- */
10
-
11
- import { isValidElement, type ReactNode, type ReactElement, Children } from "react";
12
- import type { FragmentDefinition, ComponentRelation, RelationshipType } from "../../core/index.js";
13
-
14
- interface DetectedRelationship {
15
- component: string;
16
- relationship: RelationshipType;
17
- note: string;
18
- confidence: number; // 0-1, how confident we are in this relationship
19
- }
20
-
21
- // Cache for relationship detection to avoid repeated expensive render() calls
22
- const relationshipCache = new Map<string, { timestamp: number; relationships: DetectedRelationship[] }>();
23
- const CACHE_TTL = 60000; // 1 minute cache
24
-
25
- // Cache for known component names (expensive to recompute for 98+ components)
26
- let knownComponentsCache: { count: number; set: Set<string> } | null = null;
27
-
28
- /**
29
- * Extract component name from a React element
30
- */
31
- function getComponentName(element: ReactElement): string | null {
32
- const type = element.type;
33
-
34
- if (typeof type === "string") {
35
- // HTML element, not a component
36
- return null;
37
- }
38
-
39
- if (typeof type === "function") {
40
- // Function component or class component
41
- return type.displayName || type.name || null;
42
- }
43
-
44
- if (typeof type === "object" && type !== null) {
45
- // Could be forwardRef, memo, etc.
46
- const innerType = (type as { $$typeof?: symbol; type?: unknown; render?: unknown })?.type ||
47
- (type as { render?: unknown })?.render;
48
- if (typeof innerType === "function") {
49
- return (innerType as { displayName?: string; name?: string }).displayName ||
50
- (innerType as { name?: string }).name || null;
51
- }
52
- }
53
-
54
- return null;
55
- }
56
-
57
- /**
58
- * Recursively collect all component names from an element tree
59
- */
60
- function collectComponentNames(
61
- element: ReactNode,
62
- collected: Set<string> = new Set()
63
- ): Set<string> {
64
- if (!isValidElement(element)) {
65
- return collected;
66
- }
67
-
68
- const name = getComponentName(element as ReactElement);
69
- if (name) {
70
- collected.add(name);
71
- }
72
-
73
- // Traverse children
74
- const children = (element.props as { children?: ReactNode })?.children;
75
- if (children) {
76
- Children.forEach(children, (child) => {
77
- collectComponentNames(child, collected);
78
- });
79
- }
80
-
81
- return collected;
82
- }
83
-
84
- /**
85
- * Get cached set of known component names
86
- */
87
- function getKnownComponents(
88
- allFragments: Array<{ path: string; fragment: FragmentDefinition }>
89
- ): Set<string> {
90
- // Invalidate cache if fragment count changed
91
- if (!knownComponentsCache || knownComponentsCache.count !== allFragments.length) {
92
- knownComponentsCache = {
93
- count: allFragments.length,
94
- set: new Set(allFragments.map(s => s.fragment.meta.name)),
95
- };
96
- }
97
- return knownComponentsCache.set;
98
- }
99
-
100
- /**
101
- * Detect composition relationships from a fragment's variants
102
- */
103
- export function detectCompositionRelationships(
104
- fragment: FragmentDefinition,
105
- allFragments: Array<{ path: string; fragment: FragmentDefinition }>
106
- ): DetectedRelationship[] {
107
- const relationships: DetectedRelationship[] = [];
108
- const componentName = fragment.meta.name;
109
- const knownComponents = getKnownComponents(allFragments);
110
- const usedComponents = new Set<string>();
111
-
112
- // Analyze each variant's render output
113
- for (const variant of fragment.variants || []) {
114
- try {
115
- const rendered = variant.render();
116
- const componentNames = collectComponentNames(rendered);
117
-
118
- for (const name of componentNames) {
119
- if (name !== componentName && knownComponents.has(name)) {
120
- usedComponents.add(name);
121
- }
122
- }
123
- } catch {
124
- // Render might fail, skip this variant
125
- }
126
- }
127
-
128
- // Create composition relationships for used components
129
- for (const usedComponent of usedComponents) {
130
- relationships.push({
131
- component: usedComponent,
132
- relationship: "composition",
133
- note: `Used within ${componentName} variants`,
134
- confidence: 0.8,
135
- });
136
- }
137
-
138
- return relationships;
139
- }
140
-
141
- /**
142
- * Detect sibling relationships based on semantic connections.
143
- *
144
- * NOTE: Category-based sibling detection was removed because it creates
145
- * meaningless relationships (e.g., Card → Separator just because both are "layout").
146
- *
147
- * True sibling relationships should be defined manually in the fragment definition
148
- * based on actual use cases (e.g., Table uses Badge for status columns).
149
- *
150
- * This function now only detects siblings when:
151
- * - There's a bidirectional manual relationship (A references B AND B references A)
152
- * - Components share overlapping variant patterns (future enhancement)
153
- */
154
- export function detectSiblingRelationships(
155
- _fragment: FragmentDefinition,
156
- _allFragments: Array<{ path: string; fragment: FragmentDefinition }>
157
- ): DetectedRelationship[] {
158
- // Disabled: Category-based sibling detection creates noise
159
- // Meaningful sibling relationships should be defined manually
160
- return [];
161
- }
162
-
163
- /**
164
- * Detect alternative relationships.
165
- *
166
- * NOTE: Pattern-based alternative detection was removed because it creates
167
- * weak relationships based on naming conventions rather than actual use cases.
168
- *
169
- * True alternative relationships should be defined manually in the fragment
170
- * definition based on actual decision criteria (e.g., "Use IconButton instead
171
- * of Button when only an icon is needed").
172
- *
173
- * A better approach would analyze the `whenNot` guidelines for semantic matches,
174
- * but that requires NLP/semantic analysis which is out of scope.
175
- */
176
- export function detectAlternativeRelationships(
177
- _fragment: FragmentDefinition,
178
- _allFragments: Array<{ path: string; fragment: FragmentDefinition }>
179
- ): DetectedRelationship[] {
180
- // Disabled: Pattern-based alternative detection is too weak
181
- // Meaningful alternatives should be defined manually with clear rationale
182
- return [];
183
- }
184
-
185
- /**
186
- * Combine all detected relationships and deduplicate
187
- * Uses caching to avoid expensive render() calls on subsequent views
188
- */
189
- export function detectAllRelationships(
190
- fragment: FragmentDefinition,
191
- allFragments: Array<{ path: string; fragment: FragmentDefinition }>
192
- ): DetectedRelationship[] {
193
- const cacheKey = fragment.meta.name;
194
- const now = Date.now();
195
-
196
- // Check cache first
197
- const cached = relationshipCache.get(cacheKey);
198
- if (cached && (now - cached.timestamp) < CACHE_TTL) {
199
- return cached.relationships;
200
- }
201
-
202
- const all = [
203
- ...detectCompositionRelationships(fragment, allFragments),
204
- ...detectSiblingRelationships(fragment, allFragments),
205
- ...detectAlternativeRelationships(fragment, allFragments),
206
- ];
207
-
208
- // Deduplicate by component name, keeping highest confidence
209
- const byComponent = new Map<string, DetectedRelationship>();
210
-
211
- for (const rel of all) {
212
- const existing = byComponent.get(rel.component);
213
- if (!existing || rel.confidence > existing.confidence) {
214
- byComponent.set(rel.component, rel);
215
- }
216
- }
217
-
218
- // Sort by confidence descending
219
- const result = Array.from(byComponent.values()).sort((a, b) => b.confidence - a.confidence);
220
-
221
- // Cache the result
222
- relationshipCache.set(cacheKey, { timestamp: now, relationships: result });
223
-
224
- return result;
225
- }
226
-
227
- /**
228
- * Merge manual and auto-detected relationships
229
- */
230
- export function mergeRelationships(
231
- manual: ComponentRelation[] | undefined,
232
- detected: DetectedRelationship[]
233
- ): Array<ComponentRelation & { isDetected?: boolean; confidence?: number }> {
234
- const result: Array<ComponentRelation & { isDetected?: boolean; confidence?: number }> = [];
235
- const manualComponents = new Set((manual || []).map(r => r.component));
236
-
237
- // Add manual relationships first
238
- for (const rel of manual || []) {
239
- result.push({ ...rel, isDetected: false });
240
- }
241
-
242
- // Add detected relationships that aren't already defined manually
243
- for (const rel of detected) {
244
- if (!manualComponents.has(rel.component)) {
245
- result.push({
246
- component: rel.component,
247
- relationship: rel.relationship,
248
- note: rel.note,
249
- isDetected: true,
250
- confidence: rel.confidence,
251
- });
252
- }
253
- }
254
-
255
- return result;
256
- }
@@ -1,10 +0,0 @@
1
- .accessibilityList {
2
- margin: 0.5rem 0 0;
3
- padding-left: 1.125rem;
4
- font-size: 0.8125rem;
5
- line-height: 1.6;
6
-
7
- li {
8
- margin-bottom: 0.25rem;
9
- }
10
- }
@@ -1,2 +0,0 @@
1
- declare const styles: Record<string, string>;
2
- export default styles;
@@ -1,274 +0,0 @@
1
- 'use client';
2
-
3
- import type { ReactNode } from 'react';
4
- import {
5
- Badge,
6
- Box,
7
- Card,
8
- CardBody,
9
- Grid,
10
- ListRoot,
11
- ListItem,
12
- Stack,
13
- Text,
14
- Alert,
15
- AlertIcon,
16
- AlertBody,
17
- AlertTitle,
18
- AlertContent,
19
- CodeBlock,
20
- Link,
21
- } from '@fragments-sdk/ui';
22
- import type { DocProp } from './types';
23
- import { PropsTable } from './PropsTable';
24
- import styles from './ComponentDocContent.module.scss';
25
-
26
- /** Normalize PascalCase to lowercase with spaces: "AppSwitcher" → "app switcher" */
27
- function pascalToLowerSpaced(name: string): string {
28
- return name.replace(/([A-Z])/g, ' $1').trim().toLowerCase();
29
- }
30
-
31
- /** Detect auto-generated placeholder descriptions like "ActionMenu component" */
32
- function isGenericDescription(name: string, description: string): boolean {
33
- const lower = description.toLowerCase().trim();
34
- const nameLower = name.toLowerCase();
35
- const nameSpaced = pascalToLowerSpaced(name);
36
- return (
37
- lower === `${nameLower} component` ||
38
- lower === `${nameSpaced} component` ||
39
- lower === `${nameLower} component.` ||
40
- lower === `${nameSpaced} component.` ||
41
- (lower.startsWith('interactive ') && lower.endsWith(' element for triggering actions')) ||
42
- (lower.startsWith('form ') && lower.endsWith(' for user input')) ||
43
- (lower.startsWith('container ') && lower.endsWith(' for grouping content'))
44
- );
45
- }
46
-
47
- /** Detect placeholder usage items like "Use X for its intended purpose" */
48
- function isGenericUsageItem(item: string): boolean {
49
- const lower = item.toLowerCase().trim();
50
- return (
51
- (lower.startsWith('use ') && lower.endsWith(' for its intended purpose')) ||
52
- lower === 'when a more specific component is available' ||
53
- lower.startsWith('todo:')
54
- );
55
- }
56
-
57
- /** Check if any real (non-placeholder) usage content exists */
58
- function hasRealUsageContent(usage: { when: string[]; whenNot: string[]; guidelines: string[]; accessibility: string[] }): boolean {
59
- const allItems = [...usage.when, ...usage.whenNot, ...usage.guidelines, ...usage.accessibility];
60
- return allItems.length > 0 && allItems.some(item => !isGenericUsageItem(item));
61
- }
62
-
63
- /** Filter out generic placeholder items from usage lists */
64
- function filterRealItems(items: string[]): string[] {
65
- return items.filter(item => !isGenericUsageItem(item));
66
- }
67
-
68
- interface StandardReference {
69
- id: string;
70
- title: string;
71
- url: string;
72
- }
73
-
74
- export interface ComponentDocContentProps {
75
- name: string;
76
- description: string;
77
- componentId: string;
78
- props: Record<string, DocProp>;
79
- variants: Array<{ name: string; description?: string; code?: string }>;
80
- usage: {
81
- when: string[];
82
- whenNot: string[];
83
- guidelines: string[];
84
- accessibility: string[];
85
- };
86
- relations: Array<{ component: string; relationship: string; note?: string }>;
87
- dependencies?: Array<{ name: string }>;
88
- standards?: StandardReference[];
89
-
90
- /** Custom package name for the import path (e.g. '@payroc/react'). Defaults to '@fragments-sdk/ui'. */
91
- packageName?: string;
92
-
93
- /** Render a variant example (framework-specific). */
94
- renderVariant: (variant: { name: string; description?: string; code?: string }, index: number) => ReactNode;
95
- /** Render a related-component link (framework-specific). If omitted, renders plain text. */
96
- renderRelatedLink?: (component: string, relationship: string, note: string | undefined, key: string) => ReactNode;
97
- }
98
-
99
- export function ComponentDocContent({
100
- name,
101
- description,
102
- componentId,
103
- props,
104
- variants,
105
- usage,
106
- relations,
107
- packageName,
108
- dependencies,
109
- standards,
110
- renderVariant,
111
- renderRelatedLink,
112
- }: ComponentDocContentProps) {
113
- return (
114
- <Stack gap="xl">
115
- <Box as="header">
116
- <Stack gap="sm">
117
- <Text as="h1" size="2xl" weight="semibold">{name}</Text>
118
- {!isGenericDescription(name, description) && (
119
- <Text as="p" color="secondary">{description}</Text>
120
- )}
121
- </Stack>
122
- </Box>
123
-
124
- <Box as="section">
125
- <Stack gap="md">
126
- <Text as="h2" id="setup" size="xl" weight="semibold">Setup</Text>
127
- <CodeBlock
128
- code={`import { ${componentId} } from '${packageName || '@fragments-sdk/ui'}';`}
129
- language="tsx"
130
- />
131
- {dependencies && dependencies.length > 0 && (
132
- <Stack gap="sm">
133
- <Text as="h3" size="base" weight="semibold">Dependencies</Text>
134
- <Text size="sm" color="secondary">
135
- This component requires additional packages:
136
- </Text>
137
- <CodeBlock
138
- code={`npm install ${dependencies.map((d) => d.name).join(' ')}`}
139
- language="bash"
140
- />
141
- </Stack>
142
- )}
143
- </Stack>
144
- </Box>
145
-
146
- {variants.length > 0 && (
147
- <Box as="section">
148
- <Stack gap="md">
149
- <Text as="h2" id="examples" size="xl" weight="semibold">Examples</Text>
150
- {variants.map((variant, index) => renderVariant(variant, index))}
151
- </Stack>
152
- </Box>
153
- )}
154
-
155
- {Object.keys(props).length > 0 && (
156
- <Box as="section">
157
- <Stack gap="md">
158
- <Text as="h2" id="props" size="xl" weight="semibold">Props</Text>
159
- <PropsTable props={props} />
160
- </Stack>
161
- </Box>
162
- )}
163
-
164
- {hasRealUsageContent(usage) && (
165
- <Box as="section">
166
- <Stack gap="md">
167
- <Text as="h2" id="usage-guidelines" size="xl" weight="semibold">Usage Guidelines</Text>
168
-
169
- <Grid columns={{ base: 1, md: 2 }} gap="md">
170
- {filterRealItems(usage.when).length > 0 && (
171
- <Box background="secondary" rounded="md" padding="md">
172
- <Stack gap="sm">
173
- <Text as="h3" id="when-to-use" size="base" weight="semibold">When to use</Text>
174
- <ListRoot variant="disc" gap="xs">
175
- {filterRealItems(usage.when).map((item, i) => (
176
- <ListItem key={i}>{item}</ListItem>
177
- ))}
178
- </ListRoot>
179
- </Stack>
180
- </Box>
181
- )}
182
-
183
- {filterRealItems(usage.whenNot).length > 0 && (
184
- <Box background="secondary" rounded="md" padding="md">
185
- <Stack gap="sm">
186
- <Text as="h3" id="when-not-to-use" size="base" weight="semibold">When not to use</Text>
187
- <ListRoot variant="disc" gap="xs">
188
- {filterRealItems(usage.whenNot).map((item, i) => (
189
- <ListItem key={i}>{item}</ListItem>
190
- ))}
191
- </ListRoot>
192
- </Stack>
193
- </Box>
194
- )}
195
- </Grid>
196
-
197
- {filterRealItems(usage.guidelines).length > 0 && (
198
- <Stack gap="sm">
199
- <Text as="h3" id="best-practices" size="base" weight="semibold">Best practices</Text>
200
- <ListRoot variant="disc" gap="xs">
201
- {filterRealItems(usage.guidelines).map((item, i) => (
202
- <ListItem key={i}>{item}</ListItem>
203
- ))}
204
- </ListRoot>
205
- </Stack>
206
- )}
207
-
208
- {usage.accessibility.length > 0 && (
209
- <Alert severity="info">
210
- <AlertIcon />
211
- <AlertBody>
212
- <AlertTitle>Accessibility</AlertTitle>
213
- <AlertContent>
214
- <ul className={styles.accessibilityList}>
215
- {usage.accessibility.map((item, i) => (
216
- <li key={i}>{item}</li>
217
- ))}
218
- </ul>
219
- </AlertContent>
220
- </AlertBody>
221
- </Alert>
222
- )}
223
- </Stack>
224
- </Box>
225
- )}
226
-
227
- {standards && standards.length > 0 && (
228
- <Box as="section">
229
- <Stack gap="sm">
230
- <Text as="h2" id="standards" size="xl" weight="semibold">Standards References</Text>
231
- <ListRoot variant="disc" gap="xs">
232
- {standards.map((standard) => (
233
- <ListItem key={standard.id}>
234
- <Link href={standard.url} target="_blank" rel="noreferrer">
235
- {standard.title}
236
- </Link>
237
- </ListItem>
238
- ))}
239
- </ListRoot>
240
- </Stack>
241
- </Box>
242
- )}
243
-
244
- {relations.length > 0 && (
245
- <Box as="section">
246
- <Stack gap="md">
247
- <Text as="h2" id="related-components" size="xl" weight="semibold">Related Components</Text>
248
- <Grid columns="auto" minChildWidth="220px" gap="sm">
249
- {relations.map((relation) => {
250
- const key = `${relation.component}-${relation.relationship}`;
251
- if (renderRelatedLink) {
252
- return renderRelatedLink(relation.component, relation.relationship, relation.note, key);
253
- }
254
- return (
255
- <Card key={key} variant="outlined">
256
- <CardBody>
257
- <Stack gap="xs">
258
- <Stack direction="row" align="center" justify="between">
259
- <Text weight="semibold">{relation.component}</Text>
260
- <Badge size="sm">{relation.relationship}</Badge>
261
- </Stack>
262
- {relation.note && <Text size="sm" color="secondary">{relation.note}</Text>}
263
- </Stack>
264
- </CardBody>
265
- </Card>
266
- );
267
- })}
268
- </Grid>
269
- </Stack>
270
- </Box>
271
- )}
272
- </Stack>
273
- );
274
- }
@@ -1,129 +0,0 @@
1
- 'use client';
2
-
3
- import { Header, NavigationMenu } from '@fragments-sdk/ui';
4
- import type { ReactNode } from 'react';
5
- import { DocsSearchCommand } from './DocsSearchCommand';
6
- import type { DocsNavLinkRenderer, HeaderNavEntry, NavSection, SearchItem } from './types';
7
- import { isDropdown } from './types';
8
-
9
- interface DocsHeaderBarProps {
10
- brand: ReactNode;
11
- headerNav: HeaderNavEntry[];
12
- mobileSections?: NavSection[];
13
- currentPath: string;
14
- searchItems: SearchItem[];
15
- onSearchSelect: (item: SearchItem) => void;
16
- renderLink?: DocsNavLinkRenderer;
17
- isActive?: (href: string, currentPath: string) => boolean;
18
- actions?: ReactNode;
19
- showSkipLink?: boolean;
20
- navAriaLabel?: string;
21
- }
22
-
23
- const defaultLinkRenderer: DocsNavLinkRenderer = ({ href, label, onClick }) => (
24
- <a href={href} onClick={onClick}>
25
- {label}
26
- </a>
27
- );
28
-
29
- function defaultIsActive(href: string, currentPath: string): boolean {
30
- return currentPath === href || currentPath.startsWith(`${href}/`);
31
- }
32
-
33
- export function DocsHeaderBar({
34
- brand,
35
- headerNav,
36
- mobileSections = [],
37
- currentPath,
38
- searchItems,
39
- onSearchSelect,
40
- renderLink = defaultLinkRenderer,
41
- isActive = defaultIsActive,
42
- actions,
43
- showSkipLink = true,
44
- navAriaLabel = 'Primary navigation',
45
- }: DocsHeaderBarProps) {
46
- return (
47
- <Header aria-label="Documentation header">
48
- <Header.Brand>{brand}</Header.Brand>
49
- {showSkipLink ? <Header.SkipLink /> : null}
50
-
51
- <NavigationMenu aria-label={navAriaLabel}>
52
- <NavigationMenu.List>
53
- {headerNav.map((entry) =>
54
- isDropdown(entry) ? (
55
- <NavigationMenu.Item key={entry.label} value={entry.label}>
56
- <NavigationMenu.Trigger>{entry.label}</NavigationMenu.Trigger>
57
- <NavigationMenu.Content>
58
- <div style={{ display: 'flex', flexDirection: 'column', padding: '4px', minWidth: '180px' }}>
59
- {entry.items.map((child) => (
60
- <NavigationMenu.Link
61
- key={child.href}
62
- href={child.href}
63
- active={isActive(child.href, currentPath)}
64
- asChild
65
- >
66
- {renderLink({ href: child.href, label: child.label })}
67
- </NavigationMenu.Link>
68
- ))}
69
- </div>
70
- </NavigationMenu.Content>
71
- </NavigationMenu.Item>
72
- ) : (
73
- <NavigationMenu.Item key={entry.href}>
74
- <NavigationMenu.Link
75
- href={entry.href}
76
- active={isActive(entry.href, currentPath)}
77
- asChild
78
- >
79
- {renderLink({ href: entry.href, label: entry.label })}
80
- </NavigationMenu.Link>
81
- </NavigationMenu.Item>
82
- )
83
- )}
84
- </NavigationMenu.List>
85
-
86
- <NavigationMenu.Viewport />
87
-
88
- <NavigationMenu.MobileBrand>{brand}</NavigationMenu.MobileBrand>
89
-
90
- <NavigationMenu.MobileContent>
91
- {/* Render all headerNav items in the mobile drawer */}
92
- <NavigationMenu.MobileSection>
93
- {headerNav.map((entry) =>
94
- isDropdown(entry) ? (
95
- entry.items.map((child) => (
96
- <NavigationMenu.Link key={child.href} href={child.href} asChild>
97
- {renderLink({ href: child.href, label: child.label })}
98
- </NavigationMenu.Link>
99
- ))
100
- ) : (
101
- <NavigationMenu.Link key={entry.href} href={entry.href} asChild>
102
- {renderLink({ href: entry.href, label: entry.label })}
103
- </NavigationMenu.Link>
104
- )
105
- )}
106
- </NavigationMenu.MobileSection>
107
-
108
- {mobileSections.map((section) => (
109
- <NavigationMenu.MobileSection key={section.title} label={section.title}>
110
- {section.items.map((item) => (
111
- <NavigationMenu.Link key={item.href} href={item.href} asChild>
112
- {renderLink({ href: item.href, label: item.label })}
113
- </NavigationMenu.Link>
114
- ))}
115
- </NavigationMenu.MobileSection>
116
- ))}
117
- </NavigationMenu.MobileContent>
118
- </NavigationMenu>
119
-
120
- <Header.Spacer />
121
-
122
- <Header.Search>
123
- <DocsSearchCommand searchItems={searchItems} onSelect={onSearchSelect} />
124
- </Header.Search>
125
-
126
- <Header.Actions>{actions}</Header.Actions>
127
- </Header>
128
- );
129
- }