-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
204 lines (166 loc) · 6.58 KB
/
app.js
File metadata and controls
204 lines (166 loc) · 6.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Global state
let currentCode = '';
let isCodeView = false;
let currentRoot = null;
// Set example prompt
function setExample(text) {
document.getElementById('promptInput').value = text;
}
// Toggle between preview and code view
function toggleView() {
isCodeView = !isCodeView;
const previewArea = document.getElementById('previewArea');
const codeArea = document.getElementById('codeArea');
const toggleBtn = document.getElementById('toggleViewBtn');
if (isCodeView) {
previewArea.classList.add('hidden');
codeArea.classList.remove('hidden');
toggleBtn.textContent = 'View Preview';
} else {
previewArea.classList.remove('hidden');
codeArea.classList.add('hidden');
toggleBtn.textContent = 'View Code';
}
}
// Sanitize code to prevent XSS attacks
function sanitizeCode(code) {
// Remove script tags
code = code.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
// Remove dangerous event handlers
code = code.replace(/on\w+\s*=\s*["'][^"']*["']/gi, '');
// Remove javascript: protocol
code = code.replace(/javascript:/gi, '');
// Remove eval and Function constructor
code = code.replace(/eval\s*\(/gi, '');
code = code.replace(/Function\s*\(/gi, '');
// Remove iframe tags
code = code.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '');
return code;
}
// Extract component code from LLM response
function extractComponentCode(response) {
// Remove markdown code blocks
let code = response.replace(/```jsx?\n?/g, '').replace(/```\n?/g, '');
// Try to find a function component
const functionMatch = code.match(/(?:const|function)\s+(\w+)\s*=?\s*(?:\([^)]*\))?\s*=>\s*{[\s\S]*?}/);
if (functionMatch) {
return { code: functionMatch[0], name: functionMatch[1] };
}
// Try to find a function declaration
const funcDeclMatch = code.match(/function\s+(\w+)\s*\([^)]*\)\s*{[\s\S]*?}/);
if (funcDeclMatch) {
return { code: funcDeclMatch[0], name: funcDeclMatch[1] };
}
// If it's just JSX, wrap it in a component
if (code.trim().startsWith('<')) {
const wrappedCode = `function GeneratedComponent() { return (${code.trim()}); }`;
return { code: wrappedCode, name: 'GeneratedComponent' };
}
// Default: assume it's a complete component
return { code: code.trim(), name: 'GeneratedComponent' };
}
// Render the component dynamically
function renderComponent(code) {
const previewArea = document.getElementById('previewArea');
try {
// Clear previous content
if (currentRoot) {
currentRoot.unmount();
}
previewArea.innerHTML = '';
// Sanitize the code
const sanitizedCode = sanitizeCode(code);
// Extract component
const { code: componentCode, name: componentName } = extractComponentCode(sanitizedCode);
// Transform JSX to JavaScript using Babel
const transformedCode = Babel.transform(componentCode, {
presets: ['react']
}).code;
// Create a safe execution context
const React = window.React;
const { useState, useEffect, useCallback } = React;
// Execute the transformed code to get the component
const componentFunction = new Function('React', 'useState', 'useEffect', 'useCallback', `
${transformedCode}
return ${componentName};
`)(React, useState, useEffect, useCallback);
// Create root and render
const container = document.createElement('div');
previewArea.appendChild(container);
currentRoot = ReactDOM.createRoot(container);
currentRoot.render(React.createElement(componentFunction));
// Update code view
document.getElementById('codeContent').textContent = componentCode;
currentCode = componentCode;
} catch (error) {
console.error('Render error:', error);
previewArea.innerHTML = `
<div class="text-red-600 text-center">
<p class="font-bold mb-2">⚠️ Rendering Error</p>
<p class="text-sm">${error.message}</p>
<p class="text-xs mt-2 text-gray-500">The generated code might have syntax errors.</p>
</div>
`;
}
}
// Generate component using Hugging Face API
async function generateComponent() {
const promptInput = document.getElementById('promptInput');
const generateBtn = document.getElementById('generateBtn');
const loadingIndicator = document.getElementById('loadingIndicator');
const errorMessage = document.getElementById('errorMessage');
const userPrompt = promptInput.value.trim();
if (!userPrompt) {
showError('Please enter a component description!');
return;
}
// Show loading state
generateBtn.disabled = true;
loadingIndicator.classList.remove('hidden');
errorMessage.classList.add('hidden');
try {
// Call backend API
const response = await fetch('http://localhost:3000/api/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt: userPrompt })
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
// Render the generated component
renderComponent(data.code);
} catch (error) {
console.error('Generation error:', error);
showError(`Failed to generate component: ${error.message}`);
} finally {
// Hide loading state
generateBtn.disabled = false;
loadingIndicator.classList.add('hidden');
}
}
// Show error message
function showError(message) {
const errorMessage = document.getElementById('errorMessage');
errorMessage.textContent = message;
errorMessage.classList.remove('hidden');
// Auto-hide after 5 seconds
setTimeout(() => {
errorMessage.classList.add('hidden');
}, 5000);
}
// Allow Enter key to generate (Ctrl+Enter or Cmd+Enter)
document.addEventListener('DOMContentLoaded', () => {
const promptInput = document.getElementById('promptInput');
promptInput.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
generateComponent();
}
});
});