@fragments-sdk/cli 0.6.0 → 0.7.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 (178) hide show
  1. package/dist/bin.js +529 -285
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-F7ITZPDJ.js → chunk-32VIEOQY.js} +18 -18
  4. package/dist/chunk-32VIEOQY.js.map +1 -0
  5. package/dist/{chunk-SSLQXHNX.js → chunk-5ITIP3ES.js} +27 -27
  6. package/dist/chunk-5ITIP3ES.js.map +1 -0
  7. package/dist/{chunk-RVRTRESS.js → chunk-DQHWLAUV.js} +29 -29
  8. package/dist/chunk-DQHWLAUV.js.map +1 -0
  9. package/dist/{chunk-Q7GOHVOK.js → chunk-GCZMFLDI.js} +67 -32
  10. package/dist/chunk-GCZMFLDI.js.map +1 -0
  11. package/dist/{chunk-6JBGU74P.js → chunk-GHYYFAQN.js} +23 -23
  12. package/dist/chunk-GHYYFAQN.js.map +1 -0
  13. package/dist/{chunk-NWQ4CJOQ.js → chunk-GKX2HPZ6.js} +40 -40
  14. package/dist/chunk-GKX2HPZ6.js.map +1 -0
  15. package/dist/{chunk-D35RGPAG.js → chunk-U6VTHBNI.js} +499 -83
  16. package/dist/chunk-U6VTHBNI.js.map +1 -0
  17. package/dist/{core-SKRPJQZG.js → core-SFHPYR5H.js} +24 -26
  18. package/dist/{generate-7AF7WRVK.js → generate-54GJAWUY.js} +5 -5
  19. package/dist/generate-54GJAWUY.js.map +1 -0
  20. package/dist/index.d.ts +23 -27
  21. package/dist/index.js +10 -10
  22. package/dist/{init-WKGDPYI4.js → init-EIM5WNMP.js} +5 -5
  23. package/dist/{init-WKGDPYI4.js.map → init-EIM5WNMP.js.map} +1 -1
  24. package/dist/mcp-bin.js +73 -73
  25. package/dist/mcp-bin.js.map +1 -1
  26. package/dist/scan-KQBKUS64.js +12 -0
  27. package/dist/{service-F3E4JJM7.js → service-ED2LNCTU.js} +6 -6
  28. package/dist/{static-viewer-4LQZ5AGA.js → static-viewer-Q4F4QP5M.js} +4 -4
  29. package/dist/{test-CJDNJTPZ.js → test-6VN2DA3S.js} +19 -19
  30. package/dist/test-6VN2DA3S.js.map +1 -0
  31. package/dist/{tokens-JAJABYXP.js → tokens-P2B7ZAM3.js} +5 -5
  32. package/dist/{viewer-R3Q6WAMJ.js → viewer-GM7IQPPB.js} +199 -199
  33. package/dist/viewer-GM7IQPPB.js.map +1 -0
  34. package/package.json +2 -2
  35. package/src/ai.ts +5 -5
  36. package/src/analyze.ts +11 -11
  37. package/src/bin.ts +24 -1
  38. package/src/build.ts +64 -21
  39. package/src/commands/a11y.ts +6 -6
  40. package/src/commands/add.ts +11 -11
  41. package/src/commands/audit.ts +4 -4
  42. package/src/commands/baseline.ts +3 -3
  43. package/src/commands/build.ts +8 -8
  44. package/src/commands/compare.ts +20 -20
  45. package/src/commands/context.ts +16 -16
  46. package/src/commands/enhance.ts +36 -36
  47. package/src/commands/generate.ts +1 -1
  48. package/src/commands/graph.ts +274 -0
  49. package/src/commands/init.ts +1 -1
  50. package/src/commands/link/figma.ts +82 -82
  51. package/src/commands/link/index.ts +3 -3
  52. package/src/commands/link/storybook.ts +9 -9
  53. package/src/commands/list.ts +2 -2
  54. package/src/commands/reset.ts +15 -15
  55. package/src/commands/scan.ts +27 -27
  56. package/src/commands/storygen.ts +24 -24
  57. package/src/commands/validate.ts +2 -2
  58. package/src/commands/verify.ts +8 -8
  59. package/src/core/auto-props.ts +4 -4
  60. package/src/core/composition.test.ts +36 -36
  61. package/src/core/composition.ts +83 -20
  62. package/src/core/config.ts +6 -6
  63. package/src/core/{defineSegment.ts → defineFragment.ts} +16 -22
  64. package/src/core/discovery.ts +6 -6
  65. package/src/core/figma.ts +2 -2
  66. package/src/core/graph-extractor.test.ts +542 -0
  67. package/src/core/graph-extractor.ts +601 -0
  68. package/src/core/importAnalyzer.ts +6 -1
  69. package/src/core/index.ts +22 -23
  70. package/src/core/loader.ts +22 -22
  71. package/src/core/node.ts +5 -5
  72. package/src/core/parser.ts +31 -31
  73. package/src/core/previewLoader.ts +1 -1
  74. package/src/core/schema.ts +16 -16
  75. package/src/core/storyAdapter.test.ts +87 -87
  76. package/src/core/storyAdapter.ts +16 -16
  77. package/src/core/types.ts +21 -26
  78. package/src/diff.ts +22 -22
  79. package/src/index.ts +2 -2
  80. package/src/mcp/server.ts +80 -80
  81. package/src/migrate/__tests__/utils/utils.test.ts +3 -3
  82. package/src/migrate/bin.ts +4 -4
  83. package/src/migrate/converter.ts +16 -16
  84. package/src/migrate/index.ts +3 -3
  85. package/src/migrate/migrate.ts +3 -3
  86. package/src/migrate/parser.ts +8 -8
  87. package/src/migrate/report.ts +2 -2
  88. package/src/migrate/types.ts +4 -4
  89. package/src/screenshot.ts +22 -22
  90. package/src/service/__tests__/props-extractor.test.ts +15 -15
  91. package/src/service/analytics.ts +39 -39
  92. package/src/service/enhance/codebase-scanner.ts +1 -1
  93. package/src/service/enhance/index.ts +1 -1
  94. package/src/service/enhance/props-extractor.ts +2 -2
  95. package/src/service/enhance/types.ts +2 -2
  96. package/src/service/index.ts +2 -2
  97. package/src/service/metrics-store.ts +1 -1
  98. package/src/service/patch-generator.ts +1 -1
  99. package/src/setup.ts +52 -52
  100. package/src/shared/dev-server-client.ts +7 -7
  101. package/src/shared/fragment-loader.ts +59 -0
  102. package/src/shared/index.ts +1 -1
  103. package/src/shared/types.ts +4 -4
  104. package/src/static-viewer.ts +35 -35
  105. package/src/test/discovery.ts +6 -6
  106. package/src/test/index.ts +5 -5
  107. package/src/test/reporters/console.ts +1 -1
  108. package/src/test/reporters/junit.ts +1 -1
  109. package/src/test/runner.ts +7 -7
  110. package/src/test/types.ts +3 -3
  111. package/src/test/watch.ts +9 -9
  112. package/src/validators.ts +26 -26
  113. package/src/viewer/__tests__/render-utils.test.ts +28 -28
  114. package/src/viewer/__tests__/viewer-integration.test.ts +4 -4
  115. package/src/viewer/cli/health.ts +26 -26
  116. package/src/viewer/components/App.tsx +201 -103
  117. package/src/viewer/components/BottomPanel.tsx +17 -17
  118. package/src/viewer/components/CodePanel.tsx +3 -3
  119. package/src/viewer/components/CommandPalette.tsx +11 -11
  120. package/src/viewer/components/ComponentGraph.tsx +28 -28
  121. package/src/viewer/components/ComponentHeader.tsx +2 -2
  122. package/src/viewer/components/ContractPanel.tsx +6 -6
  123. package/src/viewer/components/FigmaEmbed.tsx +9 -9
  124. package/src/viewer/components/HealthDashboard.tsx +17 -17
  125. package/src/viewer/components/Icons.tsx +53 -1
  126. package/src/viewer/components/InteractionsPanel.tsx +2 -2
  127. package/src/viewer/components/IsolatedPreviewFrame.tsx +6 -6
  128. package/src/viewer/components/IsolatedRender.tsx +10 -10
  129. package/src/viewer/components/Layout.tsx +7 -3
  130. package/src/viewer/components/LeftSidebar.tsx +92 -114
  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 +77 -48
  134. package/src/viewer/components/PreviewToolbar.tsx +57 -10
  135. package/src/viewer/components/RightSidebar.tsx +9 -9
  136. package/src/viewer/components/Sidebar.tsx +17 -17
  137. package/src/viewer/components/StoryRenderer.tsx +2 -2
  138. package/src/viewer/components/TokenStylePanel.tsx +1 -1
  139. package/src/viewer/components/UsageSection.tsx +2 -2
  140. package/src/viewer/components/VariantMatrix.tsx +11 -11
  141. package/src/viewer/components/VariantRenderer.tsx +3 -3
  142. package/src/viewer/components/VariantTabs.tsx +2 -2
  143. package/src/viewer/components/ViewportSelector.tsx +56 -45
  144. package/src/viewer/components/_future/CreatePage.tsx +6 -6
  145. package/src/viewer/composition-renderer.ts +11 -11
  146. package/src/viewer/constants/ui.ts +4 -4
  147. package/src/viewer/entry.tsx +40 -40
  148. package/src/viewer/hooks/useFigmaIntegration.ts +1 -1
  149. package/src/viewer/hooks/usePreviewBridge.ts +5 -5
  150. package/src/viewer/hooks/useUrlState.ts +6 -6
  151. package/src/viewer/index.ts +2 -2
  152. package/src/viewer/intelligence/healthReport.ts +17 -17
  153. package/src/viewer/intelligence/styleDrift.ts +1 -1
  154. package/src/viewer/intelligence/usageScanner.ts +1 -1
  155. package/src/viewer/preview-frame.html +22 -13
  156. package/src/viewer/render-template.html +1 -1
  157. package/src/viewer/render-utils.ts +21 -21
  158. package/src/viewer/server.ts +18 -18
  159. package/src/viewer/styles/globals.css +42 -81
  160. package/src/viewer/utils/detectRelationships.ts +22 -22
  161. package/src/viewer/vite-plugin.ts +213 -213
  162. package/dist/chunk-6JBGU74P.js.map +0 -1
  163. package/dist/chunk-D35RGPAG.js.map +0 -1
  164. package/dist/chunk-F7ITZPDJ.js.map +0 -1
  165. package/dist/chunk-NWQ4CJOQ.js.map +0 -1
  166. package/dist/chunk-Q7GOHVOK.js.map +0 -1
  167. package/dist/chunk-RVRTRESS.js.map +0 -1
  168. package/dist/chunk-SSLQXHNX.js.map +0 -1
  169. package/dist/generate-7AF7WRVK.js.map +0 -1
  170. package/dist/scan-K6JNMCGM.js +0 -12
  171. package/dist/test-CJDNJTPZ.js.map +0 -1
  172. package/dist/viewer-R3Q6WAMJ.js.map +0 -1
  173. package/src/shared/segment-loader.ts +0 -59
  174. /package/dist/{core-SKRPJQZG.js.map → core-SFHPYR5H.js.map} +0 -0
  175. /package/dist/{scan-K6JNMCGM.js.map → scan-KQBKUS64.js.map} +0 -0
  176. /package/dist/{service-F3E4JJM7.js.map → service-ED2LNCTU.js.map} +0 -0
  177. /package/dist/{static-viewer-4LQZ5AGA.js.map → static-viewer-Q4F4QP5M.js.map} +0 -0
  178. /package/dist/{tokens-JAJABYXP.js.map → tokens-P2B7ZAM3.js.map} +0 -0
