@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,132 +0,0 @@
1
- /**
2
- * Figma integration hook.
3
- * Handles fetching Figma styles and extracting rendered component styles.
4
- */
5
-
6
- import { useState, useCallback, useEffect } from 'react';
7
-
8
- interface FigmaStylesState {
9
- status: 'idle' | 'loading' | 'success' | 'error';
10
- styles?: Record<string, string>;
11
- error?: string;
12
- }
13
-
14
- interface UseFigmaIntegrationOptions {
15
- figmaUrl?: string;
16
- showComparison?: boolean;
17
- dependencies?: unknown[]; // Dependencies that should reset styles when changed
18
- }
19
-
20
- export function useFigmaIntegration(options: UseFigmaIntegrationOptions = {}) {
21
- const { figmaUrl, showComparison = false, dependencies = [] } = options;
22
-
23
- const [figmaStyles, setFigmaStyles] = useState<FigmaStylesState>({ status: 'idle' });
24
- const [renderedStyles, setRenderedStyles] = useState<Record<string, string> | null>(null);
25
-
26
- // Fetch Figma styles from API
27
- const fetchFigmaStyles = useCallback(async () => {
28
- if (!figmaUrl) return;
29
-
30
- setFigmaStyles({ status: 'loading' });
31
-
32
- try {
33
- const response = await fetch('/fragments/figma-styles', {
34
- method: 'POST',
35
- headers: { 'Content-Type': 'application/json' },
36
- body: JSON.stringify({ figmaUrl }),
37
- });
38
-
39
- const result = await response.json();
40
-
41
- if (result.error) {
42
- setFigmaStyles({ status: 'error', error: result.error });
43
- } else {
44
- setFigmaStyles({ status: 'success', styles: result.styles });
45
- }
46
- } catch {
47
- setFigmaStyles({ status: 'error', error: 'Failed to fetch Figma styles' });
48
- }
49
- }, [figmaUrl]);
50
-
51
- // Extract computed styles from rendered component
52
- const extractRenderedStyles = useCallback(() => {
53
- const container = document.querySelector('[data-preview-container="true"]');
54
- if (!container) return;
55
-
56
- const candidates = container.querySelectorAll('*');
57
- let bestElement: HTMLElement | null = null;
58
- let bestScore = -1;
59
-
60
- const isVisibleColor = (color: string | undefined): boolean => {
61
- if (!color) return false;
62
- if (color === 'transparent' || color === 'rgba(0, 0, 0, 0)') return false;
63
- return true;
64
- };
65
-
66
- for (const el of candidates) {
67
- const htmlEl = el as HTMLElement;
68
- const styles = window.getComputedStyle(htmlEl);
69
- let score = 0;
70
-
71
- if (isVisibleColor(styles.backgroundColor)) score += 10;
72
- if (styles.borderWidth && styles.borderWidth !== '0px') score += 3;
73
- if (styles.boxShadow && styles.boxShadow !== 'none') score += 3;
74
-
75
- const tagName = htmlEl.tagName.toLowerCase();
76
- if (['button', 'a', 'input', 'select', 'textarea'].includes(tagName)) score += 5;
77
- if (htmlEl.getAttribute('role') === 'button') score += 5;
78
-
79
- const rect = htmlEl.getBoundingClientRect();
80
- if (rect.width < 10 || rect.height < 10) score -= 10;
81
- if (rect.width > 500 || rect.height > 500) score -= 3;
82
-
83
- if (score > bestScore) {
84
- bestScore = score;
85
- bestElement = htmlEl;
86
- }
87
- }
88
-
89
- if (!bestElement) return;
90
-
91
- const styles = window.getComputedStyle(bestElement);
92
- const relevantProps = [
93
- 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius',
94
- 'fontFamily', 'fontSize', 'fontWeight', 'lineHeight', 'letterSpacing',
95
- 'textAlign', 'boxShadow', 'padding', 'gap', 'opacity'
96
- ];
97
-
98
- const result: Record<string, string> = {};
99
- for (const prop of relevantProps) {
100
- const cssKey = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
101
- const value = styles.getPropertyValue(cssKey);
102
- if (value) result[prop] = value;
103
- }
104
-
105
- setRenderedStyles(result);
106
- }, []);
107
-
108
- // Reset styles when dependencies change
109
- useEffect(() => {
110
- setFigmaStyles({ status: 'idle' });
111
- setRenderedStyles(null);
112
- }, dependencies);
113
-
114
- // Auto-fetch Figma styles when comparison is shown
115
- useEffect(() => {
116
- if (showComparison && figmaUrl && figmaStyles.status === 'idle') {
117
- fetchFigmaStyles();
118
- }
119
- }, [showComparison, figmaUrl, figmaStyles.status, fetchFigmaStyles]);
120
-
121
- return {
122
- figmaStyles,
123
- renderedStyles,
124
- fetchFigmaStyles,
125
- extractRenderedStyles,
126
- isLoading: figmaStyles.status === 'loading',
127
- hasError: figmaStyles.status === 'error',
128
- errorMessage: figmaStyles.error,
129
- };
130
- }
131
-
132
- export type { FigmaStylesState };
@@ -1,109 +0,0 @@
1
- import { useState, useEffect, useCallback } from 'react';
2
- import type { HmrStatus } from '../constants/ui.js';
3
-
4
- /**
5
- * Hook to track Vite HMR connection status.
6
- * Returns the current connection status and any recent file changes.
7
- */
8
- export function useHmrStatus() {
9
- const [status, setStatus] = useState<HmrStatus>('connected');
10
- const [lastUpdate, setLastUpdate] = useState<string | null>(null);
11
-
12
- useEffect(() => {
13
- // Check if we're in a Vite environment
14
- if (typeof import.meta.hot === 'undefined') {
15
- return;
16
- }
17
-
18
- const hot = import.meta.hot;
19
-
20
- // Listen for HMR connection status
21
- // Vite emits these events on the WebSocket connection
22
- const handleConnect = () => {
23
- setStatus('connected');
24
- };
25
-
26
- const handleDisconnect = () => {
27
- setStatus('disconnected');
28
- };
29
-
30
- const handleReconnecting = () => {
31
- setStatus('reconnecting');
32
- };
33
-
34
- // Listen for module updates
35
- const handleUpdate = (data: { type: string; path?: string }) => {
36
- if (data.path) {
37
- setLastUpdate(data.path);
38
- // Clear the update notification after 3 seconds
39
- setTimeout(() => setLastUpdate(null), 3000);
40
- }
41
- };
42
-
43
- // Vite's HMR API
44
- // @ts-expect-error Vite internal events
45
- hot.on('vite:beforeUpdate', handleUpdate);
46
-
47
- // Listen for WebSocket events via custom events
48
- // These are dispatched by Vite's client
49
- window.addEventListener('vite:ws-connect', handleConnect);
50
- window.addEventListener('vite:ws-disconnect', handleDisconnect);
51
-
52
- // For Vite 5+, we can use the connection status directly
53
- // Check current status
54
- try {
55
- // @ts-expect-error Vite internal
56
- if (hot.connection?.socket?.readyState === 1) {
57
- setStatus('connected');
58
- }
59
- } catch {
60
- // Ignore - may not be available
61
- }
62
-
63
- return () => {
64
- window.removeEventListener('vite:ws-connect', handleConnect);
65
- window.removeEventListener('vite:ws-disconnect', handleDisconnect);
66
- };
67
- }, []);
68
-
69
- // Manual check for connection status every 5 seconds
70
- useEffect(() => {
71
- const checkConnection = () => {
72
- if (typeof import.meta.hot === 'undefined') {
73
- setStatus('disconnected');
74
- return;
75
- }
76
-
77
- try {
78
- // @ts-expect-error Vite internal
79
- const socket = import.meta.hot.connection?.socket;
80
- if (socket) {
81
- switch (socket.readyState) {
82
- case 0: // CONNECTING
83
- setStatus('reconnecting');
84
- break;
85
- case 1: // OPEN
86
- setStatus('connected');
87
- break;
88
- case 2: // CLOSING
89
- case 3: // CLOSED
90
- setStatus('disconnected');
91
- break;
92
- }
93
- }
94
- } catch {
95
- // Ignore errors - HMR may not be available
96
- }
97
- };
98
-
99
- // Initial check
100
- checkConnection();
101
-
102
- // Periodic check
103
- const interval = setInterval(checkConnection, 5000);
104
-
105
- return () => clearInterval(interval);
106
- }, []);
107
-
108
- return { status, lastUpdate };
109
- }
@@ -1,270 +0,0 @@
1
- /**
2
- * Keyboard Shortcuts Hook
3
- *
4
- * Provides global keyboard navigation for the viewer:
5
- * - ⌘↓/⌘↑: Navigate components
6
- * - ⌘←/⌘→: Navigate variants
7
- * - j/k or ↓/↑: Navigate components (legacy)
8
- * - [/] or ←/→: Navigate variants (legacy)
9
- * - 1-9: Jump to variant by number
10
- * - t: Toggle preview theme
11
- * - p: Toggle panel
12
- * - m: Toggle matrix view
13
- * - r: Toggle responsive view
14
- * - cmd+shift+c: Copy link
15
- * - ?: Show shortcuts help
16
- * - Escape: Close modals/clear search
17
- */
18
-
19
- import { useEffect, useCallback } from "react";
20
-
21
- export interface ShortcutActions {
22
- /** Navigate to next component */
23
- nextComponent: () => void;
24
- /** Navigate to previous component */
25
- prevComponent: () => void;
26
- /** Navigate to next variant */
27
- nextVariant: () => void;
28
- /** Navigate to previous variant */
29
- prevVariant: () => void;
30
- /** Jump to variant by index (0-based) */
31
- goToVariant: (index: number) => void;
32
- /** Toggle preview theme */
33
- toggleTheme: () => void;
34
- /** Toggle panel open/closed */
35
- togglePanel: () => void;
36
- /** Toggle matrix view */
37
- toggleMatrix: () => void;
38
- /** Toggle responsive/multi-viewport view */
39
- toggleResponsive: () => void;
40
- /** Copy shareable link */
41
- copyLink: () => void;
42
- /** Show shortcuts help */
43
- showHelp: () => void;
44
- /** Open search/command palette */
45
- openSearch: () => void;
46
- /** Close any open modal/dialog */
47
- escape: () => void;
48
- }
49
-
50
- export interface ShortcutConfig {
51
- /** Whether shortcuts are enabled */
52
- enabled?: boolean;
53
- /** Number of variants available */
54
- variantCount?: number;
55
- }
56
-
57
- /**
58
- * Check if an element is an input that should capture keyboard events
59
- */
60
- function isInputElement(element: EventTarget | null): boolean {
61
- if (!element || !(element instanceof HTMLElement)) return false;
62
-
63
- const tagName = element.tagName.toLowerCase();
64
- if (tagName === "input" || tagName === "textarea" || tagName === "select") {
65
- return true;
66
- }
67
-
68
- if (element.isContentEditable) {
69
- return true;
70
- }
71
-
72
- // Check if inside Monaco Editor
73
- if (element.closest('.monaco-editor')) {
74
- return true;
75
- }
76
-
77
- return false;
78
- }
79
-
80
- /**
81
- * Hook to register keyboard shortcuts
82
- */
83
- export function useKeyboardShortcuts(
84
- actions: Partial<ShortcutActions>,
85
- config: ShortcutConfig = {}
86
- ) {
87
- const { enabled = true, variantCount = 0 } = config;
88
-
89
- const handleKeyDown = useCallback(
90
- (event: KeyboardEvent) => {
91
- if (!enabled) return;
92
-
93
- // Don't capture events from input elements (except for specific shortcuts)
94
- const isInput = isInputElement(event.target);
95
- const isMeta = event.metaKey || event.ctrlKey;
96
-
97
- // Global shortcuts that work even in inputs
98
- if (event.key === "Escape") {
99
- actions.escape?.();
100
- return;
101
- }
102
-
103
- // cmd+shift+c: Copy link (works everywhere)
104
- if (isMeta && event.shiftKey && event.key === "c") {
105
- event.preventDefault();
106
- actions.copyLink?.();
107
- return;
108
- }
109
-
110
- // cmd+k: Open search/command palette (works everywhere)
111
- if (isMeta && event.key === "k") {
112
- event.preventDefault();
113
- actions.openSearch?.();
114
- return;
115
- }
116
-
117
- // cmd+arrow: Navigation (works everywhere)
118
- if (isMeta && event.key === "ArrowDown") {
119
- event.preventDefault();
120
- actions.nextComponent?.();
121
- return;
122
- }
123
- if (isMeta && event.key === "ArrowUp") {
124
- event.preventDefault();
125
- actions.prevComponent?.();
126
- return;
127
- }
128
- if (isMeta && event.key === "ArrowRight") {
129
- event.preventDefault();
130
- actions.nextVariant?.();
131
- return;
132
- }
133
- if (isMeta && event.key === "ArrowLeft") {
134
- event.preventDefault();
135
- actions.prevVariant?.();
136
- return;
137
- }
138
-
139
- // Skip other shortcuts if in input
140
- if (isInput) return;
141
-
142
- // "/" also opens search (when not in input)
143
- if (event.key === "/") {
144
- event.preventDefault();
145
- actions.openSearch?.();
146
- return;
147
- }
148
-
149
- // Navigation shortcuts
150
- switch (event.key) {
151
- // Component navigation
152
- case "j":
153
- case "ArrowDown":
154
- if (!isMeta && !event.altKey) {
155
- event.preventDefault();
156
- actions.nextComponent?.();
157
- }
158
- break;
159
-
160
- case "k":
161
- case "ArrowUp":
162
- if (!isMeta && !event.altKey) {
163
- event.preventDefault();
164
- actions.prevComponent?.();
165
- }
166
- break;
167
-
168
- // Variant navigation
169
- case "[":
170
- case "ArrowLeft":
171
- if (!isMeta && !event.altKey) {
172
- event.preventDefault();
173
- actions.prevVariant?.();
174
- }
175
- break;
176
-
177
- case "]":
178
- case "ArrowRight":
179
- if (!isMeta && !event.altKey) {
180
- event.preventDefault();
181
- actions.nextVariant?.();
182
- }
183
- break;
184
-
185
- // Number keys 1-9 for variant selection
186
- case "1":
187
- case "2":
188
- case "3":
189
- case "4":
190
- case "5":
191
- case "6":
192
- case "7":
193
- case "8":
194
- case "9":
195
- if (!isMeta && !event.altKey) {
196
- const index = parseInt(event.key, 10) - 1;
197
- if (index < variantCount) {
198
- event.preventDefault();
199
- actions.goToVariant?.(index);
200
- }
201
- }
202
- break;
203
-
204
- // Theme toggle
205
- case "t":
206
- if (!isMeta && !event.altKey) {
207
- event.preventDefault();
208
- actions.toggleTheme?.();
209
- }
210
- break;
211
-
212
- // Panel toggle
213
- case "p":
214
- if (!isMeta && !event.altKey) {
215
- event.preventDefault();
216
- actions.togglePanel?.();
217
- }
218
- break;
219
-
220
- // Matrix view toggle
221
- case "m":
222
- if (!isMeta && !event.altKey) {
223
- event.preventDefault();
224
- actions.toggleMatrix?.();
225
- }
226
- break;
227
-
228
- // Responsive view toggle
229
- case "v":
230
- if (!isMeta && !event.altKey) {
231
- event.preventDefault();
232
- actions.toggleResponsive?.();
233
- }
234
- break;
235
-
236
- // Help
237
- case "?":
238
- event.preventDefault();
239
- actions.showHelp?.();
240
- break;
241
- }
242
- },
243
- [enabled, actions, variantCount]
244
- );
245
-
246
- useEffect(() => {
247
- document.addEventListener("keydown", handleKeyDown);
248
- return () => document.removeEventListener("keydown", handleKeyDown);
249
- }, [handleKeyDown]);
250
- }
251
-
252
- /**
253
- * Keyboard shortcuts data for help display
254
- */
255
- export const SHORTCUTS = [
256
- { keys: ["⌘↓"], description: "Next component" },
257
- { keys: ["⌘↑"], description: "Previous component" },
258
- { keys: ["⌘→"], description: "Next variant" },
259
- { keys: ["⌘←"], description: "Previous variant" },
260
- { keys: ["1-9"], description: "Go to variant" },
261
- { keys: ["t"], description: "Toggle preview theme" },
262
- { keys: ["p"], description: "Toggle addons panel" },
263
- { keys: ["m"], description: "Matrix view" },
264
- { keys: ["v"], description: "Responsive view" },
265
- { keys: ["/", "⌘K"], description: "Command palette" },
266
- { keys: ["?"], description: "Show shortcuts" },
267
- { keys: ["Esc"], description: "Close / Clear" },
268
- ];
269
-
270
- export default useKeyboardShortcuts;