@fragments-sdk/cli 0.2.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 (259) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/bin.d.ts +1 -0
  4. package/dist/bin.js +4783 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chunk-4FDQSGKX.js +786 -0
  7. package/dist/chunk-4FDQSGKX.js.map +1 -0
  8. package/dist/chunk-7H2MMGYG.js +369 -0
  9. package/dist/chunk-7H2MMGYG.js.map +1 -0
  10. package/dist/chunk-BSCG3IP7.js +619 -0
  11. package/dist/chunk-BSCG3IP7.js.map +1 -0
  12. package/dist/chunk-LY2CFFPY.js +898 -0
  13. package/dist/chunk-LY2CFFPY.js.map +1 -0
  14. package/dist/chunk-MUZ6CM66.js +6636 -0
  15. package/dist/chunk-MUZ6CM66.js.map +1 -0
  16. package/dist/chunk-OAENNG3G.js +1489 -0
  17. package/dist/chunk-OAENNG3G.js.map +1 -0
  18. package/dist/chunk-XHNKNI6J.js +235 -0
  19. package/dist/chunk-XHNKNI6J.js.map +1 -0
  20. package/dist/core-DWKLGY4N.js +68 -0
  21. package/dist/core-DWKLGY4N.js.map +1 -0
  22. package/dist/generate-4LQNJ7SX.js +249 -0
  23. package/dist/generate-4LQNJ7SX.js.map +1 -0
  24. package/dist/index.d.ts +775 -0
  25. package/dist/index.js +41 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/init-EMVI47QG.js +416 -0
  28. package/dist/init-EMVI47QG.js.map +1 -0
  29. package/dist/mcp-bin.d.ts +1 -0
  30. package/dist/mcp-bin.js +1117 -0
  31. package/dist/mcp-bin.js.map +1 -0
  32. package/dist/scan-4YPRF7FV.js +12 -0
  33. package/dist/scan-4YPRF7FV.js.map +1 -0
  34. package/dist/service-QSZMZJBJ.js +208 -0
  35. package/dist/service-QSZMZJBJ.js.map +1 -0
  36. package/dist/static-viewer-MIPGZ4Z7.js +12 -0
  37. package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
  38. package/dist/test-SQ5ZHXWU.js +1067 -0
  39. package/dist/test-SQ5ZHXWU.js.map +1 -0
  40. package/dist/tokens-HSGMYK64.js +173 -0
  41. package/dist/tokens-HSGMYK64.js.map +1 -0
  42. package/dist/viewer-YRF4SQE4.js +11101 -0
  43. package/dist/viewer-YRF4SQE4.js.map +1 -0
  44. package/package.json +107 -0
  45. package/src/ai.ts +266 -0
  46. package/src/analyze.ts +265 -0
  47. package/src/bin.ts +916 -0
  48. package/src/build.ts +248 -0
  49. package/src/commands/a11y.ts +302 -0
  50. package/src/commands/add.ts +313 -0
  51. package/src/commands/audit.ts +195 -0
  52. package/src/commands/baseline.ts +221 -0
  53. package/src/commands/build.ts +144 -0
  54. package/src/commands/compare.ts +337 -0
  55. package/src/commands/context.ts +107 -0
  56. package/src/commands/dev.ts +107 -0
  57. package/src/commands/enhance.ts +858 -0
  58. package/src/commands/generate.ts +391 -0
  59. package/src/commands/init.ts +531 -0
  60. package/src/commands/link/figma.ts +645 -0
  61. package/src/commands/link/index.ts +10 -0
  62. package/src/commands/link/storybook.ts +267 -0
  63. package/src/commands/list.ts +49 -0
  64. package/src/commands/metrics.ts +114 -0
  65. package/src/commands/reset.ts +242 -0
  66. package/src/commands/scan.ts +537 -0
  67. package/src/commands/storygen.ts +207 -0
  68. package/src/commands/tokens.ts +251 -0
  69. package/src/commands/validate.ts +93 -0
  70. package/src/commands/verify.ts +215 -0
  71. package/src/core/composition.test.ts +262 -0
  72. package/src/core/composition.ts +255 -0
  73. package/src/core/config.ts +84 -0
  74. package/src/core/constants.ts +111 -0
  75. package/src/core/context.ts +380 -0
  76. package/src/core/defineSegment.ts +137 -0
  77. package/src/core/discovery.ts +337 -0
  78. package/src/core/figma.ts +263 -0
  79. package/src/core/fragment-types.ts +214 -0
  80. package/src/core/generators/context.ts +389 -0
  81. package/src/core/generators/index.ts +23 -0
  82. package/src/core/generators/registry.ts +364 -0
  83. package/src/core/generators/typescript-extractor.ts +374 -0
  84. package/src/core/importAnalyzer.ts +217 -0
  85. package/src/core/index.ts +149 -0
  86. package/src/core/loader.ts +155 -0
  87. package/src/core/node.ts +63 -0
  88. package/src/core/parser.ts +551 -0
  89. package/src/core/previewLoader.ts +172 -0
  90. package/src/core/schema/fragment.schema.json +189 -0
  91. package/src/core/schema/registry.schema.json +137 -0
  92. package/src/core/schema.ts +182 -0
  93. package/src/core/storyAdapter.test.ts +571 -0
  94. package/src/core/storyAdapter.ts +761 -0
  95. package/src/core/token-types.ts +287 -0
  96. package/src/core/types.ts +754 -0
  97. package/src/diff.ts +323 -0
  98. package/src/index.ts +43 -0
  99. package/src/mcp/__tests__/projectFields.test.ts +130 -0
  100. package/src/mcp/bin.ts +36 -0
  101. package/src/mcp/index.ts +8 -0
  102. package/src/mcp/server.ts +1310 -0
  103. package/src/mcp/utils.ts +54 -0
  104. package/src/mcp-bin.ts +36 -0
  105. package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
  106. package/src/migrate/__tests__/args/args.test.ts +452 -0
  107. package/src/migrate/__tests__/meta/meta.test.ts +198 -0
  108. package/src/migrate/__tests__/stories/stories.test.ts +278 -0
  109. package/src/migrate/__tests__/utils/utils.test.ts +371 -0
  110. package/src/migrate/__tests__/values/values.test.ts +303 -0
  111. package/src/migrate/bin.ts +108 -0
  112. package/src/migrate/converter.ts +658 -0
  113. package/src/migrate/detect.ts +196 -0
  114. package/src/migrate/index.ts +45 -0
  115. package/src/migrate/migrate.ts +163 -0
  116. package/src/migrate/parser.ts +1136 -0
  117. package/src/migrate/report.ts +624 -0
  118. package/src/migrate/types.ts +169 -0
  119. package/src/screenshot.ts +249 -0
  120. package/src/service/__tests__/ast-utils.test.ts +426 -0
  121. package/src/service/__tests__/enhance-scanner.test.ts +200 -0
  122. package/src/service/__tests__/figma/figma.test.ts +652 -0
  123. package/src/service/__tests__/metrics-store.test.ts +409 -0
  124. package/src/service/__tests__/patch-generator.test.ts +186 -0
  125. package/src/service/__tests__/props-extractor.test.ts +365 -0
  126. package/src/service/__tests__/token-registry.test.ts +267 -0
  127. package/src/service/analytics.ts +659 -0
  128. package/src/service/ast-utils.ts +444 -0
  129. package/src/service/browser-pool.ts +339 -0
  130. package/src/service/capture.ts +267 -0
  131. package/src/service/diff.ts +279 -0
  132. package/src/service/enhance/aggregator.ts +489 -0
  133. package/src/service/enhance/cache.ts +275 -0
  134. package/src/service/enhance/codebase-scanner.ts +357 -0
  135. package/src/service/enhance/context-generator.ts +529 -0
  136. package/src/service/enhance/doc-extractor.ts +523 -0
  137. package/src/service/enhance/index.ts +131 -0
  138. package/src/service/enhance/props-extractor.ts +665 -0
  139. package/src/service/enhance/scanner.ts +445 -0
  140. package/src/service/enhance/storybook-parser.ts +552 -0
  141. package/src/service/enhance/types.ts +346 -0
  142. package/src/service/enhance/variant-renderer.ts +479 -0
  143. package/src/service/figma.ts +1008 -0
  144. package/src/service/index.ts +249 -0
  145. package/src/service/metrics-store.ts +333 -0
  146. package/src/service/patch-generator.ts +349 -0
  147. package/src/service/report.ts +854 -0
  148. package/src/service/storage.ts +401 -0
  149. package/src/service/token-fixes.ts +281 -0
  150. package/src/service/token-parser.ts +504 -0
  151. package/src/service/token-registry.ts +721 -0
  152. package/src/service/utils.ts +172 -0
  153. package/src/setup.ts +241 -0
  154. package/src/shared/command-wrapper.ts +81 -0
  155. package/src/shared/dev-server-client.ts +199 -0
  156. package/src/shared/index.ts +8 -0
  157. package/src/shared/segment-loader.ts +59 -0
  158. package/src/shared/types.ts +147 -0
  159. package/src/static-viewer.ts +715 -0
  160. package/src/test/discovery.ts +172 -0
  161. package/src/test/index.ts +281 -0
  162. package/src/test/reporters/console.ts +194 -0
  163. package/src/test/reporters/json.ts +190 -0
  164. package/src/test/reporters/junit.ts +186 -0
  165. package/src/test/runner.ts +598 -0
  166. package/src/test/types.ts +245 -0
  167. package/src/test/watch.ts +200 -0
  168. package/src/validators.ts +152 -0
  169. package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
  170. package/src/viewer/__tests__/render-utils.test.ts +232 -0
  171. package/src/viewer/__tests__/style-utils.test.ts +404 -0
  172. package/src/viewer/bin.ts +86 -0
  173. package/src/viewer/cli/health.ts +256 -0
  174. package/src/viewer/cli/index.ts +33 -0
  175. package/src/viewer/cli/scan.ts +124 -0
  176. package/src/viewer/cli/utils.ts +174 -0
  177. package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
  178. package/src/viewer/components/ActionCapture.tsx +172 -0
  179. package/src/viewer/components/ActionsPanel.tsx +371 -0
  180. package/src/viewer/components/App.tsx +638 -0
  181. package/src/viewer/components/BottomPanel.tsx +224 -0
  182. package/src/viewer/components/CodePanel.tsx +589 -0
  183. package/src/viewer/components/CommandPalette.tsx +336 -0
  184. package/src/viewer/components/ComponentGraph.tsx +394 -0
  185. package/src/viewer/components/ComponentHeader.tsx +85 -0
  186. package/src/viewer/components/ContractPanel.tsx +234 -0
  187. package/src/viewer/components/ErrorBoundary.tsx +85 -0
  188. package/src/viewer/components/FigmaEmbed.tsx +231 -0
  189. package/src/viewer/components/FragmentEditor.tsx +485 -0
  190. package/src/viewer/components/HealthDashboard.tsx +452 -0
  191. package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
  192. package/src/viewer/components/Icons.tsx +417 -0
  193. package/src/viewer/components/InteractionsPanel.tsx +720 -0
  194. package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
  195. package/src/viewer/components/IsolatedRender.tsx +111 -0
  196. package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
  197. package/src/viewer/components/LandingPage.tsx +441 -0
  198. package/src/viewer/components/Layout.tsx +22 -0
  199. package/src/viewer/components/LeftSidebar.tsx +391 -0
  200. package/src/viewer/components/MultiViewportPreview.tsx +429 -0
  201. package/src/viewer/components/PreviewArea.tsx +404 -0
  202. package/src/viewer/components/PreviewFrameHost.tsx +310 -0
  203. package/src/viewer/components/PreviewPane.tsx +150 -0
  204. package/src/viewer/components/PreviewToolbar.tsx +176 -0
  205. package/src/viewer/components/PropsEditor.tsx +512 -0
  206. package/src/viewer/components/PropsTable.tsx +98 -0
  207. package/src/viewer/components/RelationsSection.tsx +57 -0
  208. package/src/viewer/components/ResizablePanel.tsx +328 -0
  209. package/src/viewer/components/RightSidebar.tsx +118 -0
  210. package/src/viewer/components/ScreenshotButton.tsx +90 -0
  211. package/src/viewer/components/Sidebar.tsx +169 -0
  212. package/src/viewer/components/SkeletonLoader.tsx +156 -0
  213. package/src/viewer/components/StoryRenderer.tsx +128 -0
  214. package/src/viewer/components/ThemeProvider.tsx +96 -0
  215. package/src/viewer/components/Toast.tsx +67 -0
  216. package/src/viewer/components/TokenStylePanel.tsx +708 -0
  217. package/src/viewer/components/UsageSection.tsx +95 -0
  218. package/src/viewer/components/VariantMatrix.tsx +350 -0
  219. package/src/viewer/components/VariantRenderer.tsx +131 -0
  220. package/src/viewer/components/VariantTabs.tsx +84 -0
  221. package/src/viewer/components/ViewportSelector.tsx +165 -0
  222. package/src/viewer/components/_future/CreatePage.tsx +836 -0
  223. package/src/viewer/composition-renderer.ts +381 -0
  224. package/src/viewer/constants/index.ts +1 -0
  225. package/src/viewer/constants/ui.ts +185 -0
  226. package/src/viewer/entry.tsx +299 -0
  227. package/src/viewer/hooks/index.ts +2 -0
  228. package/src/viewer/hooks/useA11yCache.ts +383 -0
  229. package/src/viewer/hooks/useA11yService.ts +498 -0
  230. package/src/viewer/hooks/useActions.ts +138 -0
  231. package/src/viewer/hooks/useAppState.ts +124 -0
  232. package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
  233. package/src/viewer/hooks/useHmrStatus.ts +109 -0
  234. package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
  235. package/src/viewer/hooks/usePreviewBridge.ts +347 -0
  236. package/src/viewer/hooks/useScrollSpy.ts +78 -0
  237. package/src/viewer/hooks/useUrlState.ts +330 -0
  238. package/src/viewer/hooks/useViewSettings.ts +125 -0
  239. package/src/viewer/index.html +28 -0
  240. package/src/viewer/index.ts +14 -0
  241. package/src/viewer/intelligence/healthReport.ts +505 -0
  242. package/src/viewer/intelligence/styleDrift.ts +340 -0
  243. package/src/viewer/intelligence/usageScanner.ts +309 -0
  244. package/src/viewer/jsx-parser.ts +485 -0
  245. package/src/viewer/postcss.config.js +6 -0
  246. package/src/viewer/preview-frame-entry.tsx +25 -0
  247. package/src/viewer/preview-frame.html +109 -0
  248. package/src/viewer/render-template.html +68 -0
  249. package/src/viewer/render-utils.ts +170 -0
  250. package/src/viewer/server.ts +276 -0
  251. package/src/viewer/style-utils.ts +414 -0
  252. package/src/viewer/styles/globals.css +355 -0
  253. package/src/viewer/tailwind.config.js +37 -0
  254. package/src/viewer/types/a11y.ts +197 -0
  255. package/src/viewer/utils/a11y-fixes.ts +471 -0
  256. package/src/viewer/utils/actionExport.ts +372 -0
  257. package/src/viewer/utils/colorSchemes.ts +201 -0
  258. package/src/viewer/utils/detectRelationships.ts +256 -0
  259. package/src/viewer/vite-plugin.ts +2143 -0
