@layoutdesign/context 0.1.7

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 (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +424 -0
  3. package/dist/bin/cli.d.ts +3 -0
  4. package/dist/bin/cli.d.ts.map +1 -0
  5. package/dist/bin/cli.js +57 -0
  6. package/dist/bin/cli.js.map +1 -0
  7. package/dist/src/cli/import-zip.d.ts +2 -0
  8. package/dist/src/cli/import-zip.d.ts.map +1 -0
  9. package/dist/src/cli/import-zip.js +156 -0
  10. package/dist/src/cli/import-zip.js.map +1 -0
  11. package/dist/src/cli/init.d.ts +4 -0
  12. package/dist/src/cli/init.d.ts.map +1 -0
  13. package/dist/src/cli/init.js +104 -0
  14. package/dist/src/cli/init.js.map +1 -0
  15. package/dist/src/cli/install.d.ts +5 -0
  16. package/dist/src/cli/install.d.ts.map +1 -0
  17. package/dist/src/cli/install.js +192 -0
  18. package/dist/src/cli/install.js.map +1 -0
  19. package/dist/src/cli/list.d.ts +2 -0
  20. package/dist/src/cli/list.d.ts.map +1 -0
  21. package/dist/src/cli/list.js +36 -0
  22. package/dist/src/cli/list.js.map +1 -0
  23. package/dist/src/cli/serve.d.ts +2 -0
  24. package/dist/src/cli/serve.d.ts.map +1 -0
  25. package/dist/src/cli/serve.js +9 -0
  26. package/dist/src/cli/serve.js.map +1 -0
  27. package/dist/src/cli/use.d.ts +2 -0
  28. package/dist/src/cli/use.d.ts.map +1 -0
  29. package/dist/src/cli/use.js +54 -0
  30. package/dist/src/cli/use.js.map +1 -0
  31. package/dist/src/compliance/checker.d.ts +23 -0
  32. package/dist/src/compliance/checker.d.ts.map +1 -0
  33. package/dist/src/compliance/checker.js +31 -0
  34. package/dist/src/compliance/checker.js.map +1 -0
  35. package/dist/src/compliance/rules.d.ts +11 -0
  36. package/dist/src/compliance/rules.d.ts.map +1 -0
  37. package/dist/src/compliance/rules.js +147 -0
  38. package/dist/src/compliance/rules.js.map +1 -0
  39. package/dist/src/index.d.ts +9 -0
  40. package/dist/src/index.d.ts.map +1 -0
  41. package/dist/src/index.js +6 -0
  42. package/dist/src/index.js.map +1 -0
  43. package/dist/src/kit/loader.d.ts +16 -0
  44. package/dist/src/kit/loader.d.ts.map +1 -0
  45. package/dist/src/kit/loader.js +98 -0
  46. package/dist/src/kit/loader.js.map +1 -0
  47. package/dist/src/kit/parser.d.ts +21 -0
  48. package/dist/src/kit/parser.d.ts.map +1 -0
  49. package/dist/src/kit/parser.js +98 -0
  50. package/dist/src/kit/parser.js.map +1 -0
  51. package/dist/src/kit/registry.d.ts +4 -0
  52. package/dist/src/kit/registry.d.ts.map +1 -0
  53. package/dist/src/kit/registry.js +91 -0
  54. package/dist/src/kit/registry.js.map +1 -0
  55. package/dist/src/kit/types.d.ts +51 -0
  56. package/dist/src/kit/types.d.ts.map +1 -0
  57. package/dist/src/kit/types.js +11 -0
  58. package/dist/src/kit/types.js.map +1 -0
  59. package/dist/src/mcp/server.d.ts +6 -0
  60. package/dist/src/mcp/server.d.ts.map +1 -0
  61. package/dist/src/mcp/server.js +56 -0
  62. package/dist/src/mcp/server.js.map +1 -0
  63. package/dist/src/mcp/tools/check-compliance.d.ts +16 -0
  64. package/dist/src/mcp/tools/check-compliance.d.ts.map +1 -0
  65. package/dist/src/mcp/tools/check-compliance.js +44 -0
  66. package/dist/src/mcp/tools/check-compliance.js.map +1 -0
  67. package/dist/src/mcp/tools/design-in-figma.d.ts +24 -0
  68. package/dist/src/mcp/tools/design-in-figma.d.ts.map +1 -0
  69. package/dist/src/mcp/tools/design-in-figma.js +202 -0
  70. package/dist/src/mcp/tools/design-in-figma.js.map +1 -0
  71. package/dist/src/mcp/tools/get-component.d.ts +16 -0
  72. package/dist/src/mcp/tools/get-component.d.ts.map +1 -0
  73. package/dist/src/mcp/tools/get-component.js +52 -0
  74. package/dist/src/mcp/tools/get-component.js.map +1 -0
  75. package/dist/src/mcp/tools/get-design-system.d.ts +16 -0
  76. package/dist/src/mcp/tools/get-design-system.d.ts.map +1 -0
  77. package/dist/src/mcp/tools/get-design-system.js +51 -0
  78. package/dist/src/mcp/tools/get-design-system.js.map +1 -0
  79. package/dist/src/mcp/tools/get-screenshots.d.ts +23 -0
  80. package/dist/src/mcp/tools/get-screenshots.d.ts.map +1 -0
  81. package/dist/src/mcp/tools/get-screenshots.js +78 -0
  82. package/dist/src/mcp/tools/get-screenshots.js.map +1 -0
  83. package/dist/src/mcp/tools/get-tokens.d.ts +20 -0
  84. package/dist/src/mcp/tools/get-tokens.d.ts.map +1 -0
  85. package/dist/src/mcp/tools/get-tokens.js +50 -0
  86. package/dist/src/mcp/tools/get-tokens.js.map +1 -0
  87. package/dist/src/mcp/tools/list-components.d.ts +11 -0
  88. package/dist/src/mcp/tools/list-components.d.ts.map +1 -0
  89. package/dist/src/mcp/tools/list-components.js +38 -0
  90. package/dist/src/mcp/tools/list-components.js.map +1 -0
  91. package/dist/src/mcp/tools/preview.d.ts +21 -0
  92. package/dist/src/mcp/tools/preview.d.ts.map +1 -0
  93. package/dist/src/mcp/tools/preview.js +63 -0
  94. package/dist/src/mcp/tools/preview.js.map +1 -0
  95. package/dist/src/mcp/tools/push-to-figma.d.ts +24 -0
  96. package/dist/src/mcp/tools/push-to-figma.d.ts.map +1 -0
  97. package/dist/src/mcp/tools/push-to-figma.js +101 -0
  98. package/dist/src/mcp/tools/push-to-figma.js.map +1 -0
  99. package/dist/src/mcp/tools/update-tokens.d.ts +21 -0
  100. package/dist/src/mcp/tools/update-tokens.d.ts.map +1 -0
  101. package/dist/src/mcp/tools/update-tokens.js +187 -0
  102. package/dist/src/mcp/tools/update-tokens.js.map +1 -0
  103. package/dist/src/mcp/tools/url-to-figma.d.ts +29 -0
  104. package/dist/src/mcp/tools/url-to-figma.d.ts.map +1 -0
  105. package/dist/src/mcp/tools/url-to-figma.js +103 -0
  106. package/dist/src/mcp/tools/url-to-figma.js.map +1 -0
  107. package/dist/src/preview/server.d.ts +15 -0
  108. package/dist/src/preview/server.d.ts.map +1 -0
  109. package/dist/src/preview/server.js +146 -0
  110. package/dist/src/preview/server.js.map +1 -0
  111. package/dist/src/preview/static/index.html +493 -0
  112. package/dist/src/preview/transpile.d.ts +10 -0
  113. package/dist/src/preview/transpile.d.ts.map +1 -0
  114. package/dist/src/preview/transpile.js +40 -0
  115. package/dist/src/preview/transpile.js.map +1 -0
  116. package/dist/src/preview/ws.d.ts +17 -0
  117. package/dist/src/preview/ws.d.ts.map +1 -0
  118. package/dist/src/preview/ws.js +66 -0
  119. package/dist/src/preview/ws.js.map +1 -0
  120. package/kits/linear-lite/DESIGN.md +421 -0
  121. package/kits/linear-lite/kit.json +12 -0
  122. package/kits/linear-lite/tokens.css +46 -0
  123. package/kits/linear-lite/tokens.json +47 -0
  124. package/kits/notion-lite/DESIGN.md +528 -0
  125. package/kits/notion-lite/kit.json +12 -0
  126. package/kits/notion-lite/tokens.css +50 -0
  127. package/kits/notion-lite/tokens.json +51 -0
  128. package/kits/stripe-lite/DESIGN.md +539 -0
  129. package/kits/stripe-lite/kit.json +12 -0
  130. package/kits/stripe-lite/tokens.css +57 -0
  131. package/kits/stripe-lite/tokens.json +58 -0
  132. package/package.json +63 -0
@@ -0,0 +1,493 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Layout Preview</title>
7
+ <script src="https://mcp.figma.com/mcp/html-to-design/capture.js" async></script>
8
+ <style>
9
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
10
+
11
+ :root {
12
+ --bg-app: #0C0C0E;
13
+ --bg-panel: #111115;
14
+ --bg-surface: #17171C;
15
+ --bg-elevated: #1E1E24;
16
+ --bg-hover: #24242B;
17
+ --border: rgba(255,255,255,0.07);
18
+ --border-strong: rgba(255,255,255,0.14);
19
+ --accent: #6366F1;
20
+ --accent-hover: #7577F3;
21
+ --text-primary: #E8E8F0;
22
+ --text-secondary: rgba(232,232,240,0.55);
23
+ --text-muted: rgba(232,232,240,0.35);
24
+ --ease: cubic-bezier(0.0, 0.0, 0.2, 1);
25
+ }
26
+
27
+ body {
28
+ background: var(--bg-app);
29
+ color: var(--text-primary);
30
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
31
+ height: 100vh;
32
+ display: flex;
33
+ flex-direction: column;
34
+ overflow: hidden;
35
+ }
36
+
37
+ /* Toolbar */
38
+ .toolbar {
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 8px;
42
+ padding: 8px 16px;
43
+ background: var(--bg-panel);
44
+ border-bottom: 1px solid var(--border);
45
+ flex-shrink: 0;
46
+ }
47
+
48
+ .toolbar-group {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 2px;
52
+ background: var(--bg-surface);
53
+ border-radius: 6px;
54
+ padding: 2px;
55
+ }
56
+
57
+ .toolbar-btn {
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ gap: 6px;
62
+ padding: 6px 12px;
63
+ border: none;
64
+ border-radius: 4px;
65
+ background: transparent;
66
+ color: var(--text-secondary);
67
+ font-size: 12px;
68
+ font-weight: 500;
69
+ cursor: pointer;
70
+ transition: all 150ms var(--ease);
71
+ white-space: nowrap;
72
+ }
73
+
74
+ .toolbar-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
75
+ .toolbar-btn.active { background: var(--accent); color: white; }
76
+
77
+ .toolbar-spacer { flex: 1; }
78
+
79
+ .status {
80
+ display: flex;
81
+ align-items: center;
82
+ gap: 6px;
83
+ font-size: 11px;
84
+ color: var(--text-muted);
85
+ }
86
+
87
+ .status-dot {
88
+ width: 6px;
89
+ height: 6px;
90
+ border-radius: 50%;
91
+ background: #ef4444;
92
+ transition: background 300ms var(--ease);
93
+ }
94
+
95
+ .status-dot.connected { background: #22c55e; }
96
+
97
+ /* Main content */
98
+ .main {
99
+ display: flex;
100
+ flex: 1;
101
+ overflow: hidden;
102
+ }
103
+
104
+ /* Preview area */
105
+ .preview-area {
106
+ flex: 1;
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: center;
110
+ padding: 24px;
111
+ overflow: auto;
112
+ background: var(--bg-app);
113
+ }
114
+
115
+ .preview-container {
116
+ background: white;
117
+ border-radius: 8px;
118
+ overflow: hidden;
119
+ box-sizing: border-box;
120
+ border: 1px solid var(--border-strong);
121
+ transition: width 300ms var(--ease);
122
+ height: 100%;
123
+ display: flex;
124
+ }
125
+
126
+ .preview-container.dark-content {
127
+ background: #1a1a2e;
128
+ }
129
+
130
+ .preview-container[data-viewport="mobile"] { width: 375px; }
131
+ .preview-container[data-viewport="tablet"] { width: 768px; }
132
+ .preview-container[data-viewport="desktop"] { width: 100%; max-width: 1280px; }
133
+
134
+ .preview-iframe {
135
+ width: 100%;
136
+ height: 100%;
137
+ border: none;
138
+ }
139
+
140
+ /* Source panel */
141
+ .source-panel {
142
+ width: 400px;
143
+ background: var(--bg-panel);
144
+ border-left: 1px solid var(--border);
145
+ display: flex;
146
+ flex-direction: column;
147
+ flex-shrink: 0;
148
+ transition: width 200ms var(--ease);
149
+ overflow: hidden;
150
+ }
151
+
152
+ .source-panel.collapsed { width: 0; }
153
+
154
+ .source-header {
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: space-between;
158
+ padding: 10px 16px;
159
+ border-bottom: 1px solid var(--border);
160
+ font-size: 12px;
161
+ font-weight: 600;
162
+ color: var(--text-secondary);
163
+ text-transform: uppercase;
164
+ letter-spacing: 0.05em;
165
+ }
166
+
167
+ .source-close {
168
+ background: none;
169
+ border: none;
170
+ color: var(--text-muted);
171
+ cursor: pointer;
172
+ font-size: 16px;
173
+ padding: 2px 6px;
174
+ border-radius: 4px;
175
+ transition: all 150ms var(--ease);
176
+ }
177
+
178
+ .source-close:hover { color: var(--text-primary); background: var(--bg-hover); }
179
+
180
+ .source-code {
181
+ flex: 1;
182
+ overflow: auto;
183
+ padding: 16px;
184
+ font-family: 'SF Mono', 'Fira Code', 'JetBrains Mono', 'Cascadia Code', monospace;
185
+ font-size: 12px;
186
+ line-height: 1.6;
187
+ color: var(--text-primary);
188
+ white-space: pre;
189
+ tab-size: 2;
190
+ }
191
+
192
+ /* Empty state */
193
+ .empty-state {
194
+ display: flex;
195
+ flex-direction: column;
196
+ align-items: center;
197
+ justify-content: center;
198
+ height: 100%;
199
+ gap: 12px;
200
+ color: var(--text-muted);
201
+ font-size: 14px;
202
+ }
203
+
204
+ .empty-state svg {
205
+ width: 48px;
206
+ height: 48px;
207
+ opacity: 0.3;
208
+ }
209
+
210
+ /* Branding */
211
+ .branding {
212
+ position: fixed;
213
+ bottom: 12px;
214
+ left: 16px;
215
+ font-size: 11px;
216
+ color: var(--text-muted);
217
+ letter-spacing: 0.04em;
218
+ user-select: none;
219
+ z-index: 10;
220
+ }
221
+
222
+ /* Scrollbar */
223
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
224
+ ::-webkit-scrollbar-track { background: transparent; }
225
+ ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 3px; }
226
+ ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.2); }
227
+
228
+ /* Copy toast */
229
+ .toast {
230
+ position: fixed;
231
+ bottom: 24px;
232
+ right: 24px;
233
+ background: var(--bg-elevated);
234
+ border: 1px solid var(--border-strong);
235
+ color: var(--text-primary);
236
+ padding: 8px 16px;
237
+ border-radius: 6px;
238
+ font-size: 12px;
239
+ opacity: 0;
240
+ transform: translateY(8px);
241
+ transition: all 200ms var(--ease);
242
+ pointer-events: none;
243
+ z-index: 100;
244
+ }
245
+
246
+ .toast.show {
247
+ opacity: 1;
248
+ transform: translateY(0);
249
+ }
250
+ </style>
251
+ </head>
252
+ <body>
253
+ <!-- Toolbar -->
254
+ <div class="toolbar">
255
+ <div class="toolbar-group">
256
+ <button class="toolbar-btn active" data-viewport="desktop" onclick="setViewport('desktop')">
257
+ Desktop
258
+ </button>
259
+ <button class="toolbar-btn" data-viewport="tablet" onclick="setViewport('tablet')">
260
+ Tablet
261
+ </button>
262
+ <button class="toolbar-btn" data-viewport="mobile" onclick="setViewport('mobile')">
263
+ Mobile
264
+ </button>
265
+ </div>
266
+
267
+ <div class="toolbar-group">
268
+ <button class="toolbar-btn active" id="lightModeBtn" onclick="setContentMode('light')">
269
+ Light
270
+ </button>
271
+ <button class="toolbar-btn" id="darkModeBtn" onclick="setContentMode('dark')">
272
+ Dark
273
+ </button>
274
+ </div>
275
+
276
+ <div class="toolbar-spacer"></div>
277
+
278
+ <div class="status">
279
+ <span class="status-dot" id="statusDot"></span>
280
+ <span id="statusText">Connecting...</span>
281
+ </div>
282
+
283
+ <button class="toolbar-btn" id="sourceToggleBtn" onclick="toggleSource()">
284
+ Source
285
+ </button>
286
+
287
+ <button class="toolbar-btn" onclick="copyCode()">
288
+ Copy Code
289
+ </button>
290
+ </div>
291
+
292
+ <!-- Main -->
293
+ <div class="main">
294
+ <div class="preview-area">
295
+ <div class="preview-container" id="previewContainer" data-viewport="desktop">
296
+ <div class="empty-state" id="emptyState">
297
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
298
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
299
+ </svg>
300
+ <span>Waiting for component...</span>
301
+ </div>
302
+ <iframe class="preview-iframe" id="previewIframe" style="display:none;"></iframe>
303
+ </div>
304
+ </div>
305
+
306
+ <div class="source-panel" id="sourcePanel">
307
+ <div class="source-header">
308
+ <span>Source</span>
309
+ <button class="source-close" onclick="toggleSource()">&times;</button>
310
+ </div>
311
+ <pre class="source-code" id="sourceCode">No code received yet.</pre>
312
+ </div>
313
+ </div>
314
+
315
+ <div class="branding">Layout</div>
316
+ <div class="toast" id="toast">Copied to clipboard</div>
317
+
318
+ <script>
319
+ // State
320
+ let currentCode = '';
321
+ let currentCompiledJs = '';
322
+ let currentViewport = 'desktop';
323
+ let contentDarkMode = false;
324
+ let sourceVisible = true;
325
+
326
+ // Elements
327
+ const previewContainer = document.getElementById('previewContainer');
328
+ const previewIframe = document.getElementById('previewIframe');
329
+ const emptyState = document.getElementById('emptyState');
330
+ const sourcePanel = document.getElementById('sourcePanel');
331
+ const sourceCode = document.getElementById('sourceCode');
332
+ const statusDot = document.getElementById('statusDot');
333
+ const statusText = document.getElementById('statusText');
334
+ const toast = document.getElementById('toast');
335
+
336
+ // Viewport
337
+ function setViewport(viewport) {
338
+ currentViewport = viewport;
339
+ previewContainer.setAttribute('data-viewport', viewport);
340
+ document.querySelectorAll('[data-viewport]').forEach(function(btn) {
341
+ if (btn.classList.contains('toolbar-btn')) {
342
+ btn.classList.toggle('active', btn.getAttribute('data-viewport') === viewport);
343
+ }
344
+ });
345
+ }
346
+
347
+ // Content dark/light mode
348
+ function setContentMode(mode) {
349
+ contentDarkMode = mode === 'dark';
350
+ document.getElementById('lightModeBtn').classList.toggle('active', !contentDarkMode);
351
+ document.getElementById('darkModeBtn').classList.toggle('active', contentDarkMode);
352
+ previewContainer.classList.toggle('dark-content', contentDarkMode);
353
+ if (currentCode) {
354
+ renderPreview(currentCode, currentCompiledJs);
355
+ }
356
+ }
357
+
358
+ // Source panel
359
+ function toggleSource() {
360
+ sourceVisible = !sourceVisible;
361
+ sourcePanel.classList.toggle('collapsed', !sourceVisible);
362
+ document.getElementById('sourceToggleBtn').classList.toggle('active', sourceVisible);
363
+ }
364
+
365
+ // Copy code
366
+ function copyCode() {
367
+ if (!currentCode) return;
368
+ navigator.clipboard.writeText(currentCode).then(function() {
369
+ toast.classList.add('show');
370
+ setTimeout(function() { toast.classList.remove('show'); }, 1500);
371
+ });
372
+ }
373
+
374
+ // Check if code is plain HTML
375
+ function isPlainHtml(code) {
376
+ var t = code.trim();
377
+ return t.startsWith('<!') || (
378
+ t.startsWith('<') &&
379
+ t.indexOf('import ') === -1 &&
380
+ t.indexOf('export ') === -1 &&
381
+ t.indexOf('useState') === -1 &&
382
+ t.indexOf('useEffect') === -1 &&
383
+ t.indexOf('const ') === -1 &&
384
+ t.indexOf('function ') === -1
385
+ );
386
+ }
387
+
388
+ // Build error display element safely (no innerHTML with untrusted content)
389
+ function buildErrorHtml(message) {
390
+ return '<pre style="padding:16px;color:#ef4444;font-family:monospace;font-size:13px;white-space:pre-wrap;">' +
391
+ message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') +
392
+ '</pre>';
393
+ }
394
+
395
+ // Render preview
396
+ function renderPreview(code, compiledJs) {
397
+ emptyState.style.display = 'none';
398
+ previewIframe.style.display = 'block';
399
+
400
+ var bgColour = contentDarkMode ? '#1a1a2e' : '#ffffff';
401
+ var textColour = contentDarkMode ? '#e2e8f0' : '#1a202c';
402
+
403
+ if (isPlainHtml(code)) {
404
+ // Plain HTML - inject directly
405
+ previewIframe.srcdoc = '<!DOCTYPE html>' +
406
+ '<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">' +
407
+ '<script src="https://cdn.tailwindcss.com"><\/script>' +
408
+ '<style>body { background: ' + bgColour + '; color: ' + textColour + '; }</style>' +
409
+ '</head><body>' + code + '</body></html>';
410
+ } else {
411
+ // TSX - use compiled JS with require/exports/module shims
412
+ var escapedJs = JSON.stringify(compiledJs)
413
+ .replace(/</g, '\\u003c')
414
+ .replace(/>/g, '\\u003e');
415
+
416
+ previewIframe.srcdoc = '<!DOCTYPE html>' +
417
+ '<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">' +
418
+ '<script src="https://cdn.tailwindcss.com"><\/script>' +
419
+ '<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"><\/script>' +
420
+ '<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"><\/script>' +
421
+ '<style>body { background: ' + bgColour + '; color: ' + textColour + '; margin: 0; }</style>' +
422
+ '</head><body><div id="root"></div>' +
423
+ '<script>' +
424
+ 'try {' +
425
+ ' var exports = {};' +
426
+ ' var module = { exports: exports };' +
427
+ ' function require(name) {' +
428
+ ' if (name === "react" || name === "React") return React;' +
429
+ ' if (name === "react-dom" || name === "react-dom/client" || name === "ReactDOM") return ReactDOM;' +
430
+ ' throw new Error("Cannot require: " + name);' +
431
+ ' }' +
432
+ ' var __code = ' + escapedJs + ';' +
433
+ ' var s = document.createElement("script");' +
434
+ ' s.textContent = __code;' +
435
+ ' document.body.appendChild(s);' +
436
+ ' var Component = module.exports.default || module.exports;' +
437
+ ' if (typeof Component === "function") {' +
438
+ ' var root = ReactDOM.createRoot(document.getElementById("root"));' +
439
+ ' root.render(React.createElement(Component));' +
440
+ ' } else {' +
441
+ ' document.getElementById("root").textContent = "No default export found. Export a React component as default.";' +
442
+ ' }' +
443
+ '} catch(e) {' +
444
+ ' document.getElementById("root").textContent = e.message + "\\n" + (e.stack || "");' +
445
+ '}' +
446
+ '<\/script></body></html>';
447
+ }
448
+ }
449
+
450
+ // Update source panel
451
+ function updateSourcePanel(code) {
452
+ sourceCode.textContent = code;
453
+ }
454
+
455
+ // WebSocket connection with auto-reconnect
456
+ function connect() {
457
+ var protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
458
+ var ws = new WebSocket(protocol + '//' + location.host);
459
+
460
+ ws.onopen = function() {
461
+ statusDot.classList.add('connected');
462
+ statusText.textContent = 'Connected';
463
+ };
464
+
465
+ ws.onclose = function() {
466
+ statusDot.classList.remove('connected');
467
+ statusText.textContent = 'Disconnected';
468
+ setTimeout(connect, 2000);
469
+ };
470
+
471
+ ws.onerror = function() {
472
+ ws.close();
473
+ };
474
+
475
+ ws.onmessage = function(event) {
476
+ try {
477
+ var data = JSON.parse(event.data);
478
+ if (data.type === 'update') {
479
+ currentCode = data.code;
480
+ currentCompiledJs = data.compiledJs;
481
+ renderPreview(currentCode, currentCompiledJs);
482
+ updateSourcePanel(currentCode);
483
+ }
484
+ } catch (e) {
485
+ console.error('Failed to parse WebSocket message:', e);
486
+ }
487
+ };
488
+ }
489
+
490
+ connect();
491
+ </script>
492
+ </body>
493
+ </html>
@@ -0,0 +1,10 @@
1
+ export interface TranspileResult {
2
+ js: string;
3
+ error?: string;
4
+ }
5
+ /**
6
+ * Transpile TSX/JSX source to CommonJS JavaScript using TypeScript's transpileModule.
7
+ * If the input looks like plain HTML (no JSX indicators), returns it unchanged.
8
+ */
9
+ export declare function transpileTsx(code: string): TranspileResult;
10
+ //# sourceMappingURL=transpile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transpile.d.ts","sourceRoot":"","sources":["../../../src/preview/transpile.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAqC1D"}
@@ -0,0 +1,40 @@
1
+ import ts from "typescript";
2
+ /**
3
+ * Transpile TSX/JSX source to CommonJS JavaScript using TypeScript's transpileModule.
4
+ * If the input looks like plain HTML (no JSX indicators), returns it unchanged.
5
+ */
6
+ export function transpileTsx(code) {
7
+ const trimmed = code.trim();
8
+ // If it looks like plain HTML (starts with < but has no JSX indicators), return as-is
9
+ const isPlainHtml = trimmed.startsWith("<!") ||
10
+ (trimmed.startsWith("<") &&
11
+ !trimmed.includes("import ") &&
12
+ !trimmed.includes("export ") &&
13
+ !trimmed.includes("const ") &&
14
+ !trimmed.includes("function ") &&
15
+ !trimmed.includes("=> ") &&
16
+ !trimmed.includes("useState") &&
17
+ !trimmed.includes("useEffect"));
18
+ if (isPlainHtml) {
19
+ return { js: code };
20
+ }
21
+ try {
22
+ const result = ts.transpileModule(code, {
23
+ compilerOptions: {
24
+ module: ts.ModuleKind.CommonJS,
25
+ target: ts.ScriptTarget.ES2020,
26
+ jsx: ts.JsxEmit.React,
27
+ esModuleInterop: true,
28
+ allowJs: true,
29
+ strict: false,
30
+ },
31
+ fileName: "component.tsx",
32
+ });
33
+ return { js: result.outputText };
34
+ }
35
+ catch (err) {
36
+ const message = err instanceof Error ? err.message : String(err);
37
+ return { js: "", error: message };
38
+ }
39
+ }
40
+ //# sourceMappingURL=transpile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transpile.js","sourceRoot":"","sources":["../../../src/preview/transpile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAO5B;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAE5B,sFAAsF;IACtF,MAAM,WAAW,GACf,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;QACxB,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACtB,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC5B,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC5B,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC3B,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC9B,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;YACxB,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;YAC7B,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;IAEpC,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE;YACtC,eAAe,EAAE;gBACf,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ;gBAC9B,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM;gBAC9B,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK;gBACrB,eAAe,EAAE,IAAI;gBACrB,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,KAAK;aACd;YACD,QAAQ,EAAE,eAAe;SAC1B,CAAC,CAAC;QAEH,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACpC,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type http from "node:http";
2
+ export interface WsBroadcaster {
3
+ broadcast: (code: string, compiledJs: string) => void;
4
+ getLastPreview: () => {
5
+ code: string;
6
+ compiledJs: string;
7
+ } | null;
8
+ }
9
+ /**
10
+ * Create a WebSocket server attached to the given HTTP server.
11
+ * Returns a broadcast function that pushes updates to all connected clients.
12
+ *
13
+ * Also handles incoming { type: "preview", code, language } messages from MCP tools:
14
+ * transpiles TSX server-side and broadcasts the compiled result to all preview clients.
15
+ */
16
+ export declare function createWsServer(server: http.Server): WsBroadcaster;
17
+ //# sourceMappingURL=ws.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../../src/preview/ws.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAIlC,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,cAAc,EAAE,MAAM;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACnE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,aAAa,CAuEjE"}
@@ -0,0 +1,66 @@
1
+ import { WebSocketServer } from "ws";
2
+ import { transpileTsx } from "./transpile.js";
3
+ /**
4
+ * Create a WebSocket server attached to the given HTTP server.
5
+ * Returns a broadcast function that pushes updates to all connected clients.
6
+ *
7
+ * Also handles incoming { type: "preview", code, language } messages from MCP tools:
8
+ * transpiles TSX server-side and broadcasts the compiled result to all preview clients.
9
+ */
10
+ export function createWsServer(server) {
11
+ const wss = new WebSocketServer({ server });
12
+ wss.on("error", () => {
13
+ // Handled by the HTTP server's error listener — suppress unhandled crash
14
+ });
15
+ const clients = new Set();
16
+ let lastPreview = null;
17
+ wss.on("connection", (ws) => {
18
+ clients.add(ws);
19
+ ws.on("message", (data) => {
20
+ try {
21
+ const msg = JSON.parse(String(data));
22
+ if (msg.type === "preview" && msg.code) {
23
+ const language = msg.language ?? "tsx";
24
+ let compiledJs = msg.code;
25
+ if (language === "tsx" || language === "jsx" || language === "ts") {
26
+ const result = transpileTsx(msg.code);
27
+ if (result.error) {
28
+ // Send error back to the sender
29
+ ws.send(JSON.stringify({
30
+ type: "error",
31
+ message: `Transpilation failed: ${result.error}`,
32
+ }));
33
+ return;
34
+ }
35
+ compiledJs = result.js;
36
+ }
37
+ // Broadcast to all OTHER clients (the preview page)
38
+ broadcast(msg.code, compiledJs);
39
+ }
40
+ }
41
+ catch {
42
+ // Ignore malformed messages
43
+ }
44
+ });
45
+ ws.on("close", () => {
46
+ clients.delete(ws);
47
+ });
48
+ ws.on("error", () => {
49
+ clients.delete(ws);
50
+ });
51
+ });
52
+ function broadcast(code, compiledJs) {
53
+ lastPreview = { code, compiledJs };
54
+ const payload = JSON.stringify({ type: "update", code, compiledJs });
55
+ for (const client of clients) {
56
+ if (client.readyState === client.OPEN) {
57
+ client.send(payload);
58
+ }
59
+ }
60
+ }
61
+ function getLastPreview() {
62
+ return lastPreview;
63
+ }
64
+ return { broadcast, getLastPreview };
65
+ }
66
+ //# sourceMappingURL=ws.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws.js","sourceRoot":"","sources":["../../../src/preview/ws.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAkB,MAAM,IAAI,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO9C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5C,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACnB,yEAAyE;IAC3E,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,IAAI,GAAG,EAAa,CAAC;IACrC,IAAI,WAAW,GAAgD,IAAI,CAAC;IAEpE,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAIlC,CAAC;gBAEF,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;oBACvC,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,KAAK,CAAC;oBACvC,IAAI,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC;oBAE1B,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;wBAClE,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;wBACtC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;4BACjB,gCAAgC;4BAChC,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;gCACb,IAAI,EAAE,OAAO;gCACb,OAAO,EAAE,yBAAyB,MAAM,CAAC,KAAK,EAAE;6BACjD,CAAC,CACH,CAAC;4BACF,OAAO;wBACT,CAAC;wBACD,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC;oBACzB,CAAC;oBAED,oDAAoD;oBACpD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;gBAClC,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,4BAA4B;YAC9B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAClB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAClB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,SAAS,SAAS,CAAC,IAAY,EAAE,UAAkB;QACjD,WAAW,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAErE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;gBACtC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,cAAc;QACrB,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;AACvC,CAAC"}