@fragments-sdk/cli 0.5.2 → 0.7.0

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 (124) hide show
  1. package/dist/bin.js +996 -79
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
  4. package/dist/chunk-6JBGU74P.js.map +1 -0
  5. package/dist/chunk-7OPWMLOE.js +1625 -0
  6. package/dist/chunk-7OPWMLOE.js.map +1 -0
  7. package/dist/{chunk-2H2JAA3U.js → chunk-CVXKXVOY.js} +3 -3
  8. package/dist/{chunk-2H2JAA3U.js.map → chunk-CVXKXVOY.js.map} +1 -1
  9. package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
  10. package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
  11. package/dist/{chunk-V7YLRR4C.js → chunk-TJ34N7C7.js} +41 -4
  12. package/dist/{chunk-V7YLRR4C.js.map → chunk-TJ34N7C7.js.map} +1 -1
  13. package/dist/{chunk-XNWDI6UT.js → chunk-XHUDJNN3.js} +5 -5
  14. package/dist/{core-DKHB7FYV.js → core-W2HYIQW6.js} +4 -4
  15. package/dist/{generate-KL24VZVD.js → generate-LMTISDIJ.js} +5 -5
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +15 -7
  18. package/dist/index.js.map +1 -1
  19. package/dist/{init-NION5S3M.js → init-7CHRKQ7P.js} +5 -5
  20. package/dist/mcp-bin.js +8 -220
  21. package/dist/mcp-bin.js.map +1 -1
  22. package/dist/scan-WY23TJCP.js +12 -0
  23. package/dist/{service-RWUMZ3EW.js → service-T2L7VLTE.js} +5 -5
  24. package/dist/static-viewer-GBR7YNF3.js +12 -0
  25. package/dist/{test-ECPEXFDN.js → test-OJRXNDO2.js} +4 -4
  26. package/dist/{tokens-ITADYVPF.js → tokens-3BWDESVM.js} +6 -6
  27. package/dist/viewer-SUFOISZM.js +1822 -0
  28. package/dist/viewer-SUFOISZM.js.map +1 -0
  29. package/package.json +6 -5
  30. package/src/bin.ts +31 -0
  31. package/src/build.ts +147 -13
  32. package/src/cli-commands.ts +18 -0
  33. package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
  34. package/src/commands/a11y-report.ts +625 -0
  35. package/src/commands/a11y.ts +168 -14
  36. package/src/commands/build.ts +16 -0
  37. package/src/commands/graph.ts +274 -0
  38. package/src/core/auto-props.ts +464 -0
  39. package/src/core/composition.ts +64 -1
  40. package/src/core/graph-extractor.test.ts +542 -0
  41. package/src/core/graph-extractor.ts +601 -0
  42. package/src/core/importAnalyzer.ts +5 -0
  43. package/src/core/schema.ts +2 -0
  44. package/src/core/types.ts +3 -1
  45. package/src/index.ts +4 -0
  46. package/src/mcp/server.ts +13 -220
  47. package/src/theme/__tests__/component-contrast.test.ts +338 -0
  48. package/src/theme/__tests__/contrast-validation.test.ts +326 -0
  49. package/src/theme/contrast.test.ts +331 -0
  50. package/src/theme/contrast.ts +246 -0
  51. package/src/theme/generator.ts +213 -1
  52. package/src/theme/index.ts +16 -0
  53. package/src/theme/types.ts +51 -0
  54. package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
  55. package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
  56. package/src/viewer/components/AccessibilityPanel.tsx +493 -433
  57. package/src/viewer/components/ActionCapture.tsx +1 -1
  58. package/src/viewer/components/ActionsPanel.tsx +142 -183
  59. package/src/viewer/components/App.tsx +276 -183
  60. package/src/viewer/components/BottomPanel.tsx +40 -80
  61. package/src/viewer/components/CodePanel.tsx +9 -87
  62. package/src/viewer/components/CommandPalette.tsx +117 -74
  63. package/src/viewer/components/ComponentGraph.tsx +143 -126
  64. package/src/viewer/components/ComponentHeader.tsx +46 -43
  65. package/src/viewer/components/ContractPanel.tsx +124 -117
  66. package/src/viewer/components/ErrorBoundary.tsx +47 -35
  67. package/src/viewer/components/FigmaEmbed.tsx +18 -13
  68. package/src/viewer/components/FragmentEditor.tsx +126 -63
  69. package/src/viewer/components/HealthDashboard.tsx +146 -171
  70. package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
  71. package/src/viewer/components/Icons.tsx +151 -98
  72. package/src/viewer/components/InteractionsPanel.tsx +317 -264
  73. package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
  74. package/src/viewer/components/IsolatedRender.tsx +12 -6
  75. package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
  76. package/src/viewer/components/LandingPage.tsx +285 -305
  77. package/src/viewer/components/Layout.tsx +12 -10
  78. package/src/viewer/components/LeftSidebar.tsx +103 -155
  79. package/src/viewer/components/MultiViewportPreview.tsx +254 -63
  80. package/src/viewer/components/PreviewArea.tsx +113 -44
  81. package/src/viewer/components/PreviewFrameHost.tsx +36 -6
  82. package/src/viewer/components/PreviewPane.tsx +2 -3
  83. package/src/viewer/components/PreviewToolbar.tsx +109 -105
  84. package/src/viewer/components/PropsEditor.tsx +154 -74
  85. package/src/viewer/components/PropsTable.tsx +95 -82
  86. package/src/viewer/components/RelationsSection.tsx +71 -40
  87. package/src/viewer/components/ResizablePanel.tsx +158 -55
  88. package/src/viewer/components/RightSidebar.tsx +46 -56
  89. package/src/viewer/components/ScreenshotButton.tsx +12 -12
  90. package/src/viewer/components/SkeletonLoader.tsx +99 -83
  91. package/src/viewer/components/StoryRenderer.tsx +4 -11
  92. package/src/viewer/components/Toast.tsx +3 -67
  93. package/src/viewer/components/TokenStylePanel.tsx +136 -118
  94. package/src/viewer/components/UsageSection.tsx +26 -26
  95. package/src/viewer/components/VariantMatrix.tsx +140 -47
  96. package/src/viewer/components/VariantTabs.tsx +24 -68
  97. package/src/viewer/components/ViewportSelector.tsx +121 -114
  98. package/src/viewer/constants/ui.ts +23 -22
  99. package/src/viewer/entry.tsx +8 -3
  100. package/src/viewer/index.ts +3 -6
  101. package/src/viewer/preview-frame.html +43 -18
  102. package/src/viewer/server.ts +7 -16
  103. package/src/viewer/styles/globals.css +46 -85
  104. package/src/viewer/utils/a11y-fixes.ts +53 -30
  105. package/dist/chunk-ICAIQ57V.js.map +0 -1
  106. package/dist/chunk-U4GQ2JTD.js +0 -832
  107. package/dist/chunk-U4GQ2JTD.js.map +0 -1
  108. package/dist/scan-ESEXV7LF.js +0 -12
  109. package/dist/static-viewer-O37MJ5B6.js +0 -12
  110. package/dist/viewer-YDGFDTK5.js +0 -11104
  111. package/dist/viewer-YDGFDTK5.js.map +0 -1
  112. package/src/viewer/postcss.config.js +0 -6
  113. package/src/viewer/tailwind.config.js +0 -37
  114. /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
  115. /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
  116. /package/dist/{chunk-XNWDI6UT.js.map → chunk-XHUDJNN3.js.map} +0 -0
  117. /package/dist/{core-DKHB7FYV.js.map → core-W2HYIQW6.js.map} +0 -0
  118. /package/dist/{generate-KL24VZVD.js.map → generate-LMTISDIJ.js.map} +0 -0
  119. /package/dist/{init-NION5S3M.js.map → init-7CHRKQ7P.js.map} +0 -0
  120. /package/dist/{scan-ESEXV7LF.js.map → scan-WY23TJCP.js.map} +0 -0
  121. /package/dist/{service-RWUMZ3EW.js.map → service-T2L7VLTE.js.map} +0 -0
  122. /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-GBR7YNF3.js.map} +0 -0
  123. /package/dist/{test-ECPEXFDN.js.map → test-OJRXNDO2.js.map} +0 -0
  124. /package/dist/{tokens-ITADYVPF.js.map → tokens-3BWDESVM.js.map} +0 -0
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  import { memo, useRef, useEffect, useState, useCallback } from 'react';
12
- import clsx from 'clsx';
13
12
  import { usePreviewBridge, type ParentMessage } from '../hooks/usePreviewBridge.js';
