@fragments-sdk/cli 0.7.0 → 0.7.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 (173) hide show
  1. package/LICENSE +77 -14
  2. package/dist/bin.js +247 -247
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-CVXKXVOY.js → chunk-3T6QL7IY.js} +47 -29
  5. package/dist/chunk-3T6QL7IY.js.map +1 -0
  6. package/dist/{chunk-7OPWMLOE.js → chunk-7KUSBMI4.js} +114 -112
  7. package/dist/chunk-7KUSBMI4.js.map +1 -0
  8. package/dist/{chunk-XHUDJNN3.js → chunk-DH4ETVSM.js} +18 -18
  9. package/dist/chunk-DH4ETVSM.js.map +1 -0
  10. package/dist/{chunk-RVRTRESS.js → chunk-DQHWLAUV.js} +29 -29
  11. package/dist/chunk-DQHWLAUV.js.map +1 -0
  12. package/dist/{chunk-6JBGU74P.js → chunk-GHYYFAQN.js} +23 -23
  13. package/dist/chunk-GHYYFAQN.js.map +1 -0
  14. package/dist/{chunk-NWQ4CJOQ.js → chunk-GKX2HPZ6.js} +40 -40
  15. package/dist/chunk-GKX2HPZ6.js.map +1 -0
  16. package/dist/{chunk-TJ34N7C7.js → chunk-OOGTG5FM.js} +34 -33
  17. package/dist/chunk-OOGTG5FM.js.map +1 -0
  18. package/dist/{core-W2HYIQW6.js → core-UQXZTBFZ.js} +24 -26
  19. package/dist/{generate-LMTISDIJ.js → generate-GP6ZLAQB.js} +5 -5
  20. package/dist/generate-GP6ZLAQB.js.map +1 -0
  21. package/dist/index.d.ts +23 -27
  22. package/dist/index.js +10 -10
  23. package/dist/{init-7CHRKQ7P.js → init-W72WBSU2.js} +5 -5
  24. package/dist/{init-7CHRKQ7P.js.map → init-W72WBSU2.js.map} +1 -1
  25. package/dist/mcp-bin.js +73 -73
  26. package/dist/mcp-bin.js.map +1 -1
  27. package/dist/scan-V54HWRDY.js +12 -0
  28. package/dist/{service-T2L7VLTE.js → service-PVGTYUKX.js} +6 -6
  29. package/dist/{static-viewer-GBR7YNF3.js → static-viewer-KILKIVN7.js} +4 -4
  30. package/dist/{test-OJRXNDO2.js → test-3YRYQRGV.js} +19 -19
  31. package/dist/test-3YRYQRGV.js.map +1 -0
  32. package/dist/{tokens-3BWDESVM.js → tokens-IXSQHPQK.js} +5 -5
  33. package/dist/{viewer-SUFOISZM.js → viewer-K42REJU2.js} +199 -199
  34. package/dist/viewer-K42REJU2.js.map +1 -0
  35. package/package.json +13 -2
  36. package/src/ai.ts +5 -5
  37. package/src/analyze.ts +11 -11
  38. package/src/bin.ts +1 -1
  39. package/src/build.ts +37 -35
  40. package/src/commands/a11y.ts +6 -6
  41. package/src/commands/add.ts +11 -11
  42. package/src/commands/audit.ts +4 -4
  43. package/src/commands/baseline.ts +3 -3
  44. package/src/commands/build.ts +8 -8
  45. package/src/commands/compare.ts +20 -20
  46. package/src/commands/context.ts +16 -16
  47. package/src/commands/enhance.ts +36 -36
  48. package/src/commands/generate.ts +1 -1
  49. package/src/commands/graph.ts +5 -5
  50. package/src/commands/init.ts +1 -1
  51. package/src/commands/link/figma.ts +82 -82
  52. package/src/commands/link/index.ts +3 -3
  53. package/src/commands/link/storybook.ts +9 -9
  54. package/src/commands/list.ts +2 -2
  55. package/src/commands/reset.ts +15 -15
  56. package/src/commands/scan.ts +27 -27
  57. package/src/commands/storygen.ts +24 -24
  58. package/src/commands/validate.ts +2 -2
  59. package/src/commands/verify.ts +8 -8
  60. package/src/core/auto-props.ts +4 -4
  61. package/src/core/composition.test.ts +36 -36
  62. package/src/core/composition.ts +19 -19
  63. package/src/core/config.ts +6 -6
  64. package/src/core/{defineSegment.ts → defineFragment.ts} +16 -22
  65. package/src/core/discovery.ts +6 -6
  66. package/src/core/figma.ts +2 -2
  67. package/src/core/graph-extractor.test.ts +77 -77
  68. package/src/core/graph-extractor.ts +32 -32
  69. package/src/core/importAnalyzer.ts +1 -1
  70. package/src/core/index.ts +22 -23
  71. package/src/core/loader.ts +21 -24
  72. package/src/core/node.ts +5 -5
  73. package/src/core/parser.ts +71 -31
  74. package/src/core/previewLoader.ts +1 -1
  75. package/src/core/schema.ts +16 -16
  76. package/src/core/storyAdapter.test.ts +87 -87
  77. package/src/core/storyAdapter.ts +16 -16
  78. package/src/core/token-parser.ts +9 -1
  79. package/src/core/types.ts +21 -26
  80. package/src/diff.ts +22 -22
  81. package/src/index.ts +2 -2
  82. package/src/mcp/server.ts +80 -80
  83. package/src/migrate/__tests__/utils/utils.test.ts +3 -3
  84. package/src/migrate/bin.ts +4 -4
  85. package/src/migrate/converter.ts +16 -16
  86. package/src/migrate/index.ts +3 -3
  87. package/src/migrate/migrate.ts +3 -3
  88. package/src/migrate/parser.ts +8 -8
  89. package/src/migrate/report.ts +2 -2
  90. package/src/migrate/types.ts +4 -4
  91. package/src/screenshot.ts +22 -22
  92. package/src/service/__tests__/props-extractor.test.ts +15 -15
  93. package/src/service/analytics.ts +39 -39
  94. package/src/service/enhance/codebase-scanner.ts +1 -1
  95. package/src/service/enhance/index.ts +1 -1
  96. package/src/service/enhance/props-extractor.ts +2 -2
  97. package/src/service/enhance/types.ts +2 -2
  98. package/src/service/index.ts +2 -2
  99. package/src/service/metrics-store.ts +1 -1
  100. package/src/service/patch-generator.ts +1 -1
  101. package/src/setup.ts +52 -52
  102. package/src/shared/dev-server-client.ts +7 -7
  103. package/src/shared/fragment-loader.ts +59 -0
  104. package/src/shared/index.ts +1 -1
  105. package/src/shared/types.ts +4 -4
  106. package/src/static-viewer.ts +35 -35
  107. package/src/test/discovery.ts +6 -6
  108. package/src/test/index.ts +5 -5
  109. package/src/test/reporters/console.ts +1 -1
  110. package/src/test/reporters/junit.ts +1 -1
  111. package/src/test/runner.ts +7 -7
  112. package/src/test/types.ts +3 -3
  113. package/src/test/watch.ts +9 -9
  114. package/src/validators.ts +26 -26
  115. package/src/viewer/__tests__/render-utils.test.ts +28 -28
  116. package/src/viewer/__tests__/viewer-integration.test.ts +4 -4
  117. package/src/viewer/cli/health.ts +26 -26
  118. package/src/viewer/components/App.tsx +79 -79
  119. package/src/viewer/components/BottomPanel.tsx +17 -17
  120. package/src/viewer/components/CodePanel.tsx +3 -3
  121. package/src/viewer/components/CommandPalette.tsx +11 -11
  122. package/src/viewer/components/ComponentGraph.tsx +28 -28
  123. package/src/viewer/components/ComponentHeader.tsx +2 -2
  124. package/src/viewer/components/ContractPanel.tsx +6 -6
  125. package/src/viewer/components/FigmaEmbed.tsx +9 -9
  126. package/src/viewer/components/HealthDashboard.tsx +17 -17
  127. package/src/viewer/components/InteractionsPanel.tsx +2 -2
  128. package/src/viewer/components/IsolatedPreviewFrame.tsx +6 -6
  129. package/src/viewer/components/IsolatedRender.tsx +10 -10
  130. package/src/viewer/components/LeftSidebar.tsx +28 -28
  131. package/src/viewer/components/MultiViewportPreview.tsx +14 -14
  132. package/src/viewer/components/PreviewArea.tsx +11 -11
  133. package/src/viewer/components/PreviewFrameHost.tsx +51 -51
  134. package/src/viewer/components/RightSidebar.tsx +9 -9
  135. package/src/viewer/components/Sidebar.tsx +17 -17
  136. package/src/viewer/components/StoryRenderer.tsx +2 -2
  137. package/src/viewer/components/TokenStylePanel.tsx +1 -1
  138. package/src/viewer/components/UsageSection.tsx +2 -2
  139. package/src/viewer/components/VariantMatrix.tsx +11 -11
  140. package/src/viewer/components/VariantRenderer.tsx +3 -3
  141. package/src/viewer/components/VariantTabs.tsx +2 -2
  142. package/src/viewer/components/_future/CreatePage.tsx +6 -6
  143. package/src/viewer/composition-renderer.ts +11 -11
  144. package/src/viewer/entry.tsx +40 -40
  145. package/src/viewer/hooks/useFigmaIntegration.ts +1 -1
  146. package/src/viewer/hooks/usePreviewBridge.ts +5 -5
  147. package/src/viewer/hooks/useUrlState.ts +6 -6
  148. package/src/viewer/index.ts +2 -2
  149. package/src/viewer/intelligence/healthReport.ts +17 -17
  150. package/src/viewer/intelligence/styleDrift.ts +1 -1
  151. package/src/viewer/intelligence/usageScanner.ts +1 -1
  152. package/src/viewer/render-template.html +1 -1
  153. package/src/viewer/render-utils.ts +21 -21
  154. package/src/viewer/server.ts +18 -18
  155. package/src/viewer/utils/detectRelationships.ts +22 -22
  156. package/src/viewer/vite-plugin.ts +213 -213
  157. package/dist/chunk-6JBGU74P.js.map +0 -1
  158. package/dist/chunk-7OPWMLOE.js.map +0 -1
  159. package/dist/chunk-CVXKXVOY.js.map +0 -1
  160. package/dist/chunk-NWQ4CJOQ.js.map +0 -1
  161. package/dist/chunk-RVRTRESS.js.map +0 -1
  162. package/dist/chunk-TJ34N7C7.js.map +0 -1
  163. package/dist/chunk-XHUDJNN3.js.map +0 -1
  164. package/dist/generate-LMTISDIJ.js.map +0 -1
  165. package/dist/scan-WY23TJCP.js +0 -12
  166. package/dist/test-OJRXNDO2.js.map +0 -1
  167. package/dist/viewer-SUFOISZM.js.map +0 -1
  168. package/src/shared/segment-loader.ts +0 -59
  169. /package/dist/{core-W2HYIQW6.js.map → core-UQXZTBFZ.js.map} +0 -0
  170. /package/dist/{scan-WY23TJCP.js.map → scan-V54HWRDY.js.map} +0 -0
  171. /package/dist/{service-T2L7VLTE.js.map → service-PVGTYUKX.js.map} +0 -0
  172. /package/dist/{static-viewer-GBR7YNF3.js.map → static-viewer-KILKIVN7.js.map} +0 -0
  173. /package/dist/{tokens-3BWDESVM.js.map → tokens-IXSQHPQK.js.map} +0 -0
