@tpitre/story-ui 2.2.0 → 2.3.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 (188) hide show
  1. package/.env.sample +82 -11
  2. package/README.md +89 -0
  3. package/dist/cli/deploy.d.ts +17 -0
  4. package/dist/cli/deploy.d.ts.map +1 -0
  5. package/dist/cli/deploy.js +696 -0
  6. package/dist/cli/index.d.ts +3 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +26 -2
  9. package/dist/cli/setup.d.ts +11 -0
  10. package/dist/cli/setup.d.ts.map +1 -0
  11. package/dist/cli/setup.js +437 -110
  12. package/dist/mcp-server/index.d.ts +2 -0
  13. package/dist/mcp-server/index.d.ts.map +1 -0
  14. package/dist/mcp-server/index.js +120 -2
  15. package/dist/mcp-server/mcp-stdio-server.d.ts +3 -0
  16. package/dist/mcp-server/mcp-stdio-server.d.ts.map +1 -0
  17. package/dist/mcp-server/mcp-stdio-server.js +8 -1
  18. package/dist/mcp-server/routes/claude.d.ts +3 -0
  19. package/dist/mcp-server/routes/claude.d.ts.map +1 -0
  20. package/dist/mcp-server/routes/claude.js +60 -23
  21. package/dist/mcp-server/routes/components.d.ts +4 -0
  22. package/dist/mcp-server/routes/components.d.ts.map +1 -0
  23. package/dist/mcp-server/routes/frameworks.d.ts +38 -0
  24. package/dist/mcp-server/routes/frameworks.d.ts.map +1 -0
  25. package/dist/mcp-server/routes/frameworks.js +183 -0
  26. package/dist/mcp-server/routes/generateStory.d.ts +3 -0
  27. package/dist/mcp-server/routes/generateStory.d.ts.map +1 -0
  28. package/dist/mcp-server/routes/generateStory.js +160 -76
  29. package/dist/mcp-server/routes/generateStoryStream.d.ts +12 -0
  30. package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -0
  31. package/dist/mcp-server/routes/generateStoryStream.js +947 -0
  32. package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
  33. package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
  34. package/dist/mcp-server/routes/mcpRemote.d.ts +14 -0
  35. package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
  36. package/dist/mcp-server/routes/mcpRemote.js +489 -0
  37. package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
  38. package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
  39. package/dist/mcp-server/routes/providers.d.ts +89 -0
  40. package/dist/mcp-server/routes/providers.d.ts.map +1 -0
  41. package/dist/mcp-server/routes/providers.js +369 -0
  42. package/dist/mcp-server/routes/storySync.d.ts +26 -0
  43. package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
  44. package/dist/mcp-server/routes/streamTypes.d.ts +110 -0
  45. package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
  46. package/dist/mcp-server/routes/streamTypes.js +18 -0
  47. package/dist/mcp-server/sessionManager.d.ts +50 -0
  48. package/dist/mcp-server/sessionManager.d.ts.map +1 -0
  49. package/dist/story-generator/componentBlacklist.d.ts +21 -0
  50. package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
  51. package/dist/story-generator/componentDiscovery.d.ts +28 -0
  52. package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
  53. package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
  54. package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
  55. package/dist/story-generator/componentRegistryGenerator.js +205 -0
  56. package/dist/story-generator/configLoader.d.ts +33 -0
  57. package/dist/story-generator/configLoader.d.ts.map +1 -0
  58. package/dist/story-generator/considerationsLoader.d.ts +32 -0
  59. package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
  60. package/dist/story-generator/documentation-sources.d.ts +28 -0
  61. package/dist/story-generator/documentation-sources.d.ts.map +1 -0
  62. package/dist/story-generator/documentationLoader.d.ts +64 -0
  63. package/dist/story-generator/documentationLoader.d.ts.map +1 -0
  64. package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
  65. package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
  66. package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
  67. package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
  68. package/dist/story-generator/enhancedComponentDiscovery.js +111 -11
  69. package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
  70. package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
  71. package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
  72. package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
  73. package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
  74. package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
  75. package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
  76. package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
  77. package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
  78. package/dist/story-generator/framework-adapters/index.d.ts +97 -0
  79. package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
  80. package/dist/story-generator/framework-adapters/index.js +198 -0
  81. package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
  82. package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
  83. package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
  84. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
  85. package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
  86. package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
  87. package/dist/story-generator/framework-adapters/types.d.ts +182 -0
  88. package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
  89. package/dist/story-generator/framework-adapters/types.js +8 -0
  90. package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
  91. package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
  92. package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
  93. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
  94. package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
  95. package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
  96. package/dist/story-generator/generateStory.d.ts +7 -0
  97. package/dist/story-generator/generateStory.d.ts.map +1 -0
  98. package/dist/story-generator/gitignoreManager.d.ts +50 -0
  99. package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
  100. package/dist/story-generator/imageProcessor.d.ts +80 -0
  101. package/dist/story-generator/imageProcessor.d.ts.map +1 -0
  102. package/dist/story-generator/imageProcessor.js +391 -0
  103. package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
  104. package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
  105. package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
  106. package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
  107. package/dist/story-generator/llm-providers/base-provider.js +135 -0
  108. package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
  109. package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
  110. package/dist/story-generator/llm-providers/claude-provider.js +414 -0
  111. package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
  112. package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
  113. package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
  114. package/dist/story-generator/llm-providers/index.d.ts +63 -0
  115. package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
  116. package/dist/story-generator/llm-providers/index.js +169 -0
  117. package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
  118. package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
  119. package/dist/story-generator/llm-providers/openai-provider.js +458 -0
  120. package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
  121. package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
  122. package/dist/story-generator/llm-providers/settings-manager.js +173 -0
  123. package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
  124. package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
  125. package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
  126. package/dist/story-generator/llm-providers/types.d.ts +153 -0
  127. package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
  128. package/dist/story-generator/llm-providers/types.js +8 -0
  129. package/dist/story-generator/logger.d.ts +14 -0
  130. package/dist/story-generator/logger.d.ts.map +1 -0
  131. package/dist/story-generator/logger.js +96 -29
  132. package/dist/story-generator/postProcessStory.d.ts +6 -0
  133. package/dist/story-generator/postProcessStory.d.ts.map +1 -0
  134. package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
  135. package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
  136. package/dist/story-generator/promptGenerator.d.ts +48 -0
  137. package/dist/story-generator/promptGenerator.d.ts.map +1 -0
  138. package/dist/story-generator/promptGenerator.js +186 -1
  139. package/dist/story-generator/storyHistory.d.ts +44 -0
  140. package/dist/story-generator/storyHistory.d.ts.map +1 -0
  141. package/dist/story-generator/storySync.d.ts +68 -0
  142. package/dist/story-generator/storySync.d.ts.map +1 -0
  143. package/dist/story-generator/storyTracker.d.ts +48 -0
  144. package/dist/story-generator/storyTracker.d.ts.map +1 -0
  145. package/dist/story-generator/storyValidator.d.ts +6 -0
  146. package/dist/story-generator/storyValidator.d.ts.map +1 -0
  147. package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
  148. package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
  149. package/dist/story-generator/universalDesignSystemAdapter.js +138 -1
  150. package/dist/story-generator/urlRedirectService.d.ts +21 -0
  151. package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
  152. package/dist/story-generator/validateStory.d.ts +19 -0
  153. package/dist/story-generator/validateStory.d.ts.map +1 -0
  154. package/dist/story-generator/validateStory.js +6 -2
  155. package/dist/story-generator/visionPrompts.d.ts +88 -0
  156. package/dist/story-generator/visionPrompts.d.ts.map +1 -0
  157. package/dist/story-generator/visionPrompts.js +462 -0
  158. package/dist/story-ui.config.d.ts +78 -0
  159. package/dist/story-ui.config.d.ts.map +1 -0
  160. package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
  161. package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
  162. package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
  163. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
  164. package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
  165. package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
  166. package/dist/templates/StoryUI/index.d.ts +3 -0
  167. package/dist/templates/StoryUI/index.d.ts.map +1 -0
  168. package/dist/templates/StoryUI/index.js +2 -0
  169. package/package.json +17 -3
  170. package/templates/StoryUI/StoryUIPanel.tsx +1960 -384
  171. package/templates/StoryUI/index.tsx +1 -1
  172. package/templates/StoryUI/manager.tsx +264 -0
  173. package/templates/production-app/.env.example +11 -0
  174. package/templates/production-app/index.html +66 -0
  175. package/templates/production-app/package.json +30 -0
  176. package/templates/production-app/public/favicon.svg +5 -0
  177. package/templates/production-app/src/App.tsx +1560 -0
  178. package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
  179. package/templates/production-app/src/componentRegistry.ts +315 -0
  180. package/templates/production-app/src/considerations.ts +16 -0
  181. package/templates/production-app/src/index.css +284 -0
  182. package/templates/production-app/src/main.tsx +25 -0
  183. package/templates/production-app/tsconfig.json +32 -0
  184. package/templates/production-app/tsconfig.node.json +11 -0
  185. package/templates/production-app/vite.config.ts +83 -0
  186. package/templates/react-import-rule.json +2 -2
  187. package/dist/index.js +0 -12
  188. package/dist/story-ui.config.loader.js +0 -205
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Live Preview Renderer
3
+ *
4
+ * This component takes generated JSX code as a string and renders it live
5
+ * using Babel standalone for JSX compilation and the component registry
6
+ * for component resolution.
7
+ */
8
+
9
+ import React, { useState, useEffect, useRef } from 'react';
10
+ import * as Babel from '@babel/standalone';
11
+ // This import will be replaced with the actual component registry at build time
12
+ import { componentRegistry, React as ReactExport } from './componentRegistry';
13
+
14
+ interface LivePreviewRendererProps {
15
+ /** The JSX code string to render */
16
+ code: string;
17
+ /** Optional error handler */
18
+ onError?: (error: Error) => void;
19
+ /** Optional success handler */
20
+ onSuccess?: () => void;
21
+ /** Custom styles for the container */
22
+ containerStyle?: React.CSSProperties;
23
+ }
24
+
25
+ interface ErrorBoundaryState {
26
+ hasError: boolean;
27
+ error: Error | null;
28
+ }
29
+
30
+ // Error boundary to catch render errors
31
+ class ErrorBoundary extends React.Component<
32
+ { children: React.ReactNode; onError?: (error: Error) => void },
33
+ ErrorBoundaryState
34
+ > {
35
+ constructor(props: { children: React.ReactNode; onError?: (error: Error) => void }) {
36
+ super(props);
37
+ this.state = { hasError: false, error: null };
38
+ }
39
+
40
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
41
+ return { hasError: true, error };
42
+ }
43
+
44
+ componentDidCatch(error: Error) {
45
+ this.props.onError?.(error);
46
+ }
47
+
48
+ componentDidUpdate(prevProps: { children: React.ReactNode }) {
49
+ // Reset error state when children change
50
+ if (prevProps.children !== this.props.children && this.state.hasError) {
51
+ this.setState({ hasError: false, error: null });
52
+ }
53
+ }
54
+
55
+ render() {
56
+ if (this.state.hasError) {
57
+ return (
58
+ <div style={{
59
+ padding: '16px',
60
+ background: 'rgba(239, 68, 68, 0.1)',
61
+ borderLeft: '3px solid #ef4444',
62
+ borderRadius: '4px',
63
+ color: '#ef4444',
64
+ fontFamily: 'monospace',
65
+ fontSize: '13px',
66
+ whiteSpace: 'pre-wrap',
67
+ }}>
68
+ <strong>Render Error:</strong>
69
+ <br />
70
+ {this.state.error?.message}
71
+ </div>
72
+ );
73
+ }
74
+
75
+ return this.props.children;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Extract JSX from generated code
81
+ * The AI typically returns a full story file, but we only need the JSX render content
82
+ * This function handles various LLM output formats including markdown-wrapped code
83
+ */
84
+ function extractJSX(code: string): string {
85
+ let cleanCode = code.trim();
86
+
87
+ // Step 0: Detect and reject HTML documents and invalid XML responses
88
+ // LLMs sometimes return full HTML pages or internal XML tags instead of JSX components
89
+ if (cleanCode.match(/^<!DOCTYPE\s+html/i) || cleanCode.match(/^<html[\s>]/i)) {
90
+ // Try to extract JSX components from within the HTML
91
+ // Look for React/JSX component patterns inside the HTML
92
+ const jsxInHtml = cleanCode.match(/<([A-Z][a-zA-Z0-9]*)[^>]*>[\s\S]*?<\/\1>/);
93
+ if (jsxInHtml) {
94
+ cleanCode = jsxInHtml[0];
95
+ } else {
96
+ throw new Error('Invalid response: LLM returned an HTML document instead of JSX components. Please try again with a more specific prompt.');
97
+ }
98
+ }
99
+
100
+ // Step 0.5: Detect internal LLM metadata tags (anthropic_info, thinking, budget, usage, etc.)
101
+ // These should never appear in responses but sometimes leak through
102
+ // Pattern matches: <tag>, <tag:value>, <tag_name>, </tag>, etc. where tag is lowercase
103
+ const llmMetadataPattern = /<(?:anthropic_info|thinking|budget|usage|system|context|response|metadata|internal)[^>]*>|<[a-z][a-z_]*:[^>]+>|<\/?[a-z_]+>/;
104
+ if (cleanCode.match(llmMetadataPattern)) {
105
+ // Try to find JSX components in the mess
106
+ const jsxMatch = cleanCode.match(/<([A-Z][a-zA-Z0-9]*)[^>]*>[\s\S]*?<\/\1>/);
107
+ if (jsxMatch) {
108
+ cleanCode = jsxMatch[0];
109
+ } else {
110
+ // Also try self-closing JSX components
111
+ const selfClosingMatch = cleanCode.match(/<([A-Z][a-zA-Z0-9]*)[^>]*\/>/);
112
+ if (selfClosingMatch) {
113
+ cleanCode = selfClosingMatch[0];
114
+ } else {
115
+ throw new Error('Invalid response: LLM returned internal metadata instead of JSX components. Please try again.');
116
+ }
117
+ }
118
+ }
119
+
120
+ // Step 1: Remove markdown headers (# Header, ## Header, etc.)
121
+ cleanCode = cleanCode.replace(/^#+\s+[^\n]*\n*/gm, '');
122
+
123
+ // Step 2: Remove markdown explanatory text before code blocks
124
+ // This handles patterns like "Here's a component:\n\n```jsx"
125
+ cleanCode = cleanCode.replace(/^[^<`]*(?=```)/s, '');
126
+
127
+ // Step 3: Remove markdown code blocks (```jsx ... ```)
128
+ // Handle multiple code blocks and various language tags
129
+ const codeBlockMatch = cleanCode.match(/```(?:jsx|tsx|javascript|js|typescript|ts|html|react)?\n?([\s\S]*?)```/);
130
+ if (codeBlockMatch) {
131
+ cleanCode = codeBlockMatch[1].trim();
132
+ // Recursively process in case extracted code is also HTML
133
+ if (cleanCode.match(/^<!DOCTYPE\s+html/i) || cleanCode.match(/^<html[\s>]/i)) {
134
+ return extractJSX(cleanCode);
135
+ }
136
+ } else if (cleanCode.startsWith('```')) {
137
+ // Fallback for unclosed code blocks
138
+ cleanCode = cleanCode
139
+ .replace(/^```(?:jsx|tsx|javascript|js|typescript|ts|html|react)?\n?/, '')
140
+ .replace(/\n?```$/, '');
141
+ }
142
+
143
+ // Step 4: Remove any remaining text before the first JSX tag
144
+ // This catches explanations like "Here's the card:" that appear before <Component>
145
+ const firstTagIndex = cleanCode.indexOf('<');
146
+ if (firstTagIndex > 0) {
147
+ // Check if there's actual JSX after this point
148
+ const potentialJSX = cleanCode.substring(firstTagIndex);
149
+ if (potentialJSX.match(/^<[A-Z]/)) {
150
+ cleanCode = potentialJSX;
151
+ }
152
+ }
153
+
154
+ // Step 4.5: Skip lowercase HTML tags and find actual JSX components
155
+ // JSX components start with uppercase letters
156
+ if (cleanCode.match(/^<[a-z]/)) {
157
+ // This is an HTML tag, not a JSX component - look for JSX further in
158
+ const jsxComponentMatch = cleanCode.match(/<([A-Z][a-zA-Z0-9]*)[^>]*>[\s\S]*$/);
159
+ if (jsxComponentMatch) {
160
+ cleanCode = jsxComponentMatch[0];
161
+ }
162
+ }
163
+
164
+ // Step 5: Remove any trailing text after the JSX closes
165
+ // Find the matching closing tag by tracking depth
166
+ if (cleanCode.startsWith('<') && cleanCode.match(/^<[A-Z]/)) {
167
+ const jsxEndIndex = findJSXEnd(cleanCode);
168
+ if (jsxEndIndex > 0 && jsxEndIndex < cleanCode.length) {
169
+ cleanCode = cleanCode.substring(0, jsxEndIndex).trim();
170
+ }
171
+ }
172
+
173
+ cleanCode = cleanCode.trim();
174
+
175
+ // Try to extract JSX from a render function
176
+ const renderMatch = cleanCode.match(/render:\s*\(\)\s*=>\s*\(?([\s\S]*?)\)?\s*,?\s*\}/);
177
+ if (renderMatch) {
178
+ const jsx = renderMatch[1].trim();
179
+ // Remove trailing paren if present
180
+ return jsx.endsWith(')') && !jsx.includes('(') ? jsx.slice(0, -1) : jsx;
181
+ }
182
+
183
+ // Try to extract from args.children
184
+ const childrenMatch = cleanCode.match(/children:\s*\(?([\s\S]*?)\)?\s*\}/);
185
+ if (childrenMatch) {
186
+ const jsx = childrenMatch[1].trim();
187
+ return jsx.endsWith(')') && !jsx.includes('(') ? jsx.slice(0, -1) : jsx;
188
+ }
189
+
190
+ // If it looks like a JSX component (starts with <Uppercase), use it directly
191
+ if (cleanCode.match(/^<[A-Z]/)) {
192
+ return cleanCode;
193
+ }
194
+
195
+ // Try to extract any JSX component from the code
196
+ const jsxMatch = cleanCode.match(/<[A-Z][a-zA-Z0-9]*[^>]*>[\s\S]*$/);
197
+ if (jsxMatch) {
198
+ return jsxMatch[0];
199
+ }
200
+
201
+ // If we still have lowercase tags, it's not valid JSX
202
+ if (cleanCode.match(/^<[a-z]/)) {
203
+ throw new Error('Invalid response: LLM returned HTML elements instead of JSX components. Please try again.');
204
+ }
205
+
206
+ // Return as-is and let Babel fail if it's not valid
207
+ return cleanCode;
208
+ }
209
+
210
+ /**
211
+ * Find where the JSX expression ends by tracking tag depth
212
+ */
213
+ function findJSXEnd(code: string): number {
214
+ let depth = 0;
215
+ let i = 0;
216
+ let inTag = false;
217
+ let inString = false;
218
+ let stringChar = '';
219
+ let lastTagEnd = -1;
220
+
221
+ while (i < code.length) {
222
+ const char = code[i];
223
+
224
+ // Handle string boundaries
225
+ if (!inString && (char === '"' || char === "'" || char === '`')) {
226
+ inString = true;
227
+ stringChar = char;
228
+ } else if (inString && char === stringChar && code[i - 1] !== '\\') {
229
+ inString = false;
230
+ }
231
+
232
+ if (!inString) {
233
+ if (char === '<') {
234
+ // Check for closing tag
235
+ if (code[i + 1] === '/') {
236
+ inTag = true;
237
+ } else if (code[i + 1] && /[A-Za-z]/.test(code[i + 1])) {
238
+ // Opening tag
239
+ depth++;
240
+ inTag = true;
241
+ }
242
+ } else if (char === '>') {
243
+ if (inTag) {
244
+ // Self-closing tag
245
+ if (code[i - 1] === '/') {
246
+ depth--;
247
+ } else if (code.substring(Math.max(0, i - 10), i).includes('</')) {
248
+ // Closing tag
249
+ depth--;
250
+ }
251
+ inTag = false;
252
+ lastTagEnd = i + 1;
253
+
254
+ // If depth is 0, we've found the end of the root element
255
+ if (depth === 0) {
256
+ return lastTagEnd;
257
+ }
258
+ }
259
+ }
260
+ }
261
+ i++;
262
+ }
263
+
264
+ return lastTagEnd > 0 ? lastTagEnd : code.length;
265
+ }
266
+
267
+ /**
268
+ * Compile JSX code string to a React component
269
+ */
270
+ function compileJSX(jsxCode: string): React.ComponentType | null {
271
+ try {
272
+ // Create a scope object with all available components and React
273
+ const scope: Record<string, any> = {
274
+ React: ReactExport,
275
+ ...componentRegistry,
276
+ // Add common React hooks
277
+ useState: React.useState,
278
+ useEffect: React.useEffect,
279
+ useCallback: React.useCallback,
280
+ useMemo: React.useMemo,
281
+ useRef: React.useRef,
282
+ };
283
+
284
+ // Extract the JSX to render
285
+ const extractedJSX = extractJSX(jsxCode);
286
+
287
+ // Wrap in a function component
288
+ const wrappedCode = `
289
+ (function() {
290
+ const { ${Object.keys(scope).join(', ')} } = scope;
291
+ return function PreviewComponent() {
292
+ return (${extractedJSX});
293
+ };
294
+ })()
295
+ `;
296
+
297
+ // Transform JSX to JavaScript
298
+ const transformed = Babel.transform(wrappedCode, {
299
+ presets: ['react'],
300
+ filename: 'preview.tsx',
301
+ });
302
+
303
+ if (!transformed.code) {
304
+ throw new Error('Babel transformation produced no output');
305
+ }
306
+
307
+ // Create the component using Function constructor
308
+ // eslint-disable-next-line no-new-func
309
+ const createComponent = new Function('scope', `return ${transformed.code}`);
310
+ const Component = createComponent(scope);
311
+
312
+ return Component;
313
+ } catch (error) {
314
+ console.error('JSX compilation error:', error);
315
+ throw error;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Live Preview Renderer Component
321
+ */
322
+ export const LivePreviewRenderer: React.FC<LivePreviewRendererProps> = ({
323
+ code,
324
+ onError,
325
+ onSuccess,
326
+ containerStyle,
327
+ }) => {
328
+ const [compiledComponent, setCompiledComponent] = useState<React.ComponentType | null>(null);
329
+ const [error, setError] = useState<Error | null>(null);
330
+ const previousCodeRef = useRef<string>('');
331
+
332
+ // Compile the code when it changes
333
+ useEffect(() => {
334
+ if (!code || code === previousCodeRef.current) {
335
+ return;
336
+ }
337
+
338
+ previousCodeRef.current = code;
339
+ setError(null);
340
+
341
+ try {
342
+ const Component = compileJSX(code);
343
+ setCompiledComponent(() => Component);
344
+ onSuccess?.();
345
+ } catch (err) {
346
+ const error = err instanceof Error ? err : new Error(String(err));
347
+ setError(error);
348
+ setCompiledComponent(null);
349
+ onError?.(error);
350
+ }
351
+ }, [code, onError, onSuccess]);
352
+
353
+ // Render error state
354
+ if (error) {
355
+ return (
356
+ <div style={{
357
+ padding: '24px',
358
+ ...containerStyle,
359
+ }}>
360
+ <div style={{
361
+ padding: '16px',
362
+ background: 'rgba(239, 68, 68, 0.1)',
363
+ borderLeft: '3px solid #ef4444',
364
+ borderRadius: '4px',
365
+ color: '#ef4444',
366
+ }}>
367
+ <div style={{
368
+ fontWeight: 600,
369
+ marginBottom: '8px',
370
+ fontSize: '14px',
371
+ }}>
372
+ Compilation Error
373
+ </div>
374
+ <pre style={{
375
+ margin: 0,
376
+ fontFamily: '"Fira Code", Monaco, monospace',
377
+ fontSize: '12px',
378
+ lineHeight: 1.5,
379
+ whiteSpace: 'pre-wrap',
380
+ wordBreak: 'break-word',
381
+ }}>
382
+ {error.message}
383
+ </pre>
384
+ </div>
385
+ </div>
386
+ );
387
+ }
388
+
389
+ // Render empty state
390
+ if (!compiledComponent) {
391
+ return (
392
+ <div style={{
393
+ display: 'flex',
394
+ alignItems: 'center',
395
+ justifyContent: 'center',
396
+ height: '100%',
397
+ color: '#71717a',
398
+ ...containerStyle,
399
+ }}>
400
+ Waiting for code...
401
+ </div>
402
+ );
403
+ }
404
+
405
+ // Render the compiled component
406
+ const Component = compiledComponent;
407
+ return (
408
+ <div style={{
409
+ padding: '24px',
410
+ minHeight: '200px',
411
+ ...containerStyle,
412
+ }}>
413
+ <ErrorBoundary onError={onError}>
414
+ <Component />
415
+ </ErrorBoundary>
416
+ </div>
417
+ );
418
+ };
419
+
420
+ export default LivePreviewRenderer;