14
13
 
15
14
  /** Maximum number of retry attempts */
@@ -74,6 +73,7 @@ export const IsolatedPreviewFrame = memo(function IsolatedPreviewFrame({
74
73
  const { isReady, isRendering, lastError, contentSize, render, setTheme, clearError } = usePreviewBridge(iframeRef);
75
74
  const lastRenderRef = useRef<string>('');
76
75
  const isFirstLoad = useRef(true);
76
+ const [isHovered, setIsHovered] = useState(false);
77
77
 
78
78
  // Build the preview URL
79
79
  const previewUrl = '/fragments/preview/';
@@ -150,7 +150,6 @@ export const IsolatedPreviewFrame = memo(function IsolatedPreviewFrame({
150
150
 
151
151
  return (
152
152
  <div
153
- className={clsx('isolated-preview-frame group', className)}
154
153
  style={{
155
154
  position: 'relative',
156
155
  width: frameWidth,
@@ -159,25 +158,38 @@ export const IsolatedPreviewFrame = memo(function IsolatedPreviewFrame({
159
158
  ...background,
160
159
  ...style,
161
160
  }}
161
+ onMouseEnter={() => setIsHovered(true)}
162
+ onMouseLeave={() => setIsHovered(false)}
162
163
  >
163
164
  {/* Skeleton loading overlay (initial load) */}
164
165
  <div
165
- className={clsx(
166
- 'absolute inset-0 z-10 transition-opacity duration-150',
167
- showSkeleton ? 'opacity-100' : 'opacity-0 pointer-events-none'
168
- )}
169
- style={{ background: 'rgba(255, 255, 255, 0.95)' }}
166
+ style={{
167
+ position: 'absolute',
168
+ inset: 0,
169
+ zIndex: 10,
170
+ transition: 'opacity 150ms',
171
+ opacity: showSkeleton ? 1 : 0,
172
+ pointerEvents: showSkeleton ? 'auto' : 'none',
173
+ background: 'rgba(255, 255, 255, 0.95)',
174
+ }}
170
175
  >
171
176
  <PreviewSkeleton />
172
177
  </div>
173
178
 
174
179
  {/* Spinner overlay (subsequent renders) */}
175
180
  <div
176
- className={clsx(
177
- 'absolute inset-0 z-10 flex items-center justify-center transition-opacity duration-150',
178
- showSpinner ? 'opacity-100' : 'opacity-0 pointer-events-none'
179
- )}
180
- style={{ background: 'rgba(255, 255, 255, 0.8)' }}
181
+ style={{
182
+ position: 'absolute',
183
+ inset: 0,
184
+ zIndex: 10,
185
+ display: 'flex',
186
+ alignItems: 'center',
187
+ justifyContent: 'center',
188
+ transition: 'opacity 150ms',
189
+ opacity: showSpinner ? 1 : 0,
190
+ pointerEvents: showSpinner ? 'auto' : 'none',
191
+ background: 'rgba(255, 255, 255, 0.8)',
192
+ }}
181
193
  >
182
194
  <div style={{ display: 'flex', alignItems: 'center', gap: 8, color: '#6b7280', fontSize: 14 }}>
183
195
  <LoadingSpinner />
@@ -187,11 +199,19 @@ export const IsolatedPreviewFrame = memo(function IsolatedPreviewFrame({
187
199
 
188
200
  {/* Error overlay */}
189
201
  <div
190
- className={clsx(
191
- 'absolute inset-0 z-10 flex items-center justify-center p-4 transition-opacity duration-150',
192
- frameError && !isLoading ? 'opacity-100' : 'opacity-0 pointer-events-none'
193
- )}
194
- style={{ background: 'rgba(254, 242, 242, 0.95)' }}
202
+ style={{
203
+ position: 'absolute',
204
+ inset: 0,
205
+ zIndex: 10,
206
+ display: 'flex',
207
+ alignItems: 'center',
208
+ justifyContent: 'center',
209
+ padding: '16px',
210
+ transition: 'opacity 150ms',
211
+ opacity: frameError && !isLoading ? 1 : 0,
212
+ pointerEvents: frameError && !isLoading ? 'auto' : 'none',
213
+ background: 'rgba(254, 242, 242, 0.95)',
214
+ }}
195
215
  >
196
216
  <div
197
217
  style={{
@@ -236,16 +256,14 @@ export const IsolatedPreviewFrame = memo(function IsolatedPreviewFrame({
236
256
  title={`Preview: ${variantName}`}
237
257
  onLoad={handleLoad}
238
258
  onError={handleError}
239
- className={clsx(
240
- 'transition-opacity duration-150',
241
- showContent ? 'opacity-100' : 'opacity-0'
242
- )}
243
259
  style={{
244
260
  width: '100%',
245
261
  height: '100%',
246
262
  border: 'none',
247
263
  display: 'block',
248
264
  background: 'transparent',
265
+ transition: 'opacity 150ms',
266
+ opacity: showContent ? 1 : 0,
249
267
  }}
250
268
  // Security attributes
251
269
  sandbox="allow-scripts allow-same-origin"
@@ -254,9 +272,15 @@ export const IsolatedPreviewFrame = memo(function IsolatedPreviewFrame({
254
272
  {/* Size indicator */}
255
273
  {showSizeIndicator && contentSize && (
256
274
  <div
257
- className="absolute bottom-1 right-1 px-1.5 py-0.5 rounded font-mono
258
- opacity-0 group-hover:opacity-100 transition-opacity duration-150"
259
275
  style={{
276
+ position: 'absolute',
277
+ bottom: '4px',
278
+ right: '4px',
279
+ padding: '2px 6px',
280
+ borderRadius: '4px',
281
+ fontFamily: 'monospace',
282
+ opacity: isHovered ? 1 : 0,
283
+ transition: 'opacity 150ms',
260
284
  background: 'rgba(0, 0, 0, 0.5)',
261
285
  color: 'white',
262
286
  fontSize: 10,
@@ -274,10 +298,11 @@ export const IsolatedPreviewFrame = memo(function IsolatedPreviewFrame({
274
298
  */
275
299
  function PreviewSkeleton() {
276
300
  return (
277
- <div className="animate-pulse p-4">
278
- <div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-1/3 mb-3" />
279
- <div className="h-8 bg-gray-200 dark:bg-gray-700 rounded mb-2" />
280
- <div className="h-4 bg-gray-200 dark:bg-gray-700 rounded w-2/3" />
301
+ <div style={{ padding: '16px' }}>
302
+ <style>{`@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }`}</style>
303
+ <div style={{ height: '16px', background: 'var(--bg-secondary, #e5e7eb)', borderRadius: '4px', width: '33%', marginBottom: '12px', animation: 'pulse 2s ease-in-out infinite' }} />
304
+ <div style={{ height: '32px', background: 'var(--bg-secondary, #e5e7eb)', borderRadius: '4px', marginBottom: '8px', animation: 'pulse 2s ease-in-out infinite' }} />
305
+ <div style={{ height: '16px', background: 'var(--bg-secondary, #e5e7eb)', borderRadius: '4px', width: '66%', animation: 'pulse 2s ease-in-out infinite' }} />
281
306
  </div>
282
307
  );
283
308
  }
@@ -59,7 +59,7 @@ export function IsolatedRender({ segments }: IsolatedRenderProps) {
59
59
  // Apply theme
60
60
  useEffect(() => {
61
61
  document.documentElement.setAttribute("data-theme", params.theme);
62
-
62
+
63
63
  // Signal ready after a short delay for fonts and styles to settle
64
64
  const timer = setTimeout(() => {
65
65
  setReady(true);
@@ -71,9 +71,9 @@ export function IsolatedRender({ segments }: IsolatedRenderProps) {
71
71
  // Error state - missing component or variant
72
72
  if (!params.component || !params.variant) {
73
73
  return (
74
- <div className="p-4 text-red-500 font-mono text-sm">
74
+ <div style={{ padding: '16px', color: '#ef4444', fontFamily: 'monospace', fontSize: '14px' }}>
75
75
  Error: Missing component or variant parameter
76
- <pre className="mt-2 text-xs">
76
+ <pre style={{ marginTop: '8px', fontSize: '12px' }}>
77
77
  Required: ?component=ComponentName&variant=VariantName
78
78
  </pre>
79
79
  </div>
@@ -83,7 +83,7 @@ export function IsolatedRender({ segments }: IsolatedRenderProps) {
83
83
  // Error state - component/variant not found
84
84
  if (!match) {
85
85
  return (
86
- <div className="p-4 text-red-500 font-mono text-sm">
86
+ <div style={{ padding: '16px', color: '#ef4444', fontFamily: 'monospace', fontSize: '14px' }}>
87
87
  Error: Component "{params.component}" variant "{params.variant}" not
88
88
  found
89
89
  </div>
@@ -95,8 +95,14 @@ export function IsolatedRender({ segments }: IsolatedRenderProps) {
95
95
  <div
96
96
  id="isolated-render"
97
97
  data-ready={ready}
98
- className="min-h-screen p-8 flex items-center justify-center"
99
- style={getBackgroundStyle(params.background)}
98
+ style={{
99
+ minHeight: '100vh',
100
+ padding: '32px',
101
+ display: 'flex',
102
+ alignItems: 'center',
103
+ justifyContent: 'center',
104
+ ...getBackgroundStyle(params.background),
105
+ }}
100
106
  >
101
107
  <div
102
108
  style={{
@@ -2,11 +2,11 @@
2
2
  * Keyboard Shortcuts Help Modal
3
3
  *
4
4
  * Displays all available keyboard shortcuts in a modal overlay.
5
+ * Uses Fragments UI Dialog compound component.
5
6
  */
6
7
 
7
- import { useEffect } from "react";
8
+ import { Dialog, Stack, Text, Badge } from '@fragments/ui';
8
9
  import { SHORTCUTS } from "../hooks/useKeyboardShortcuts.js";
9
- import { CloseIcon } from "./Icons.js";
10
10
 
11
11
  interface KeyboardShortcutsHelpProps {
12
12
  isOpen: boolean;
@@ -14,75 +14,39 @@ interface KeyboardShortcutsHelpProps {
14
14
  }
15
15
 
16
16
  export function KeyboardShortcutsHelp({ isOpen, onClose }: KeyboardShortcutsHelpProps) {
17
- // Close on escape
18
- useEffect(() => {
19
- if (!isOpen) return;
20
-
21
- const handleEscape = (e: KeyboardEvent) => {
22
- if (e.key === "Escape") {
23
- onClose();
24
- }
25
- };
26
-
27
- document.addEventListener("keydown", handleEscape);
28
- return () => document.removeEventListener("keydown", handleEscape);
29
- }, [isOpen, onClose]);
30
-
31
- if (!isOpen) return null;
32
-
33
17
  return (
34
- <div
35
- className="fixed inset-0 z-50 flex items-center justify-center p-4"
36
- onClick={onClose}
37
- >
38
- {/* Backdrop */}
39
- <div className="absolute inset-0 bg-black/50 backdrop-blur-sm" />
40
-
41
- {/* Modal */}
42
- <div
43
- className="relative bg-[--bg-primary] border border-[--border] rounded-xl shadow-2xl max-w-md w-full"
44
- onClick={(e) => e.stopPropagation()}
45
- >
46
- {/* Header */}
47
- <div className="flex items-center justify-between px-5 py-4 border-b border-[--border]">
48
- <h2 className="text-base font-semibold text-primary">Keyboard Shortcuts</h2>
49
- <button
50
- onClick={onClose}
51
- className="p-1.5 text-tertiary hover:text-primary hover:bg-[--bg-hover] rounded-lg transition-colors"
52
- >
53
- <CloseIcon className="w-4 h-4" />
54
- </button>
55
- </div>
56
-
57
- {/* Shortcuts List */}
58
- <div className="p-5 space-y-3">
59
- {SHORTCUTS.map((shortcut, index) => (
60
- <div key={index} className="flex items-center justify-between">
61
- <span className="text-sm text-secondary">{shortcut.description}</span>
62
- <div className="flex items-center gap-1.5">
63
- {shortcut.keys.map((key, keyIndex) => (
64
- <span key={keyIndex} className="flex items-center">
65
- {keyIndex > 0 && (
66
- <span className="text-xs text-tertiary mx-1">or</span>
67
- )}
68
- <kbd className="px-2 py-1 text-xs font-mono bg-[--bg-secondary] border border-[--border] rounded text-primary">
69
- {key}
70
- </kbd>
71
- </span>
72
- ))}
73
- </div>
74
- </div>
75
- ))}
76
- </div>
77
-
78
- {/* Footer */}
79
- <div className="px-5 py-3 border-t border-[--border] bg-[--bg-secondary] rounded-b-xl">
80
- <p className="text-xs text-tertiary text-center">
81
- Press <kbd className="px-1.5 py-0.5 text-xs font-mono bg-[--bg-primary] border border-[--border] rounded">?</kbd> to toggle this help
82
- </p>
83
- </div>
84
- </div>
85
- </div>
18
+ <Dialog open={isOpen} onOpenChange={(open: boolean) => { if (!open) onClose(); }}>
19
+ <Dialog.Content size="md">
20
+ <Dialog.Header>
21
+ <Dialog.Title>Keyboard Shortcuts</Dialog.Title>
22
+ <Dialog.Close />
23
+ </Dialog.Header>
24
+ <Dialog.Body>
25
+ <Stack direction="column" gap="sm">
26
+ {SHORTCUTS.map((shortcut, index) => (
27
+ <Stack key={index} direction="row" align="center" justify="between">
28
+ <Text size="sm" color="secondary">{shortcut.description}</Text>
29
+ <Stack direction="row" align="center" gap="xs">
30
+ {shortcut.keys.map((key, keyIndex) => (
31
+ <span key={keyIndex} style={{ display: 'flex', alignItems: 'center' }}>
32
+ {keyIndex > 0 && (
33
+ <Text size="xs" color="tertiary" style={{ margin: '0 4px' }}>or</Text>
34
+ )}
35
+ <Badge variant="default" size="sm">{key}</Badge>
36
+ </span>
37
+ ))}
38
+ </Stack>
39
+ </Stack>
40
+ ))}
41
+ </Stack>
42
+ </Dialog.Body>
43
+ <Dialog.Footer>
44
+ <Text size="xs" color="tertiary" style={{ textAlign: 'center', width: '100%' }}>
45
+ Press <Badge variant="default" size="sm">?</Badge> to toggle this help
46
+ </Text>
47
+ </Dialog.Footer>
48
+ </Dialog.Content>
49
+ </Dialog>
86
50
  );
87
51
  }
88
52