@@ -15,7 +15,7 @@ export interface RenderRequest {
15
15
  };
16
16
  }
17
17
 
18
- export interface SegmentInfo {
18
+ export interface FragmentInfo {
19
19
  name: string;
20
20
  path: string;
21
21
  }
@@ -60,32 +60,32 @@ export function serializePropsToJsx(props: Record<string, unknown>): string {
60
60
  }
61
61
 
62
62
  /**
63
- * Find a segment by component name.
64
- * Returns the segment info if found, null otherwise.
63
+ * Find a fragment by component name.
64
+ * Returns the fragment info if found, null otherwise.
65
65
  */
66
- export function findSegmentByName(
66
+ export function findFragmentByName(
67
67
  componentName: string,
68
- segments: Array<{ path: string; segment: { meta: { name: string } } }>
69
- ): SegmentInfo | null {
70
- const match = segments.find(
71
- (s) => s.segment.meta.name.toLowerCase() === componentName.toLowerCase()
68
+ fragments: Array<{ path: string; fragment: { meta: { name: string } } }>
69
+ ): FragmentInfo | null {
70
+ const match = fragments.find(
71
+ (s) => s.fragment.meta.name.toLowerCase() === componentName.toLowerCase()
72
72
  );
73
73
 
74
74
  if (!match) return null;
75
75
 
76
76
  return {
77
- name: match.segment.meta.name,
77
+ name: match.fragment.meta.name,
78
78
  path: match.path,
79
79
  };
80
80
  }
81
81
 
82
82
  /**
83
- * Get list of available component names from loaded segments.
83
+ * Get list of available component names from loaded fragments.
84
84
  */
85
85
  export function getAvailableComponents(
86
- segments: Array<{ segment: { meta: { name: string } } }>
86
+ fragments: Array<{ fragment: { meta: { name: string } } }>
87
87
  ): string[] {
88
- return segments.map((s) => s.segment.meta.name).sort();
88
+ return fragments.map((s) => s.fragment.meta.name).sort();
89
89
  }
90
90
 
91
91
  /**
@@ -93,7 +93,7 @@ export function getAvailableComponents(
93
93
  * This script imports the component and renders it with the given props.
94
94
  */
95
95
  export function generateRenderScript(
96
- segmentPath: string,
96
+ fragmentPath: string,
97
97
  componentName: string,
98
98
  props: Record<string, unknown> = {}
99
99
  ): string {
@@ -112,20 +112,20 @@ export function generateRenderScript(
112
112
  import React from "react";
113
113
  import { createRoot } from "react-dom/client";
114
114
 
115
- // Import the segment to get the component
115
+ // Import the fragment to get the component
116
116
  async function render() {
117
117
  const root = document.getElementById("render-root");
118
118
 
119
119
  try {
120
- // Dynamic import of the segment file
121
- const segmentModule = await import("${segmentPath}");
122
- const segment = segmentModule.default;
120
+ // Dynamic import of the fragment file
121
+ const fragmentModule = await import("${fragmentPath}");
122
+ const fragment = fragmentModule.default;
123
123
 
124
- if (!segment || !segment.component) {
125
- throw new Error("Segment does not export a component");
124
+ if (!fragment || !fragment.component) {
125
+ throw new Error("Fragment does not export a component");
126
126
  }
127
127
 
128
- const Component = segment.component;
128
+ const Component = fragment.component;
129
129
 
130
130
  // Create React root and render
131
131
  const reactRoot = createRoot(root);
@@ -166,5 +166,5 @@ render();
166
166
  * This creates a unique ID that Vite can resolve.
167
167
  */
168
168
  export function generateRenderModuleId(componentName: string, requestId: string): string {
169
- return `virtual:segments-render-${componentName}-${requestId}`;
169
+ return `virtual:fragments-render-${componentName}-${requestId}`;
170
170
  }
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Segments Dev Server
2
+ * Fragments Dev Server
3
3
  *
4
4
  * Runs a Vite dev server that:
5
5
  * 1. Uses the project's existing Vite/build configuration
6
6
  * 2. Has access to all project dependencies
7
7
  * 3. Can render actual components with proper styling
8
- * 4. Supports HMR for segment file changes
8
+ * 4. Supports HMR for fragment file changes
9
9
  *
10
- * This is the "Storybook-like" experience for Segments.
10
+ * This is the "Storybook-like" experience for Fragments.
11
11
  */
12
12
 
13
13
  import {
@@ -21,8 +21,8 @@ import react from "@vitejs/plugin-react";
21
21
  import { resolve, dirname, join } from "node:path";
22
22
  import { existsSync, realpathSync } from "node:fs";
23
23
  import { fileURLToPath } from "node:url";
24
- import { loadConfig, discoverSegmentFiles, discoverInstalledFragments } from "../core/node.js";
25
- import { segmentsPlugin } from "./vite-plugin.js";
24
+ import { loadConfig, discoverFragmentFiles, discoverInstalledFragments } from "../core/node.js";
25
+ import { fragmentsPlugin } from "./vite-plugin.js";
26
26
 
27
27
  const __dirname = dirname(fileURLToPath(import.meta.url));
28
28
  // At runtime, __dirname is dist/. Viewer assets live in src/viewer/.
@@ -34,7 +34,7 @@ const uiLibRoot = resolve(packagesRoot, "../libs/ui/src");
34
34
  export interface DevServerOptions {
35
35
  /** Port to run the server on */
36
36
  port?: number;
37
- /** Path to segments config file */
37
+ /** Path to fragments config file */
38
38
  configPath?: string;
39
39
  /** Open browser on start */
40
40
  open?: boolean;
@@ -43,7 +43,7 @@ export interface DevServerOptions {
43
43
  }
44
44
 
45
45
  /**
46
- * Create and start the Segments dev server.
46
+ * Create and start the Fragments dev server.
47
47
  *
48
48
  * The server runs WITHIN the project's context, meaning:
49
49
  * - All project dependencies are available
@@ -64,14 +64,14 @@ export async function createDevServer(
64
64
 
65
65
  console.log("\n🔧 Loading configuration...");
66
66
 
67
- // Load segments config
67
+ // Load fragments config
68
68
  const { config, configDir } = await loadConfig(configPath);
69
69
 
70
- // Discover segment files (local + installed packages)
71
- const segmentFiles = await discoverSegmentFiles(config, configDir);
70
+ // Discover fragment files (local + installed packages)
71
+ const fragmentFiles = await discoverFragmentFiles(config, configDir);
72
72
  const installedFiles = await discoverInstalledFragments(projectRoot);
73
- const allSegmentFiles = [...segmentFiles, ...installedFiles];
74
- console.log(`📦 Found ${segmentFiles.length} local + ${installedFiles.length} installed fragment file(s)`);
73
+ const allFragmentFiles = [...fragmentFiles, ...installedFiles];
74
+ console.log(`📦 Found ${fragmentFiles.length} local + ${installedFiles.length} installed fragment file(s)`);
75
75
 
76
76
  // Try to load project's Vite config
77
77
  let projectViteConfig: InlineConfig = {};
@@ -112,8 +112,8 @@ export async function createDevServer(
112
112
  })
113
113
  )];
114
114
 
115
- // Our Segments-specific configuration
116
- const segmentsConfig: InlineConfig = {
115
+ // Our Fragments-specific configuration
116
+ const fragmentsConfig: InlineConfig = {
117
117
  configFile: false, // Don't load config again
118
118
  root: projectRoot, // Run from PROJECT root
119
119
  base: "/",
@@ -131,9 +131,9 @@ export async function createDevServer(
131
131
  // React support (if not already in project config)
132
132
  ...(hasReactPlugin(projectViteConfig) ? [] : [react()]),
133
133
 
134
- // Segments plugins (array including SVGR)
135
- ...segmentsPlugin({
136
- segmentFiles: allSegmentFiles,
134
+ // Fragments plugins (array including SVGR)
135
+ ...fragmentsPlugin({
136
+ fragmentFiles: allFragmentFiles,
137
137
  config,
138
138
  projectRoot,
139
139
  }),
@@ -170,7 +170,7 @@ export async function createDevServer(
170
170
 
171
171
  // Merge project config with our config
172
172
  // Project config takes precedence for most things
173
- const mergedConfig = mergeConfig(projectViteConfig, segmentsConfig);
173
+ const mergedConfig = mergeConfig(projectViteConfig, fragmentsConfig);
174
174
 
175
175
  console.log("🚀 Starting dev server...\n");
176
176
 
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { isValidElement, type ReactNode, type ReactElement, Children } from "react";
12
- import type { SegmentDefinition, ComponentRelation, RelationshipType } from "../../core/index.js";
12
+ import type { FragmentDefinition, ComponentRelation, RelationshipType } from "../../core/index.js";
13
13
 
14
14
  interface DetectedRelationship {
15
15
  component: string;
@@ -85,32 +85,32 @@ function collectComponentNames(
85
85
  * Get cached set of known component names
86
86
  */
87
87
  function getKnownComponents(
88
- allSegments: Array<{ path: string; segment: SegmentDefinition }>
88
+ allFragments: Array<{ path: string; fragment: FragmentDefinition }>
89
89
  ): Set<string> {
90
- // Invalidate cache if segment count changed
91
- if (!knownComponentsCache || knownComponentsCache.count !== allSegments.length) {
90
+ // Invalidate cache if fragment count changed
91
+ if (!knownComponentsCache || knownComponentsCache.count !== allFragments.length) {
92
92
  knownComponentsCache = {
93
- count: allSegments.length,
94
- set: new Set(allSegments.map(s => s.segment.meta.name)),
93
+ count: allFragments.length,
94
+ set: new Set(allFragments.map(s => s.fragment.meta.name)),
95
95
  };
96
96
  }
97
97
  return knownComponentsCache.set;
98
98
  }
99
99
 
100
100
  /**
101
- * Detect composition relationships from a segment's variants
101
+ * Detect composition relationships from a fragment's variants
102
102
  */
103
103
  export function detectCompositionRelationships(
104
- segment: SegmentDefinition,
105
- allSegments: Array<{ path: string; segment: SegmentDefinition }>
104
+ fragment: FragmentDefinition,
105
+ allFragments: Array<{ path: string; fragment: FragmentDefinition }>
106
106
  ): DetectedRelationship[] {
107
107
  const relationships: DetectedRelationship[] = [];
108
- const componentName = segment.meta.name;
109
- const knownComponents = getKnownComponents(allSegments);
108
+ const componentName = fragment.meta.name;
109
+ const knownComponents = getKnownComponents(allFragments);
110
110
  const usedComponents = new Set<string>();
111
111
 
112
112
  // Analyze each variant's render output
113
- for (const variant of segment.variants || []) {
113
+ for (const variant of fragment.variants || []) {
114
114
  try {
115
115
  const rendered = variant.render();
116
116
  const componentNames = collectComponentNames(rendered);
@@ -152,8 +152,8 @@ export function detectCompositionRelationships(
152
152
  * - Components share overlapping variant patterns (future enhancement)
153
153
  */
154
154
  export function detectSiblingRelationships(
155
- _segment: SegmentDefinition,
156
- _allSegments: Array<{ path: string; segment: SegmentDefinition }>
155
+ _fragment: FragmentDefinition,
156
+ _allFragments: Array<{ path: string; fragment: FragmentDefinition }>
157
157
  ): DetectedRelationship[] {
158
158
  // Disabled: Category-based sibling detection creates noise
159
159
  // Meaningful sibling relationships should be defined manually
@@ -174,8 +174,8 @@ export function detectSiblingRelationships(
174
174
  * but that requires NLP/semantic analysis which is out of scope.
175
175
  */
176
176
  export function detectAlternativeRelationships(
177
- _segment: SegmentDefinition,
178
- _allSegments: Array<{ path: string; segment: SegmentDefinition }>
177
+ _fragment: FragmentDefinition,
178
+ _allFragments: Array<{ path: string; fragment: FragmentDefinition }>
179
179
  ): DetectedRelationship[] {
180
180
  // Disabled: Pattern-based alternative detection is too weak
181
181
  // Meaningful alternatives should be defined manually with clear rationale
@@ -187,10 +187,10 @@ export function detectAlternativeRelationships(
187
187
  * Uses caching to avoid expensive render() calls on subsequent views
188
188
  */
189
189
  export function detectAllRelationships(
190
- segment: SegmentDefinition,
191
- allSegments: Array<{ path: string; segment: SegmentDefinition }>
190
+ fragment: FragmentDefinition,
191
+ allFragments: Array<{ path: string; fragment: FragmentDefinition }>
192
192
  ): DetectedRelationship[] {
193
- const cacheKey = segment.meta.name;
193
+ const cacheKey = fragment.meta.name;
194
194
  const now = Date.now();
195
195
 
196
196
  // Check cache first
@@ -200,9 +200,9 @@ export function detectAllRelationships(
200
200
  }
201
201
 
202
202
  const all = [
203
- ...detectCompositionRelationships(segment, allSegments),
204
- ...detectSiblingRelationships(segment, allSegments),
205
- ...detectAlternativeRelationships(segment, allSegments),
203
+ ...detectCompositionRelationships(fragment, allFragments),
204
+ ...detectSiblingRelationships(fragment, allFragments),
205
+ ...detectAlternativeRelationships(fragment, allFragments),
206
206
  ];
207
207
 
208
208
  // Deduplicate by component name, keeping highest confidence