@@ -1,9 +1,8 @@
1
1
  import { useState, useMemo, useRef, useEffect, useCallback } from 'react';
2
- import type { SegmentDefinition } from '../../core/index.js';
2
+ import type { FragmentDefinition } from '../../core/index.js';
3
3
  import { BRAND } from '../../core/index.js';
4
4
  import { useTheme } from './ThemeProvider.js';
5
- import { SunIcon, MoonIcon, DashboardIcon } from './Icons.js';
6
- import { Sidebar, Input, Button, Badge, Text } from '@fragments/ui';
5
+ import { Sidebar, useSidebar, Badge, Text, ThemeToggle } from '@fragments/ui';
7
6
 
8
7
  // Fuzzy matching utility
9
8
  interface FuzzyMatch {
@@ -46,19 +45,19 @@ function fuzzyMatch(text: string, pattern: string): FuzzyMatch | null {
46
45
  }
47
46
 
48
47
  interface SearchResult {
49
- item: { path: string; segment: SegmentDefinition };
48
+ item: { path: string; fragment: FragmentDefinition };
50
49
  score: number;
51
50
  nameIndices: number[];
52
51
  }
53
52
 
54
- function searchSegment(
55
- item: { path: string; segment: SegmentDefinition },
53
+ function searchFragment(
54
+ item: { path: string; fragment: FragmentDefinition },
56
55
  query: string
57
56
  ): SearchResult | null {
58
- const { segment } = item;
59
- // Skip invalid segments
60
- if (!segment?.meta) return null;
61
- const { name, category, tags } = segment.meta;
57
+ const { fragment } = item;
58
+ // Skip invalid fragments
59
+ if (!fragment?.meta) return null;
60
+ const { name, category, tags } = fragment.meta;
62
61
 
63
62
  const nameMatch = fuzzyMatch(name, query);
64
63
  if (nameMatch) {
@@ -120,34 +119,34 @@ function HighlightedText({ text, indices }: { text: string; indices: number[] })
120
119
  }
121
120
 
122
121
  interface LeftSidebarProps {
123
- segments: Array<{ path: string; segment: SegmentDefinition }>;
124
- activeSegment: string | null;
122
+ fragments: Array<{ path: string; fragment: FragmentDefinition }>;
123
+ activeFragment: string | null;
124
+ searchQuery: string;
125
125
  onSelect: (path: string) => void;
126
126
  showHealth?: boolean;
127
127
  onHealthClick?: () => void;
128
128
  }
129
129
 
130
- export function LeftSidebar({ segments, activeSegment, onSelect, showHealth, onHealthClick }: LeftSidebarProps) {
131
- const [search, setSearch] = useState('');
130
+ export function LeftSidebar({ fragments, activeFragment, searchQuery, onSelect, showHealth, onHealthClick }: LeftSidebarProps) {
132
131
  const [focusedIndex, setFocusedIndex] = useState(-1);
133
- const { theme, setTheme, resolvedTheme } = useTheme();
134
- const searchInputRef = useRef<HTMLInputElement>(null);
132
+ const { setTheme, resolvedTheme } = useTheme();
133
+ const { isMobile, setOpen } = useSidebar();
135
134
  const navRef = useRef<HTMLDivElement>(null);
136
135
 
137
- const debouncedSearch = useDebounce(search, 150);
136
+ const debouncedSearch = useDebounce(searchQuery, 150);
138
137
 
139
138
  const searchResults = useMemo(() => {
140
139
  if (!debouncedSearch) return null;
141
140
 
142
141
  const results: SearchResult[] = [];
143
- for (const item of segments) {
144
- const result = searchSegment(item, debouncedSearch);
142
+ for (const item of fragments) {
143
+ const result = searchFragment(item, debouncedSearch);
145
144
  if (result) results.push(result);
146
145
  }
147
146
 
148
147
  results.sort((a, b) => b.score - a.score);
149
148
  return results;
150
- }, [segments, debouncedSearch]);
149
+ }, [fragments, debouncedSearch]);
151
150
 
152
151
  const highlightMap = useMemo(() => {
153
152
  const map = new Map<string, number[]>();
@@ -162,94 +161,96 @@ export function LeftSidebar({ segments, activeSegment, onSelect, showHealth, onH
162
161
  const grouped = useMemo(() => {
163
162
  const source = searchResults
164
163
  ? searchResults.map(r => r.item)
165
- : segments;
164
+ : fragments;
166
165
 
167
- const groups: Record<string, typeof segments> = {};
166
+ const groups: Record<string, typeof fragments> = {};
168
167
  for (const item of source) {
169
- // Skip invalid segments
170
- if (!item.segment?.meta) continue;
171
- const category = item.segment.meta.category || 'uncategorized';
168
+ // Skip invalid fragments
169
+ if (!item.fragment?.meta) continue;
170
+ const category = item.fragment.meta.category || 'uncategorized';
172
171
  if (!groups[category]) groups[category] = [];
173
172
  groups[category].push(item);
174
173
  }
175
174
  return groups;
176
- }, [segments, searchResults]);
177
-
178
- const toggleTheme = () => {
179
- // Simple toggle between light and dark
180
- setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
181
- };
175
+ }, [fragments, searchResults]);
182
176
 
183
177
  const flatItems = useMemo(() => {
184
- const items: Array<{ path: string; segment: SegmentDefinition }> = [];
178
+ const items: Array<{ path: string; fragment: FragmentDefinition }> = [];
185
179
  const sortedEntries = Object.entries(grouped).sort(([a], [b]) =>
186
180
  a.toLowerCase().localeCompare(b.toLowerCase())
187
181
  );
188
182
  for (const [, categoryItems] of sortedEntries) {
189
183
  const sorted = [...categoryItems]
190
- .filter(item => item.segment?.meta?.name)
184
+ .filter(item => item.fragment?.meta?.name)
191
185
  .sort((a, b) =>
192
- a.segment.meta.name.toLowerCase().localeCompare(b.segment.meta.name.toLowerCase())
186
+ a.fragment.meta.name.toLowerCase().localeCompare(b.fragment.meta.name.toLowerCase())
193
187
  );
194
188
  items.push(...sorted);
195
189
  }
196
190
  return items;
197
191
  }, [grouped]);
198
192
 
193
+ const keyboardItems = useMemo(() => {
194
+ const componentItems = flatItems.map((item) => ({ type: 'component' as const, path: item.path }));
195
+ if (!onHealthClick) return componentItems;
196
+ return [{ type: 'dashboard' as const }, ...componentItems];
197
+ }, [flatItems, onHealthClick]);
198
+
199
199
  useEffect(() => {
200
- if (focusedIndex >= 0 && focusedIndex < flatItems.length && navRef.current) {
200
+ if (focusedIndex >= 0 && focusedIndex < keyboardItems.length && navRef.current) {
201
201
  // Query all nav item buttons rendered by Sidebar.Item inside the nav
202
202
  const buttons = navRef.current.querySelectorAll<HTMLButtonElement>('li > button[type="button"]');
203
203
  if (buttons[focusedIndex]) {
204
204
  buttons[focusedIndex].focus();
205
205
  }
206
206
  }
207
- }, [focusedIndex, flatItems.length]);
207
+ }, [focusedIndex, keyboardItems.length]);
208
208
 
209
209
  useEffect(() => {
210
210
  setFocusedIndex(-1);
211
- }, [search]);
211
+ }, [searchQuery]);
212
212
 
213
- const handleKeyDown = useCallback((e: KeyboardEvent) => {
214
- const target = e.target as HTMLElement;
215
- if ((target.tagName === 'INPUT' && target !== searchInputRef.current) ||
216
- target.tagName === 'TEXTAREA' || target.isContentEditable) {
217
- return;
213
+ const handleSelect = (path: string) => {
214
+ onSelect(path);
215
+ if (isMobile) {
216
+ setOpen(false);
218
217
  }
218
+ };
219
219
 
220
- if ((e.key === '/' && !e.metaKey && !e.ctrlKey) ||
221
- (e.key === 'k' && (e.metaKey || e.ctrlKey))) {
222
- e.preventDefault();
223
- searchInputRef.current?.focus();
224
- searchInputRef.current?.select();
220
+ const handleHealthClick = () => {
221
+ onHealthClick?.();
222
+ if (isMobile) {
223
+ setOpen(false);
224
+ }
225
+ };
226
+
227
+ const handleKeyDown = useCallback((e: KeyboardEvent) => {
228
+ const target = e.target as HTMLElement;
229
+ if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
225
230
  return;
226
231
  }
227
232
 
228
233
  if (e.key === 'Escape') {
229
- if (document.activeElement === searchInputRef.current) {
230
- setSearch('');
231
- searchInputRef.current?.blur();
232
- if (flatItems.length > 0) setFocusedIndex(0);
233
- } else {
234
- setSearch('');
235
- setFocusedIndex(-1);
236
- }
234
+ setFocusedIndex(-1);
237
235
  return;
238
236
  }
239
237
 
240
- if (document.activeElement === searchInputRef.current) return;
241
-
242
238
  if (e.key === 'ArrowDown') {
243
239
  e.preventDefault();
244
- setFocusedIndex(prev => (prev + 1) >= flatItems.length ? 0 : prev + 1);
240
+ setFocusedIndex(prev => (prev + 1) >= keyboardItems.length ? 0 : prev + 1);
245
241
  } else if (e.key === 'ArrowUp') {
246
242
  e.preventDefault();
247
- setFocusedIndex(prev => (prev - 1) < 0 ? flatItems.length - 1 : prev - 1);
248
- } else if (e.key === 'Enter' && focusedIndex >= 0 && focusedIndex < flatItems.length) {
243
+ setFocusedIndex(prev => (prev - 1) < 0 ? keyboardItems.length - 1 : prev - 1);
244
+ } else if (e.key === 'Enter' && focusedIndex >= 0 && focusedIndex < keyboardItems.length) {
249
245
  e.preventDefault();
250
- onSelect(flatItems[focusedIndex].path);
246
+ const currentItem = keyboardItems[focusedIndex];
247
+ if (currentItem.type === 'dashboard') {
248
+ handleHealthClick();
249
+ } else {
250
+ handleSelect(currentItem.path);
251
+ }
251
252
  }
252
- }, [flatItems, focusedIndex, onSelect]);
253
+ }, [keyboardItems, focusedIndex, handleHealthClick, handleSelect]);
253
254
 
254
255
  useEffect(() => {
255
256
  document.addEventListener('keydown', handleKeyDown);
@@ -261,65 +262,42 @@ export function LeftSidebar({ segments, activeSegment, onSelect, showHealth, onH
261
262
  );
262
263
 
263
264
  return (
264
- <Sidebar collapsible="none" style={{ height: '100%', backgroundColor: 'var(--bg-secondary)' }}>
265
+ <>
265
266
  {/* Header */}
266
267
  <Sidebar.Header>
267
268
  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
268
269
  <Text size="sm" weight="medium">{BRAND.name}</Text>
269
- <Button
270
- variant="ghost"
271
- size="sm"
272
- icon
273
- onClick={toggleTheme}
274
- aria-label={`Theme: ${resolvedTheme}`}
275
- >
276
- {resolvedTheme === 'dark' ? (
277
- <MoonIcon />
278
- ) : (
279
- <SunIcon />
280
- )}
281
- </Button>
270
+ <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
271
+ <ThemeToggle
272
+ size="sm"
273
+ value={resolvedTheme}
274
+ onValueChange={(value) => setTheme(value)}
275
+ aria-label={`Theme: ${resolvedTheme}`}
276
+ />
277
+ <Sidebar.CollapseToggle />
278
+ </div>
282
279
  </div>
283
280
  </Sidebar.Header>
284
281
 
285
- {/* Search */}
286
- <div style={{ padding: '12px' }}>
287
- <Input
288
- ref={searchInputRef}
289
- value={search}
290
- onChange={(value: string) => setSearch(value)}
291
- placeholder="Search"
292
- size="sm"
293
- />
294
- </div>
295
-
296
- {/* Dashboard Link */}
297
- {onHealthClick && (
298
- <div style={{ padding: '0 8px 12px' }}>
299
- <Button
300
- variant="ghost"
301
- size="sm"
302
- onClick={onHealthClick}
303
- style={{
304
- width: '100%',
305
- justifyContent: 'flex-start',
306
- ...(showHealth ? { backgroundColor: 'var(--bg-hover)', color: 'var(--text-primary)' } : {}),
307
- }}
308
- >
309
- <DashboardIcon />
310
- <span style={{ marginLeft: '8px' }}>Dashboard</span>
311
- </Button>
312
- </div>
313
- )}
314
-
315
282
  {/* Component list */}
316
- <div ref={navRef}>
283
+ <div ref={navRef} style={{ flex: 1, minHeight: 0, display: 'flex', overflow: 'hidden' }}>
317
284
  <Sidebar.Nav aria-label="Components">
285
+ {onHealthClick && (
286
+ <Sidebar.Section>
287
+ <Sidebar.Item
288
+ active={!!showHealth}
289
+ onClick={handleHealthClick}
290
+ >
291
+ Dashboard
292
+ </Sidebar.Item>
293
+ </Sidebar.Section>
294
+ )}
295
+
318
296
  {sortedEntries.map(([category, items]) => {
319
297
  const sortedItems = [...items]
320
- .filter(item => item.segment?.meta?.name)
298
+ .filter(item => item.fragment?.meta?.name)
321
299
  .sort((a, b) =>
322
- a.segment.meta.name.toLowerCase().localeCompare(b.segment.meta.name.toLowerCase())
300
+ a.fragment.meta.name.toLowerCase().localeCompare(b.fragment.meta.name.toLowerCase())
323
301
  );
324
302
 
325
303
  return (
@@ -330,11 +308,11 @@ export function LeftSidebar({ segments, activeSegment, onSelect, showHealth, onH
330
308
  return (
331
309
  <Sidebar.Item
332
310
  key={item.path}
333
- active={activeSegment === item.path}
334
- onClick={() => onSelect(item.path)}
311
+ active={activeFragment === item.path}
312
+ onClick={() => handleSelect(item.path)}
335
313
  >
336
314
  <HighlightedText
337
- text={item.segment.meta.name}
315
+ text={item.fragment.meta.name}
338
316
  indices={nameIndices}
339
317
  />
340
318
  </Sidebar.Item>
@@ -354,8 +332,8 @@ export function LeftSidebar({ segments, activeSegment, onSelect, showHealth, onH
354
332
 
355
333
  {/* Footer */}
356
334
  <Sidebar.Footer>
357
- <Badge size="sm">{segments.length} components</Badge>
335
+ <Badge size="sm">{fragments.length} components</Badge>
358
336
  </Sidebar.Footer>
359
- </Sidebar>
337
+ </>
360
338
  );
361
339
  }
@@ -42,8 +42,8 @@ const DEFAULT_VIEWPORTS: ViewportConfig[] = [
42
42
  interface MultiViewportPreviewProps {
43
43
  /** Component name for error boundary */
44
44
  componentName: string;
45
- /** Segment path for iframe rendering */
46
- segmentPath: string;
45
+ /** Fragment path for iframe rendering */
46
+ fragmentPath: string;
47
47
  /** Variant name for iframe rendering */
48
48
  variantName: string;
49
49
  /** Render function that returns the component (fallback) */
@@ -60,7 +60,7 @@ interface MultiViewportPreviewProps {
60
60
 
61
61
  export function MultiViewportPreview({
62
62
  componentName,
63
- segmentPath,
63
+ fragmentPath,
64
64
  variantName,
65
65
  renderContent,
66
66
  previewTheme,
@@ -203,7 +203,7 @@ export function MultiViewportPreview({
203
203
  key={`${vp.name}-${vp.width}`}
204
204
  viewport={vp}
205
205
  componentName={componentName}
206
- segmentPath={segmentPath}
206
+ fragmentPath={fragmentPath}
207
207
  variantName={variantName}
208
208
  renderContent={renderContent}
209
209
  previewTheme={previewTheme}
@@ -220,7 +220,7 @@ export function MultiViewportPreview({
220
220
  interface ViewportPanelProps {
221
221
  viewport: ViewportConfig;
222
222
  componentName: string;
223
- segmentPath: string;
223
+ fragmentPath: string;
224
224
  variantName: string;
225
225
  renderContent: () => ReactNode;
226
226
  previewTheme: "light" | "dark";
@@ -231,7 +231,7 @@ interface ViewportPanelProps {
231
231
  function ViewportPanel({
232
232
  viewport,
233
233
  componentName,
234
- segmentPath,
234
+ fragmentPath,
235
235
  variantName,
236
236
  renderContent,
237
237
  previewTheme,
@@ -247,7 +247,7 @@ function ViewportPanel({
247
247
  previewTheme={previewTheme}
248
248
  background={background}
249
249
  componentName={componentName}
250
- segmentPath={segmentPath}
250
+ fragmentPath={fragmentPath}
251
251
  variantName={variantName}
252
252
  renderContent={renderContent}
253
253
  useIframeIsolation={useIframeIsolation}
@@ -264,7 +264,7 @@ function ViewportPanel({
264
264
  previewTheme={previewTheme}
265
265
  background={background}
266
266
  componentName={componentName}
267
- segmentPath={segmentPath}
267
+ fragmentPath={fragmentPath}
268
268
  variantName={variantName}
269
269
  renderContent={renderContent}
270
270
  useIframeIsolation={useIframeIsolation}
@@ -280,7 +280,7 @@ interface DeviceMockupProps {
280
280
  previewTheme: "light" | "dark";
281
281
  background: BackgroundOption;
282
282
  componentName: string;
283
- segmentPath: string;
283
+ fragmentPath: string;
284
284
  variantName: string;
285
285
  renderContent: () => ReactNode;
286
286
  useIframeIsolation: boolean;
@@ -294,7 +294,7 @@ function DeviceMockup({
294
294
  previewTheme,
295
295
  background,
296
296
  componentName,
297
- segmentPath,
297
+ fragmentPath,
298
298
  variantName,
299
299
  renderContent,
300
300
  useIframeIsolation,
@@ -427,7 +427,7 @@ function DeviceMockup({
427
427
  >
428
428
  {useIframeIsolation ? (
429
429
  <IsolatedPreviewFrame
430
- segmentPath={segmentPath}
430
+ fragmentPath={fragmentPath}
431
431
  variantName={variantName}
432
432
  theme={previewTheme}
433
433
  width="100%"
@@ -481,7 +481,7 @@ interface DesktopMockupProps {
481
481
  previewTheme: "light" | "dark";
482
482
  background: BackgroundOption;
483
483
  componentName: string;
484
- segmentPath: string;
484
+ fragmentPath: string;
485
485
  variantName: string;
486
486
  renderContent: () => ReactNode;
487
487
  useIframeIsolation: boolean;
@@ -494,7 +494,7 @@ function DesktopMockup({
494
494
  previewTheme,
495
495
  background,
496
496
  componentName,
497
- segmentPath,
497
+ fragmentPath,
498
498
  variantName,
499
499
  renderContent,
500
500
  useIframeIsolation,
@@ -574,7 +574,7 @@ function DesktopMockup({
574
574
  >
575
575
  {useIframeIsolation ? (
576
576
  <IsolatedPreviewFrame
577
- segmentPath={segmentPath}
577
+ fragmentPath={fragmentPath}
578
578
  variantName={variantName}
579
579
  theme={previewTheme}
580
580
  width="100%"
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { memo, type ReactNode } from 'react';
10
- import type { SegmentVariant } from '../../core/index.js';
10
+ import type { FragmentVariant } from '../../core/index.js';
11
11
  import { ErrorBoundary } from './ErrorBoundary.js';
12
12
  import { FigmaEmbed } from './FigmaEmbed.js';
13
13
  import { VariantMatrix } from './VariantMatrix.js';
@@ -20,9 +20,9 @@ import { getViewportWidth, type ViewportPreset, type ViewportSize } from './View
20
20
  interface PreviewAreaProps {
21
21
  // Component data
22
22
  componentName: string;
23
- segmentPath: string;
24
- variant: SegmentVariant | undefined;
25
- variants: SegmentVariant[] | undefined;
23
+ fragmentPath: string;
24
+ variant: FragmentVariant | undefined;
25
+ variants: FragmentVariant[] | undefined;
26
26
 
27
27
  // View settings
28
28
  zoom: ZoomLevel;
@@ -185,7 +185,7 @@ const PreviewContent = memo(function PreviewContent({ zoom, previewTheme, backgr
185
185
 
186
186
  export function PreviewArea({
187
187
  componentName,
188
- segmentPath,
188
+ fragmentPath,
189
189
  variant,
190
190
  variants,
191
191
  zoom,
@@ -210,7 +210,7 @@ export function PreviewArea({
210
210
  <VariantMatrix
211
211
  variants={variants}
212
212
  componentName={componentName}
213
- segmentPath={segmentPath}
213
+ fragmentPath={fragmentPath}
214
214
  zoom={zoom}
215
215
  previewTheme={previewTheme}
216
216
  background={background}
@@ -227,7 +227,7 @@ export function PreviewArea({
227
227
  return (
228
228
  <MultiViewportPreview
229
229
  componentName={componentName}
230
- segmentPath={segmentPath}
230
+ fragmentPath={fragmentPath}
231
231
  variantName={variant.name}
232
232
  renderContent={renderContent}
233
233
  previewTheme={previewTheme}
@@ -257,7 +257,7 @@ export function PreviewArea({
257
257
  <DeviceMockup type={viewport as 'tablet' | 'mobile'} width={viewportWidth}>
258
258
  {useIframeIsolation ? (
259
259
  <IsolatedPreviewFrame
260
- segmentPath={segmentPath}
260
+ fragmentPath={fragmentPath}
261
261
  variantName={variant.name}
262
262
  theme={previewTheme}
263
263
  width={viewportWidth}
@@ -300,7 +300,7 @@ export function PreviewArea({
300
300
  <DeviceMockup type={viewport as 'tablet' | 'mobile'} width={viewportWidth}>
301
301
  {useIframeIsolation ? (
302
302
  <IsolatedPreviewFrame
303
- segmentPath={segmentPath}
303
+ fragmentPath={fragmentPath}
304
304
  variantName={variant.name}
305
305
  theme={previewTheme}
306
306
  width={viewportWidth}
@@ -337,7 +337,7 @@ export function PreviewArea({
337
337
  >
338
338
  {useIframeIsolation ? (
339
339
  <IsolatedPreviewFrame
340
- segmentPath={segmentPath}
340
+ fragmentPath={fragmentPath}
341
341
  variantName={variant.name}
342
342
  theme={previewTheme}
343
343
  width="100%"
@@ -420,7 +420,7 @@ export function PreviewArea({
420
420
  }}
421
421
  >
422
422
  <IsolatedPreviewFrame
423
- segmentPath={segmentPath}
423
+ fragmentPath={fragmentPath}
424
424
  variantName={variant.name}
425
425
  theme={previewTheme}
426
426
  width="100%"