@@ -0,0 +1,715 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import type { CompiledSegmentsFile } from "./core/index.js";
4
+ import { BRAND } from "./core/index.js";
5
+
6
+ /**
7
+ * Generate a static HTML viewer for the segments.json file.
8
+ * This viewer shows component documentation without needing to render actual components.
9
+ */
10
+ export function generateStaticViewer(data: CompiledSegmentsFile): string {
11
+ const segments = Object.values(data.segments);
12
+
13
+ // Group by category
14
+ const categories = new Map<string, typeof segments>();
15
+ for (const segment of segments) {
16
+ const category = segment.meta.category ?? "Uncategorized";
17
+ if (!categories.has(category)) {
18
+ categories.set(category, []);
19
+ }
20
+ categories.get(category)!.push(segment);
21
+ }
22
+
23
+ // Sort categories and components
24
+ const sortedCategories = Array.from(categories.entries()).sort((a, b) =>
25
+ a[0].localeCompare(b[0])
26
+ );
27
+
28
+ return `<!DOCTYPE html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="UTF-8">
32
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
33
+ <title>${BRAND.name} - Design System Viewer</title>
34
+ <link rel="preconnect" href="https://fonts.googleapis.com">
35
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
36
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
37
+ <style>
38
+ :root {
39
+ --bg-primary: #0a0a0f;
40
+ --bg-secondary: #12121a;
41
+ --bg-tertiary: #1a1a24;
42
+ --bg-hover: #22222e;
43
+ --border-color: #2a2a3a;
44
+ --text-primary: #f5f5f7;
45
+ --text-secondary: #a0a0b0;
46
+ --text-muted: #606070;
47
+ --accent: #6366f1;
48
+ --accent-hover: #818cf8;
49
+ --success: #22c55e;
50
+ --warning: #f59e0b;
51
+ --error: #ef4444;
52
+ --code-bg: #1e1e2e;
53
+ }
54
+
55
+ * {
56
+ margin: 0;
57
+ padding: 0;
58
+ box-sizing: border-box;
59
+ }
60
+
61
+ body {
62
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
63
+ background: var(--bg-primary);
64
+ color: var(--text-primary);
65
+ line-height: 1.6;
66
+ }
67
+
68
+ .layout {
69
+ display: grid;
70
+ grid-template-columns: 280px 1fr;
71
+ min-height: 100vh;
72
+ }
73
+
74
+ /* Sidebar */
75
+ .sidebar {
76
+ background: var(--bg-secondary);
77
+ border-right: 1px solid var(--border-color);
78
+ padding: 24px 0;
79
+ position: sticky;
80
+ top: 0;
81
+ height: 100vh;
82
+ overflow-y: auto;
83
+ }
84
+
85
+ .sidebar-header {
86
+ padding: 0 20px 20px;
87
+ border-bottom: 1px solid var(--border-color);
88
+ margin-bottom: 16px;
89
+ }
90
+
91
+ .sidebar-header h1 {
92
+ font-size: 20px;
93
+ font-weight: 700;
94
+ background: linear-gradient(135deg, var(--accent), #a855f7);
95
+ -webkit-background-clip: text;
96
+ -webkit-text-fill-color: transparent;
97
+ }
98
+
99
+ .sidebar-header .subtitle {
100
+ font-size: 12px;
101
+ color: var(--text-muted);
102
+ margin-top: 4px;
103
+ }
104
+
105
+ .sidebar-stats {
106
+ display: flex;
107
+ gap: 16px;
108
+ padding: 12px 20px;
109
+ background: var(--bg-tertiary);
110
+ margin: 0 12px 16px;
111
+ border-radius: 8px;
112
+ }
113
+
114
+ .stat {
115
+ text-align: center;
116
+ }
117
+
118
+ .stat-value {
119
+ font-size: 20px;
120
+ font-weight: 700;
121
+ color: var(--accent);
122
+ }
123
+
124
+ .stat-label {
125
+ font-size: 11px;
126
+ color: var(--text-muted);
127
+ text-transform: uppercase;
128
+ letter-spacing: 0.5px;
129
+ }
130
+
131
+ .category {
132
+ margin-bottom: 8px;
133
+ }
134
+
135
+ .category-header {
136
+ padding: 8px 20px;
137
+ font-size: 11px;
138
+ font-weight: 600;
139
+ text-transform: uppercase;
140
+ letter-spacing: 0.5px;
141
+ color: var(--text-muted);
142
+ }
143
+
144
+ .nav-item {
145
+ display: block;
146
+ padding: 8px 20px 8px 32px;
147
+ color: var(--text-secondary);
148
+ text-decoration: none;
149
+ font-size: 14px;
150
+ transition: all 0.15s;
151
+ }
152
+
153
+ .nav-item:hover {
154
+ background: var(--bg-hover);
155
+ color: var(--text-primary);
156
+ }
157
+
158
+ .nav-item.active {
159
+ background: var(--accent);
160
+ color: white;
161
+ }
162
+
163
+ /* Main Content */
164
+ .main {
165
+ padding: 40px 60px;
166
+ max-width: 1000px;
167
+ }
168
+
169
+ .component-section {
170
+ margin-bottom: 60px;
171
+ scroll-margin-top: 40px;
172
+ }
173
+
174
+ .component-header {
175
+ margin-bottom: 24px;
176
+ padding-bottom: 16px;
177
+ border-bottom: 1px solid var(--border-color);
178
+ }
179
+
180
+ .component-name {
181
+ font-size: 32px;
182
+ font-weight: 700;
183
+ margin-bottom: 8px;
184
+ }
185
+
186
+ .component-description {
187
+ font-size: 16px;
188
+ color: var(--text-secondary);
189
+ line-height: 1.7;
190
+ }
191
+
192
+ .component-meta {
193
+ display: flex;
194
+ gap: 16px;
195
+ margin-top: 16px;
196
+ }
197
+
198
+ .meta-badge {
199
+ display: inline-flex;
200
+ align-items: center;
201
+ gap: 6px;
202
+ padding: 4px 12px;
203
+ background: var(--bg-tertiary);
204
+ border-radius: 20px;
205
+ font-size: 12px;
206
+ color: var(--text-secondary);
207
+ }
208
+
209
+ .meta-badge.status-stable { color: var(--success); }
210
+ .meta-badge.status-beta { color: var(--warning); }
211
+ .meta-badge.status-deprecated { color: var(--error); }
212
+
213
+ /* Sections */
214
+ .section {
215
+ margin-bottom: 32px;
216
+ }
217
+
218
+ .section-title {
219
+ font-size: 18px;
220
+ font-weight: 600;
221
+ margin-bottom: 16px;
222
+ color: var(--text-primary);
223
+ }
224
+
225
+ /* Usage Guidelines */
226
+ .usage-list {
227
+ list-style: none;
228
+ }
229
+
230
+ .usage-item {
231
+ display: flex;
232
+ gap: 12px;
233
+ padding: 12px 16px;
234
+ background: var(--bg-secondary);
235
+ border-radius: 8px;
236
+ margin-bottom: 8px;
237
+ font-size: 14px;
238
+ }
239
+
240
+ .usage-item.when::before {
241
+ content: '✓';
242
+ color: var(--success);
243
+ font-weight: bold;
244
+ }
245
+
246
+ .usage-item.when-not::before {
247
+ content: '✗';
248
+ color: var(--error);
249
+ font-weight: bold;
250
+ }
251
+
252
+ /* Props Table */
253
+ .props-table {
254
+ width: 100%;
255
+ border-collapse: collapse;
256
+ font-size: 14px;
257
+ }
258
+
259
+ .props-table th {
260
+ text-align: left;
261
+ padding: 12px 16px;
262
+ background: var(--bg-tertiary);
263
+ font-weight: 600;
264
+ color: var(--text-secondary);
265
+ font-size: 12px;
266
+ text-transform: uppercase;
267
+ letter-spacing: 0.5px;
268
+ }
269
+
270
+ .props-table td {
271
+ padding: 12px 16px;
272
+ border-bottom: 1px solid var(--border-color);
273
+ vertical-align: top;
274
+ }
275
+
276
+ .props-table tr:hover td {
277
+ background: var(--bg-secondary);
278
+ }
279
+
280
+ .prop-name {
281
+ font-family: 'JetBrains Mono', monospace;
282
+ color: var(--accent);
283
+ }
284
+
285
+ .prop-type {
286
+ font-family: 'JetBrains Mono', monospace;
287
+ font-size: 13px;
288
+ color: var(--text-muted);
289
+ }
290
+
291
+ .prop-required {
292
+ color: var(--error);
293
+ font-size: 11px;
294
+ font-weight: 600;
295
+ }
296
+
297
+ .prop-default {
298
+ font-family: 'JetBrains Mono', monospace;
299
+ font-size: 13px;
300
+ color: var(--text-muted);
301
+ background: var(--code-bg);
302
+ padding: 2px 6px;
303
+ border-radius: 4px;
304
+ }
305
+
306
+ /* Variants */
307
+ .variants-grid {
308
+ display: grid;
309
+ gap: 16px;
310
+ }
311
+
312
+ .variant-card {
313
+ background: var(--bg-secondary);
314
+ border: 1px solid var(--border-color);
315
+ border-radius: 12px;
316
+ padding: 20px;
317
+ }
318
+
319
+ .variant-name {
320
+ font-weight: 600;
321
+ margin-bottom: 8px;
322
+ }
323
+
324
+ .variant-description {
325
+ font-size: 14px;
326
+ color: var(--text-secondary);
327
+ margin-bottom: 12px;
328
+ }
329
+
330
+ .variant-code {
331
+ font-family: 'JetBrains Mono', monospace;
332
+ font-size: 13px;
333
+ background: var(--code-bg);
334
+ padding: 12px 16px;
335
+ border-radius: 8px;
336
+ overflow-x: auto;
337
+ color: var(--text-secondary);
338
+ }
339
+
340
+ /* Relations */
341
+ .relations-list {
342
+ display: flex;
343
+ flex-wrap: wrap;
344
+ gap: 8px;
345
+ }
346
+
347
+ .relation-tag {
348
+ padding: 6px 14px;
349
+ background: var(--bg-tertiary);
350
+ border: 1px solid var(--border-color);
351
+ border-radius: 20px;
352
+ font-size: 13px;
353
+ color: var(--text-secondary);
354
+ text-decoration: none;
355
+ transition: all 0.15s;
356
+ }
357
+
358
+ .relation-tag:hover {
359
+ border-color: var(--accent);
360
+ color: var(--accent);
361
+ }
362
+
363
+ /* Search */
364
+ .search-container {
365
+ padding: 0 12px 16px;
366
+ }
367
+
368
+ .search-input {
369
+ width: 100%;
370
+ padding: 10px 14px;
371
+ background: var(--bg-tertiary);
372
+ border: 1px solid var(--border-color);
373
+ border-radius: 8px;
374
+ color: var(--text-primary);
375
+ font-size: 14px;
376
+ outline: none;
377
+ transition: border-color 0.15s;
378
+ }
379
+
380
+ .search-input:focus {
381
+ border-color: var(--accent);
382
+ }
383
+
384
+ .search-input::placeholder {
385
+ color: var(--text-muted);
386
+ }
387
+
388
+ /* Empty State */
389
+ .empty-state {
390
+ text-align: center;
391
+ padding: 80px 40px;
392
+ color: var(--text-muted);
393
+ }
394
+
395
+ .empty-state h2 {
396
+ font-size: 24px;
397
+ margin-bottom: 12px;
398
+ color: var(--text-secondary);
399
+ }
400
+
401
+ @media (max-width: 900px) {
402
+ .layout {
403
+ grid-template-columns: 1fr;
404
+ }
405
+
406
+ .sidebar {
407
+ position: relative;
408
+ height: auto;
409
+ border-right: none;
410
+ border-bottom: 1px solid var(--border-color);
411
+ }
412
+
413
+ .main {
414
+ padding: 24px;
415
+ }
416
+ }
417
+ </style>
418
+ </head>
419
+ <body>
420
+ <div class="layout">
421
+ <aside class="sidebar">
422
+ <div class="sidebar-header">
423
+ <h1>${BRAND.name}</h1>
424
+ <div class="subtitle">Design System Documentation</div>
425
+ </div>
426
+
427
+ <div class="sidebar-stats">
428
+ <div class="stat">
429
+ <div class="stat-value">${segments.length}</div>
430
+ <div class="stat-label">Components</div>
431
+ </div>
432
+ <div class="stat">
433
+ <div class="stat-value">${segments.reduce(
434
+ (sum, s) => sum + s.variants.length,
435
+ 0
436
+ )}</div>
437
+ <div class="stat-label">Variants</div>
438
+ </div>
439
+ </div>
440
+
441
+ <div class="search-container">
442
+ <input type="text" class="search-input" placeholder="Search components..." id="search">
443
+ </div>
444
+
445
+ <nav id="nav">
446
+ ${sortedCategories
447
+ .map(
448
+ ([category, items]) => `
449
+ <div class="category">
450
+ <div class="category-header">${category}</div>
451
+ ${items
452
+ .sort((a, b) => a.meta.name.localeCompare(b.meta.name))
453
+ .map(
454
+ (item) => `
455
+ <a href="#${item.meta.name}" class="nav-item">${item.meta.name}</a>
456
+ `
457
+ )
458
+ .join("")}
459
+ </div>
460
+ `
461
+ )
462
+ .join("")}
463
+ </nav>
464
+ </aside>
465
+
466
+ <main class="main" id="main">
467
+ ${
468
+ segments.length === 0
469
+ ? `
470
+ <div class="empty-state">
471
+ <h2>No Components Found</h2>
472
+ <p>Run <code>segments build</code> to compile your segment files.</p>
473
+ </div>
474
+ `
475
+ : segments
476
+ .sort((a, b) => a.meta.name.localeCompare(b.meta.name))
477
+ .map(
478
+ (segment) => `
479
+ <section class="component-section" id="${segment.meta.name}">
480
+ <div class="component-header">
481
+ <h2 class="component-name">${segment.meta.name}</h2>
482
+ <p class="component-description">${segment.meta.description}</p>
483
+ <div class="component-meta">
484
+ ${
485
+ segment.meta.status
486
+ ? `<span class="meta-badge status-${segment.meta.status}">${segment.meta.status}</span>`
487
+ : ""
488
+ }
489
+ ${
490
+ segment.meta.category
491
+ ? `<span class="meta-badge">${segment.meta.category}</span>`
492
+ : ""
493
+ }
494
+ ${
495
+ segment.meta.tags
496
+ ?.map((tag) => `<span class="meta-badge">${tag}</span>`)
497
+ .join("") ?? ""
498
+ }
499
+ </div>
500
+ </div>
501
+
502
+ ${
503
+ segment.usage
504
+ ? `
505
+ <div class="section">
506
+ <h3 class="section-title">Usage Guidelines</h3>
507
+ ${
508
+ segment.usage.when?.length
509
+ ? `
510
+ <ul class="usage-list">
511
+ ${segment.usage.when
512
+ .map((item) => `<li class="usage-item when">${item}</li>`)
513
+ .join("")}
514
+ </ul>
515
+ `
516
+ : ""
517
+ }
518
+ ${
519
+ segment.usage.whenNot?.length
520
+ ? `
521
+ <ul class="usage-list" style="margin-top: 12px;">
522
+ ${segment.usage.whenNot
523
+ .map(
524
+ (item) => `<li class="usage-item when-not">${item}</li>`
525
+ )
526
+ .join("")}
527
+ </ul>
528
+ `
529
+ : ""
530
+ }
531
+ </div>
532
+ `
533
+ : ""
534
+ }
535
+
536
+ ${
537
+ segment.props && Object.keys(segment.props).length
538
+ ? `
539
+ <div class="section">
540
+ <h3 class="section-title">Props</h3>
541
+ <table class="props-table">
542
+ <thead>
543
+ <tr>
544
+ <th>Name</th>
545
+ <th>Type</th>
546
+ <th>Default</th>
547
+ <th>Description</th>
548
+ </tr>
549
+ </thead>
550
+ <tbody>
551
+ ${Object.entries(segment.props)
552
+ .map(
553
+ ([name, prop]) => `
554
+ <tr>
555
+ <td>
556
+ <span class="prop-name">${name}</span>
557
+ ${
558
+ prop.required
559
+ ? '<span class="prop-required">*</span>'
560
+ : ""
561
+ }
562
+ </td>
563
+ <td><span class="prop-type">${prop.type}</span></td>
564
+ <td>${
565
+ prop.default !== undefined
566
+ ? `<span class="prop-default">${prop.default}</span>`
567
+ : "—"
568
+ }</td>
569
+ <td>${prop.description ?? ""}</td>
570
+ </tr>
571
+ `
572
+ )
573
+ .join("")}
574
+ </tbody>
575
+ </table>
576
+ </div>
577
+ `
578
+ : ""
579
+ }
580
+
581
+ ${
582
+ segment.variants?.length
583
+ ? `
584
+ <div class="section">
585
+ <h3 class="section-title">Variants</h3>
586
+ <div class="variants-grid">
587
+ ${segment.variants
588
+ .map(
589
+ (variant) => `
590
+ <div class="variant-card">
591
+ <div class="variant-name">${variant.name}</div>
592
+ ${
593
+ variant.description
594
+ ? `<div class="variant-description">${variant.description}</div>`
595
+ : ""
596
+ }
597
+ ${
598
+ variant.code
599
+ ? `<pre class="variant-code">${escapeHtml(
600
+ variant.code
601
+ )}</pre>`
602
+ : ""
603
+ }
604
+ </div>
605
+ `
606
+ )
607
+ .join("")}
608
+ </div>
609
+ </div>
610
+ `
611
+ : ""
612
+ }
613
+
614
+ ${
615
+ segment.relations?.length
616
+ ? `
617
+ <div class="section">
618
+ <h3 class="section-title">Related Components</h3>
619
+ <div class="relations-list">
620
+ ${segment.relations
621
+ .map(
622
+ (rel) =>
623
+ `<a href="#${
624
+ rel.component
625
+ }" class="relation-tag" title="${rel.note}">${
626
+ rel.relationship === "parent"
627
+ ? "↑"
628
+ : rel.relationship === "child"
629
+ ? "↓"
630
+ : "↔"
631
+ } ${rel.component}</a>`
632
+ )
633
+ .join("")}
634
+ </div>
635
+ </div>
636
+ `
637
+ : ""
638
+ }
639
+ </section>
640
+ `
641
+ )
642
+ .join("")
643
+ }
644
+ </main>
645
+ </div>
646
+
647
+ <script>
648
+ // Search functionality
649
+ const searchInput = document.getElementById('search');
650
+ const nav = document.getElementById('nav');
651
+ const main = document.getElementById('main');
652
+
653
+ searchInput.addEventListener('input', (e) => {
654
+ const query = e.target.value.toLowerCase();
655
+
656
+ // Filter nav items
657
+ nav.querySelectorAll('.nav-item').forEach(item => {
658
+ const name = item.textContent.toLowerCase();
659
+ item.style.display = name.includes(query) ? '' : 'none';
660
+ });
661
+
662
+ // Show/hide categories with no visible items
663
+ nav.querySelectorAll('.category').forEach(cat => {
664
+ const hasVisible = Array.from(cat.querySelectorAll('.nav-item')).some(
665
+ item => item.style.display !== 'none'
666
+ );
667
+ cat.style.display = hasVisible ? '' : 'none';
668
+ });
669
+
670
+ // Filter main sections
671
+ main.querySelectorAll('.component-section').forEach(section => {
672
+ const name = section.id.toLowerCase();
673
+ section.style.display = name.includes(query) ? '' : 'none';
674
+ });
675
+ });
676
+
677
+ // Highlight active nav item on scroll
678
+ const sections = document.querySelectorAll('.component-section');
679
+ const navItems = document.querySelectorAll('.nav-item');
680
+
681
+ const observer = new IntersectionObserver((entries) => {
682
+ entries.forEach(entry => {
683
+ if (entry.isIntersecting) {
684
+ navItems.forEach(item => {
685
+ item.classList.toggle('active', item.getAttribute('href') === '#' + entry.target.id);
686
+ });
687
+ }
688
+ });
689
+ }, { threshold: 0.3 });
690
+
691
+ sections.forEach(section => observer.observe(section));
692
+ </script>
693
+ </body>
694
+ </html>`;
695
+ }
696
+
697
+ function escapeHtml(str: string): string {
698
+ return str
699
+ .replace(/&/g, "&amp;")
700
+ .replace(/</g, "&lt;")
701
+ .replace(/>/g, "&gt;")
702
+ .replace(/"/g, "&quot;")
703
+ .replace(/'/g, "&#039;");
704
+ }
705
+
706
+ /**
707
+ * Load segments.json and generate static viewer
708
+ */
709
+ export async function generateViewerFromJson(
710
+ jsonPath: string
711
+ ): Promise<string> {
712
+ const content = await readFile(jsonPath, "utf-8");
713
+ const data = JSON.parse(content) as CompiledSegmentsFile;
714
+ return generateStaticViewer(data);
715
+ }