@aquiles-ai/renderize 2.0.0 → 2.1.0

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.
package/README.md CHANGED
@@ -68,6 +68,22 @@ export default function Playground() {
68
68
  }
69
69
  ```
70
70
 
71
+ <div align="center">
72
+
73
+ ## Example of integration
74
+
75
+ <img src="https://res.cloudinary.com/dmtomxyvm/image/upload/v1772990544/Captura_de_pantalla_2157_ziyjnu.png" alt="Example1" width="600"/>
76
+
77
+ <br><br>
78
+
79
+ <img src="https://res.cloudinary.com/dmtomxyvm/image/upload/v1772990545/Captura_de_pantalla_2158_b3e1up.png" alt="Example2" width="600"/>
80
+
81
+ <br><br>
82
+
83
+ <img src="https://res.cloudinary.com/dmtomxyvm/image/upload/v1772990556/Captura_de_pantalla_2159_azlagy.png" alt="Example3" width="600"/>
84
+
85
+ </div>
86
+
71
87
  ## Props
72
88
 
73
89
  | Prop | Type | Default | Description |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aquiles-ai/renderize",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Drop-in sandbox component that executes AI-generated React code with zero configuration.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/dist/index.cjs DELETED
@@ -1,450 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- Renderize: () => Renderize
24
- });
25
- module.exports = __toCommonJS(index_exports);
26
-
27
- // src/Renderize.tsx
28
- var import_react = require("react");
29
-
30
- // src/template.ts
31
- function buildTemplate(code) {
32
- return `<!DOCTYPE html>
33
- <html lang="en">
34
- <head>
35
- <meta charset="UTF-8" />
36
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
37
-
38
- <script src="https://cdn.tailwindcss.com"></script>
39
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
40
-
41
- <script type="importmap">
42
- {
43
- "imports": {
44
- "react": "https://esm.sh/react@18",
45
- "react/jsx-runtime": "https://esm.sh/react@18/jsx-runtime",
46
- "react-dom": "https://esm.sh/react-dom@18",
47
- "react-dom/client": "https://esm.sh/react-dom@18/client",
48
- "lucide-react": "https://esm.sh/lucide-react?external=react",
49
- "clsx": "https://esm.sh/clsx",
50
- "class-variance-authority": "https://esm.sh/class-variance-authority",
51
- "tailwind-merge": "https://esm.sh/tailwind-merge",
52
- "@radix-ui/react-accordion": "https://esm.sh/@radix-ui/react-accordion?external=react,react-dom",
53
- "@radix-ui/react-alert-dialog": "https://esm.sh/@radix-ui/react-alert-dialog?external=react,react-dom",
54
- "@radix-ui/react-avatar": "https://esm.sh/@radix-ui/react-avatar?external=react,react-dom",
55
- "@radix-ui/react-checkbox": "https://esm.sh/@radix-ui/react-checkbox?external=react,react-dom",
56
- "@radix-ui/react-collapsible": "https://esm.sh/@radix-ui/react-collapsible?external=react,react-dom",
57
- "@radix-ui/react-context-menu": "https://esm.sh/@radix-ui/react-context-menu?external=react,react-dom",
58
- "@radix-ui/react-dialog": "https://esm.sh/@radix-ui/react-dialog?external=react,react-dom",
59
- "@radix-ui/react-dropdown-menu": "https://esm.sh/@radix-ui/react-dropdown-menu?external=react,react-dom",
60
- "@radix-ui/react-hover-card": "https://esm.sh/@radix-ui/react-hover-card?external=react,react-dom",
61
- "@radix-ui/react-label": "https://esm.sh/@radix-ui/react-label?external=react,react-dom",
62
- "@radix-ui/react-menubar": "https://esm.sh/@radix-ui/react-menubar?external=react,react-dom",
63
- "@radix-ui/react-navigation-menu": "https://esm.sh/@radix-ui/react-navigation-menu?external=react,react-dom",
64
- "@radix-ui/react-popover": "https://esm.sh/@radix-ui/react-popover?external=react,react-dom",
65
- "@radix-ui/react-progress": "https://esm.sh/@radix-ui/react-progress?external=react,react-dom",
66
- "@radix-ui/react-radio-group": "https://esm.sh/@radix-ui/react-radio-group?external=react,react-dom",
67
- "@radix-ui/react-scroll-area": "https://esm.sh/@radix-ui/react-scroll-area?external=react,react-dom",
68
- "@radix-ui/react-select": "https://esm.sh/@radix-ui/react-select?external=react,react-dom",
69
- "@radix-ui/react-separator": "https://esm.sh/@radix-ui/react-separator?external=react,react-dom",
70
- "@radix-ui/react-slider": "https://esm.sh/@radix-ui/react-slider?external=react,react-dom",
71
- "@radix-ui/react-slot": "https://esm.sh/@radix-ui/react-slot?external=react,react-dom",
72
- "@radix-ui/react-switch": "https://esm.sh/@radix-ui/react-switch?external=react,react-dom",
73
- "@radix-ui/react-tabs": "https://esm.sh/@radix-ui/react-tabs?external=react,react-dom",
74
- "@radix-ui/react-toast": "https://esm.sh/@radix-ui/react-toast?external=react,react-dom",
75
- "@radix-ui/react-toggle": "https://esm.sh/@radix-ui/react-toggle?external=react,react-dom",
76
- "@radix-ui/react-toggle-group": "https://esm.sh/@radix-ui/react-toggle-group?external=react,react-dom",
77
- "@radix-ui/react-toolbar": "https://esm.sh/@radix-ui/react-toolbar?external=react,react-dom",
78
- "@radix-ui/react-tooltip": "https://esm.sh/@radix-ui/react-tooltip?external=react,react-dom"
79
- }
80
- }
81
- </script>
82
-
83
- <style>
84
- * { box-sizing: border-box; }
85
- body { margin: 0; padding: 0; }
86
-
87
- /* Scrollbar personalizada: fina y semi-transparente */
88
- ::-webkit-scrollbar {
89
- width: 6px;
90
- height: 6px;
91
- }
92
- ::-webkit-scrollbar-track {
93
- background: transparent;
94
- }
95
- ::-webkit-scrollbar-thumb {
96
- background: rgba(255, 255, 255, 0.35);
97
- border-radius: 999px;
98
- transition: background 0.2s ease;
99
- }
100
- ::-webkit-scrollbar-thumb:hover {
101
- background: rgba(255, 255, 255, 0.55);
102
- }
103
- /* Firefox */
104
- * {
105
- scrollbar-width: thin;
106
- scrollbar-color: rgba(255, 255, 255, 0.35) transparent;
107
- }
108
- </style>
109
- </head>
110
- <body>
111
- <div id="root"></div>
112
-
113
- <script>
114
- // \u2500\u2500 FETCH PROXY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
115
- // Override window.fetch to proxy requests through the parent window.
116
- // This solves the CORS/null-origin issue with srcdoc iframes:
117
- // the parent has a real origin and can make fetch calls freely.
118
- window.fetch = function(url, options = {}) {
119
- return new Promise((resolve, reject) => {
120
- const id = Math.random().toString(36).slice(2) + Date.now().toString(36);
121
-
122
- // Serialize body \u2014 postMessage can't transfer Request objects
123
- const serializedOptions = {
124
- method: options.method || "GET",
125
- headers: options.headers || {},
126
- body: options.body || null,
127
- };
128
-
129
- // Listen for the response from the parent
130
- function handleMessage(event) {
131
- if (
132
- event.data?.source !== "renderize" ||
133
- event.data?.type !== "fetch-response" ||
134
- event.data?.id !== id
135
- ) return;
136
-
137
- window.removeEventListener("message", handleMessage);
138
-
139
- if (event.data.error) {
140
- reject(new Error(event.data.error));
141
- return;
142
- }
143
-
144
- // Reconstruct a real Response object from the serialized data
145
- const { status, statusText, headers, body } = event.data;
146
- const responseBody = typeof body === "string" ? body : JSON.stringify(body);
147
-
148
- const response = new Response(responseBody, {
149
- status,
150
- statusText,
151
- headers: new Headers(headers),
152
- });
153
-
154
- resolve(response);
155
- }
156
-
157
- window.addEventListener("message", handleMessage);
158
-
159
- // Ask the parent to perform the fetch on our behalf
160
- window.parent.postMessage({
161
- source: "renderize",
162
- type: "fetch-request",
163
- id,
164
- url,
165
- options: serializedOptions,
166
- }, "*");
167
- });
168
- };
169
- // \u2500\u2500 END FETCH PROXY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
170
- </script>
171
-
172
- <script type="text/babel" data-type="module">
173
- import React, {
174
- useState, useEffect, useRef, useCallback,
175
- useMemo, useReducer, useContext, createContext,
176
- forwardRef, Fragment
177
- } from "react";
178
- import { createRoot } from "react-dom/client";
179
-
180
- // \u2500\u2500 USER CODE START \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
181
- ${code}
182
- // \u2500\u2500 USER CODE END \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
183
-
184
- const container = document.getElementById("root");
185
- createRoot(container).render(React.createElement(App));
186
- </script>
187
- </body>
188
- </html>`;
189
- }
190
-
191
- // src/sanitize.ts
192
- var TEMPLATE_PROVIDED_MODULES = /* @__PURE__ */ new Set([
193
- "react",
194
- "react-dom",
195
- "react-dom/client",
196
- "react/jsx-runtime"
197
- ]);
198
- var TEMPLATE_PROVIDED_NAMES = /* @__PURE__ */ new Set([
199
- "React",
200
- "useState",
201
- "useEffect",
202
- "useRef",
203
- "useCallback",
204
- "useMemo",
205
- "useReducer",
206
- "useContext",
207
- "createContext",
208
- "forwardRef",
209
- "Fragment",
210
- "createRoot"
211
- ]);
212
- var IMPORTMAP_MODULES = /* @__PURE__ */ new Set([
213
- "react",
214
- "react/jsx-runtime",
215
- "react-dom",
216
- "react-dom/client",
217
- "lucide-react",
218
- "clsx",
219
- "class-variance-authority",
220
- "tailwind-merge",
221
- "@radix-ui/react-accordion",
222
- "@radix-ui/react-alert-dialog",
223
- "@radix-ui/react-avatar",
224
- "@radix-ui/react-checkbox",
225
- "@radix-ui/react-collapsible",
226
- "@radix-ui/react-context-menu",
227
- "@radix-ui/react-dialog",
228
- "@radix-ui/react-dropdown-menu",
229
- "@radix-ui/react-hover-card",
230
- "@radix-ui/react-label",
231
- "@radix-ui/react-menubar",
232
- "@radix-ui/react-navigation-menu",
233
- "@radix-ui/react-popover",
234
- "@radix-ui/react-progress",
235
- "@radix-ui/react-radio-group",
236
- "@radix-ui/react-scroll-area",
237
- "@radix-ui/react-select",
238
- "@radix-ui/react-separator",
239
- "@radix-ui/react-slider",
240
- "@radix-ui/react-slot",
241
- "@radix-ui/react-switch",
242
- "@radix-ui/react-tabs",
243
- "@radix-ui/react-toast",
244
- "@radix-ui/react-toggle",
245
- "@radix-ui/react-toggle-group",
246
- "@radix-ui/react-toolbar",
247
- "@radix-ui/react-tooltip"
248
- ]);
249
- function findMatchingBrace(code, openIndex) {
250
- let depth = 0;
251
- let inSingle = false;
252
- let inDouble = false;
253
- let inTemplate = 0;
254
- for (let i = openIndex; i < code.length; i++) {
255
- const ch = code[i];
256
- const prev = i > 0 ? code[i - 1] : "";
257
- if (prev === "\\") continue;
258
- if (!inDouble && !inTemplate && ch === "'") {
259
- inSingle = !inSingle;
260
- continue;
261
- }
262
- if (!inSingle && !inTemplate && ch === '"') {
263
- inDouble = !inDouble;
264
- continue;
265
- }
266
- if (!inSingle && !inDouble && ch === "`") {
267
- inTemplate = inTemplate ? inTemplate - 1 : inTemplate + 1;
268
- continue;
269
- }
270
- if (inSingle || inDouble || inTemplate) continue;
271
- if (ch === "{") depth++;
272
- else if (ch === "}") {
273
- depth--;
274
- if (depth === 0) return i + 1;
275
- }
276
- }
277
- return -1;
278
- }
279
- function stripTypeScriptDeclarations(code) {
280
- const blockKeywords = /(?:export\s+)?(?:interface|enum)\s+\w[\w<,\s>]*\s*\{/g;
281
- let match;
282
- while ((match = blockKeywords.exec(code)) !== null) {
283
- const openBrace = code.indexOf("{", match.index + match[0].length - 1);
284
- if (openBrace === -1) continue;
285
- const end = findMatchingBrace(code, openBrace);
286
- if (end === -1) continue;
287
- const tail = code[end] === ";" ? end + 1 : end;
288
- code = code.slice(0, match.index) + code.slice(tail);
289
- blockKeywords.lastIndex = match.index;
290
- }
291
- code = code.replace(
292
- /^(?:export\s+)?type\s+\w[\w<,\s>]*\s*=\s*/gm,
293
- (_header, offset, fullCode) => {
294
- const rest = fullCode.slice(offset + _header.length);
295
- const trimmed = rest.trimStart();
296
- if (trimmed.startsWith("{")) {
297
- const relOpen = rest.indexOf("{");
298
- const end = findMatchingBrace(rest, relOpen);
299
- if (end !== -1) {
300
- code = fullCode.slice(0, offset) + fullCode.slice(offset + _header.length + end);
301
- }
302
- }
303
- return "";
304
- }
305
- );
306
- return code;
307
- }
308
- function stripAsAssertions(code) {
309
- return code.replace(
310
- /(?<![{,]\s*\w+)\s+as\s+[A-Z]\w*(?:<[^>]*>)?(?:\[\])?(?=\s*[),;}\n])/g,
311
- ""
312
- );
313
- }
314
- function detectMainComponentName(code) {
315
- const fnExport = code.match(/export\s+default\s+function\s+([A-Z]\w*)/);
316
- if (fnExport && fnExport[1] !== "App") return fnExport[1];
317
- const constExport = code.match(/export\s+default\s+(?:const|let)\s+([A-Z]\w*)/);
318
- if (constExport && constExport[1] !== "App") return constExport[1];
319
- const allDefs = [
320
- ...code.matchAll(
321
- /^(?:function|const|let)\s+([A-Z]\w*)\s*(?:=\s*(?:\(|React\.memo\()|[\(<(])/gm
322
- )
323
- ].map((m) => m[1]).filter((n) => n !== "App");
324
- if (allDefs.length === 1) return allDefs[0];
325
- return null;
326
- }
327
- function sanitizeCode(raw) {
328
- let code = raw;
329
- code = code.replace(/^```[a-zA-Z]*\r?\n?/, "").replace(/\r?\n?```\s*$/, "");
330
- code = code.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r");
331
- code = code.replace(/^\s*['"]use (client|server)['"]\s*;?\s*\n?/gm, "");
332
- code = stripTypeScriptDeclarations(code);
333
- code = stripAsAssertions(code);
334
- const originalName = detectMainComponentName(code);
335
- if (originalName) {
336
- code = code.replace(new RegExp(`\\b${originalName}\\b`, "g"), "App");
337
- }
338
- code = code.replace(/\bexport\s+default\s+(function\s+App\b)/, "$1");
339
- code = code.replace(/\bexport\s+default\s+((?:const|let)\s+App\b)/, "$1");
340
- code = code.replace(/\bexport\s+(function\s+App\b)/, "$1");
341
- code = code.replace(/\bexport\s+((?:const|let)\s+App\b)/, "$1");
342
- const importLineRegex = /^import\s+(?:type\s+)?(?:[^"'\n]+\s+from\s+)?["']([^"']+)["'];?\s*$/gm;
343
- code = code.replace(importLineRegex, (line, modulePath) => {
344
- if (TEMPLATE_PROVIDED_MODULES.has(modulePath)) return "";
345
- if (!IMPORTMAP_MODULES.has(modulePath)) return "";
346
- const namedMatch = line.match(/\{([^}]+)\}/);
347
- if (namedMatch) {
348
- const names = namedMatch[1].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean);
349
- if (names.length > 0 && names.every((n) => TEMPLATE_PROVIDED_NAMES.has(n))) {
350
- return "";
351
- }
352
- }
353
- return line;
354
- });
355
- code = code.replace(
356
- /^(?:const|let|var)\s+[\w\s,{}]+\s*=\s*require\s*\(["'][^"']+["']\)\s*;?\s*$/gm,
357
- ""
358
- );
359
- code = code.replace(/\n{3,}/g, "\n\n");
360
- return code.trim();
361
- }
362
-
363
- // src/Renderize.tsx
364
- var import_jsx_runtime = require("react/jsx-runtime");
365
- function Renderize({
366
- code,
367
- height = "100%",
368
- width = "100%",
369
- className,
370
- style,
371
- onError
372
- }) {
373
- const iframeRef = (0, import_react.useRef)(null);
374
- const [srcDoc, setSrcDoc] = (0, import_react.useState)(null);
375
- (0, import_react.useEffect)(() => {
376
- if (!code?.trim()) return;
377
- setSrcDoc(buildTemplate(sanitizeCode(code)));
378
- }, [code]);
379
- (0, import_react.useEffect)(() => {
380
- const handler = async (event) => {
381
- if (event.data?.source !== "renderize") return;
382
- if (event.data.type === "fetch-request") {
383
- const { id, url, options } = event.data;
384
- try {
385
- const res = await fetch(url, {
386
- method: options.method,
387
- headers: options.headers,
388
- body: options.body ?? void 0
389
- });
390
- const body = await res.text();
391
- iframeRef.current?.contentWindow?.postMessage(
392
- {
393
- source: "renderize",
394
- type: "fetch-response",
395
- id,
396
- status: res.status,
397
- statusText: res.statusText,
398
- // Convert Headers to a plain object for structured clone
399
- headers: Object.fromEntries(res.headers.entries()),
400
- body
401
- },
402
- "*"
403
- );
404
- } catch (err) {
405
- iframeRef.current?.contentWindow?.postMessage(
406
- {
407
- source: "renderize",
408
- type: "fetch-response",
409
- id,
410
- error: err instanceof Error ? err.message : String(err)
411
- },
412
- "*"
413
- );
414
- }
415
- }
416
- if (event.data.type === "error" && onError) {
417
- onError(event.data.message);
418
- }
419
- };
420
- window.addEventListener("message", handler);
421
- return () => window.removeEventListener("message", handler);
422
- }, [onError]);
423
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
424
- "div",
425
- {
426
- className,
427
- style: { width, height, overflow: "hidden", ...style },
428
- children: srcDoc && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
429
- "iframe",
430
- {
431
- ref: iframeRef,
432
- srcDoc,
433
- title: "Renderize Sandbox",
434
- sandbox: "allow-scripts allow-forms allow-modals allow-popups allow-downloads",
435
- style: {
436
- width: "100%",
437
- height: "100%",
438
- border: "none",
439
- display: "block"
440
- }
441
- },
442
- srcDoc
443
- )
444
- }
445
- );
446
- }
447
- // Annotate the CommonJS export names for ESM import in node:
448
- 0 && (module.exports = {
449
- Renderize
450
- });
package/dist/index.d.cts DELETED
@@ -1,20 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React from 'react';
3
-
4
- interface RenderizeProps {
5
- /** React code generated by the LLM. Must define a function component named App. */
6
- code: string;
7
- /** Height of the sandbox iframe. Defaults to "100%" */
8
- height?: string | number;
9
- /** Width of the sandbox iframe. Defaults to "100%" */
10
- width?: string | number;
11
- /** Custom class name for the wrapper element */
12
- className?: string;
13
- /** Custom inline styles for the wrapper element */
14
- style?: React.CSSProperties;
15
- /** Called when the sandbox encounters a runtime error */
16
- onError?: (error: string) => void;
17
- }
18
- declare function Renderize({ code, height, width, className, style, onError, }: RenderizeProps): react_jsx_runtime.JSX.Element;
19
-
20
- export { Renderize, type RenderizeProps };
package/dist/index.d.ts DELETED
@@ -1,20 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React from 'react';
3
-
4
- interface RenderizeProps {
5
- /** React code generated by the LLM. Must define a function component named App. */
6
- code: string;
7
- /** Height of the sandbox iframe. Defaults to "100%" */
8
- height?: string | number;
9
- /** Width of the sandbox iframe. Defaults to "100%" */
10
- width?: string | number;
11
- /** Custom class name for the wrapper element */
12
- className?: string;
13
- /** Custom inline styles for the wrapper element */
14
- style?: React.CSSProperties;
15
- /** Called when the sandbox encounters a runtime error */
16
- onError?: (error: string) => void;
17
- }
18
- declare function Renderize({ code, height, width, className, style, onError, }: RenderizeProps): react_jsx_runtime.JSX.Element;
19
-
20
- export { Renderize, type RenderizeProps };
package/dist/index.js DELETED
@@ -1,423 +0,0 @@
1
- // src/Renderize.tsx
2
- import { useEffect, useRef, useState } from "react";
3
-
4
- // src/template.ts
5
- function buildTemplate(code) {
6
- return `<!DOCTYPE html>
7
- <html lang="en">
8
- <head>
9
- <meta charset="UTF-8" />
10
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
11
-
12
- <script src="https://cdn.tailwindcss.com"></script>
13
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
14
-
15
- <script type="importmap">
16
- {
17
- "imports": {
18
- "react": "https://esm.sh/react@18",
19
- "react/jsx-runtime": "https://esm.sh/react@18/jsx-runtime",
20
- "react-dom": "https://esm.sh/react-dom@18",
21
- "react-dom/client": "https://esm.sh/react-dom@18/client",
22
- "lucide-react": "https://esm.sh/lucide-react?external=react",
23
- "clsx": "https://esm.sh/clsx",
24
- "class-variance-authority": "https://esm.sh/class-variance-authority",
25
- "tailwind-merge": "https://esm.sh/tailwind-merge",
26
- "@radix-ui/react-accordion": "https://esm.sh/@radix-ui/react-accordion?external=react,react-dom",
27
- "@radix-ui/react-alert-dialog": "https://esm.sh/@radix-ui/react-alert-dialog?external=react,react-dom",
28
- "@radix-ui/react-avatar": "https://esm.sh/@radix-ui/react-avatar?external=react,react-dom",
29
- "@radix-ui/react-checkbox": "https://esm.sh/@radix-ui/react-checkbox?external=react,react-dom",
30
- "@radix-ui/react-collapsible": "https://esm.sh/@radix-ui/react-collapsible?external=react,react-dom",
31
- "@radix-ui/react-context-menu": "https://esm.sh/@radix-ui/react-context-menu?external=react,react-dom",
32
- "@radix-ui/react-dialog": "https://esm.sh/@radix-ui/react-dialog?external=react,react-dom",
33
- "@radix-ui/react-dropdown-menu": "https://esm.sh/@radix-ui/react-dropdown-menu?external=react,react-dom",
34
- "@radix-ui/react-hover-card": "https://esm.sh/@radix-ui/react-hover-card?external=react,react-dom",
35
- "@radix-ui/react-label": "https://esm.sh/@radix-ui/react-label?external=react,react-dom",
36
- "@radix-ui/react-menubar": "https://esm.sh/@radix-ui/react-menubar?external=react,react-dom",
37
- "@radix-ui/react-navigation-menu": "https://esm.sh/@radix-ui/react-navigation-menu?external=react,react-dom",
38
- "@radix-ui/react-popover": "https://esm.sh/@radix-ui/react-popover?external=react,react-dom",
39
- "@radix-ui/react-progress": "https://esm.sh/@radix-ui/react-progress?external=react,react-dom",
40
- "@radix-ui/react-radio-group": "https://esm.sh/@radix-ui/react-radio-group?external=react,react-dom",
41
- "@radix-ui/react-scroll-area": "https://esm.sh/@radix-ui/react-scroll-area?external=react,react-dom",
42
- "@radix-ui/react-select": "https://esm.sh/@radix-ui/react-select?external=react,react-dom",
43
- "@radix-ui/react-separator": "https://esm.sh/@radix-ui/react-separator?external=react,react-dom",
44
- "@radix-ui/react-slider": "https://esm.sh/@radix-ui/react-slider?external=react,react-dom",
45
- "@radix-ui/react-slot": "https://esm.sh/@radix-ui/react-slot?external=react,react-dom",
46
- "@radix-ui/react-switch": "https://esm.sh/@radix-ui/react-switch?external=react,react-dom",
47
- "@radix-ui/react-tabs": "https://esm.sh/@radix-ui/react-tabs?external=react,react-dom",
48
- "@radix-ui/react-toast": "https://esm.sh/@radix-ui/react-toast?external=react,react-dom",
49
- "@radix-ui/react-toggle": "https://esm.sh/@radix-ui/react-toggle?external=react,react-dom",
50
- "@radix-ui/react-toggle-group": "https://esm.sh/@radix-ui/react-toggle-group?external=react,react-dom",
51
- "@radix-ui/react-toolbar": "https://esm.sh/@radix-ui/react-toolbar?external=react,react-dom",
52
- "@radix-ui/react-tooltip": "https://esm.sh/@radix-ui/react-tooltip?external=react,react-dom"
53
- }
54
- }
55
- </script>
56
-
57
- <style>
58
- * { box-sizing: border-box; }
59
- body { margin: 0; padding: 0; }
60
-
61
- /* Scrollbar personalizada: fina y semi-transparente */
62
- ::-webkit-scrollbar {
63
- width: 6px;
64
- height: 6px;
65
- }
66
- ::-webkit-scrollbar-track {
67
- background: transparent;
68
- }
69
- ::-webkit-scrollbar-thumb {
70
- background: rgba(255, 255, 255, 0.35);
71
- border-radius: 999px;
72
- transition: background 0.2s ease;
73
- }
74
- ::-webkit-scrollbar-thumb:hover {
75
- background: rgba(255, 255, 255, 0.55);
76
- }
77
- /* Firefox */
78
- * {
79
- scrollbar-width: thin;
80
- scrollbar-color: rgba(255, 255, 255, 0.35) transparent;
81
- }
82
- </style>
83
- </head>
84
- <body>
85
- <div id="root"></div>
86
-
87
- <script>
88
- // \u2500\u2500 FETCH PROXY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
89
- // Override window.fetch to proxy requests through the parent window.
90
- // This solves the CORS/null-origin issue with srcdoc iframes:
91
- // the parent has a real origin and can make fetch calls freely.
92
- window.fetch = function(url, options = {}) {
93
- return new Promise((resolve, reject) => {
94
- const id = Math.random().toString(36).slice(2) + Date.now().toString(36);
95
-
96
- // Serialize body \u2014 postMessage can't transfer Request objects
97
- const serializedOptions = {
98
- method: options.method || "GET",
99
- headers: options.headers || {},
100
- body: options.body || null,
101
- };
102
-
103
- // Listen for the response from the parent
104
- function handleMessage(event) {
105
- if (
106
- event.data?.source !== "renderize" ||
107
- event.data?.type !== "fetch-response" ||
108
- event.data?.id !== id
109
- ) return;
110
-
111
- window.removeEventListener("message", handleMessage);
112
-
113
- if (event.data.error) {
114
- reject(new Error(event.data.error));
115
- return;
116
- }
117
-
118
- // Reconstruct a real Response object from the serialized data
119
- const { status, statusText, headers, body } = event.data;
120
- const responseBody = typeof body === "string" ? body : JSON.stringify(body);
121
-
122
- const response = new Response(responseBody, {
123
- status,
124
- statusText,
125
- headers: new Headers(headers),
126
- });
127
-
128
- resolve(response);
129
- }
130
-
131
- window.addEventListener("message", handleMessage);
132
-
133
- // Ask the parent to perform the fetch on our behalf
134
- window.parent.postMessage({
135
- source: "renderize",
136
- type: "fetch-request",
137
- id,
138
- url,
139
- options: serializedOptions,
140
- }, "*");
141
- });
142
- };
143
- // \u2500\u2500 END FETCH PROXY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
144
- </script>
145
-
146
- <script type="text/babel" data-type="module">
147
- import React, {
148
- useState, useEffect, useRef, useCallback,
149
- useMemo, useReducer, useContext, createContext,
150
- forwardRef, Fragment
151
- } from "react";
152
- import { createRoot } from "react-dom/client";
153
-
154
- // \u2500\u2500 USER CODE START \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
155
- ${code}
156
- // \u2500\u2500 USER CODE END \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
157
-
158
- const container = document.getElementById("root");
159
- createRoot(container).render(React.createElement(App));
160
- </script>
161
- </body>
162
- </html>`;
163
- }
164
-
165
- // src/sanitize.ts
166
- var TEMPLATE_PROVIDED_MODULES = /* @__PURE__ */ new Set([
167
- "react",
168
- "react-dom",
169
- "react-dom/client",
170
- "react/jsx-runtime"
171
- ]);
172
- var TEMPLATE_PROVIDED_NAMES = /* @__PURE__ */ new Set([
173
- "React",
174
- "useState",
175
- "useEffect",
176
- "useRef",
177
- "useCallback",
178
- "useMemo",
179
- "useReducer",
180
- "useContext",
181
- "createContext",
182
- "forwardRef",
183
- "Fragment",
184
- "createRoot"
185
- ]);
186
- var IMPORTMAP_MODULES = /* @__PURE__ */ new Set([
187
- "react",
188
- "react/jsx-runtime",
189
- "react-dom",
190
- "react-dom/client",
191
- "lucide-react",
192
- "clsx",
193
- "class-variance-authority",
194
- "tailwind-merge",
195
- "@radix-ui/react-accordion",
196
- "@radix-ui/react-alert-dialog",
197
- "@radix-ui/react-avatar",
198
- "@radix-ui/react-checkbox",
199
- "@radix-ui/react-collapsible",
200
- "@radix-ui/react-context-menu",
201
- "@radix-ui/react-dialog",
202
- "@radix-ui/react-dropdown-menu",
203
- "@radix-ui/react-hover-card",
204
- "@radix-ui/react-label",
205
- "@radix-ui/react-menubar",
206
- "@radix-ui/react-navigation-menu",
207
- "@radix-ui/react-popover",
208
- "@radix-ui/react-progress",
209
- "@radix-ui/react-radio-group",
210
- "@radix-ui/react-scroll-area",
211
- "@radix-ui/react-select",
212
- "@radix-ui/react-separator",
213
- "@radix-ui/react-slider",
214
- "@radix-ui/react-slot",
215
- "@radix-ui/react-switch",
216
- "@radix-ui/react-tabs",
217
- "@radix-ui/react-toast",
218
- "@radix-ui/react-toggle",
219
- "@radix-ui/react-toggle-group",
220
- "@radix-ui/react-toolbar",
221
- "@radix-ui/react-tooltip"
222
- ]);
223
- function findMatchingBrace(code, openIndex) {
224
- let depth = 0;
225
- let inSingle = false;
226
- let inDouble = false;
227
- let inTemplate = 0;
228
- for (let i = openIndex; i < code.length; i++) {
229
- const ch = code[i];
230
- const prev = i > 0 ? code[i - 1] : "";
231
- if (prev === "\\") continue;
232
- if (!inDouble && !inTemplate && ch === "'") {
233
- inSingle = !inSingle;
234
- continue;
235
- }
236
- if (!inSingle && !inTemplate && ch === '"') {
237
- inDouble = !inDouble;
238
- continue;
239
- }
240
- if (!inSingle && !inDouble && ch === "`") {
241
- inTemplate = inTemplate ? inTemplate - 1 : inTemplate + 1;
242
- continue;
243
- }
244
- if (inSingle || inDouble || inTemplate) continue;
245
- if (ch === "{") depth++;
246
- else if (ch === "}") {
247
- depth--;
248
- if (depth === 0) return i + 1;
249
- }
250
- }
251
- return -1;
252
- }
253
- function stripTypeScriptDeclarations(code) {
254
- const blockKeywords = /(?:export\s+)?(?:interface|enum)\s+\w[\w<,\s>]*\s*\{/g;
255
- let match;
256
- while ((match = blockKeywords.exec(code)) !== null) {
257
- const openBrace = code.indexOf("{", match.index + match[0].length - 1);
258
- if (openBrace === -1) continue;
259
- const end = findMatchingBrace(code, openBrace);
260
- if (end === -1) continue;
261
- const tail = code[end] === ";" ? end + 1 : end;
262
- code = code.slice(0, match.index) + code.slice(tail);
263
- blockKeywords.lastIndex = match.index;
264
- }
265
- code = code.replace(
266
- /^(?:export\s+)?type\s+\w[\w<,\s>]*\s*=\s*/gm,
267
- (_header, offset, fullCode) => {
268
- const rest = fullCode.slice(offset + _header.length);
269
- const trimmed = rest.trimStart();
270
- if (trimmed.startsWith("{")) {
271
- const relOpen = rest.indexOf("{");
272
- const end = findMatchingBrace(rest, relOpen);
273
- if (end !== -1) {
274
- code = fullCode.slice(0, offset) + fullCode.slice(offset + _header.length + end);
275
- }
276
- }
277
- return "";
278
- }
279
- );
280
- return code;
281
- }
282
- function stripAsAssertions(code) {
283
- return code.replace(
284
- /(?<![{,]\s*\w+)\s+as\s+[A-Z]\w*(?:<[^>]*>)?(?:\[\])?(?=\s*[),;}\n])/g,
285
- ""
286
- );
287
- }
288
- function detectMainComponentName(code) {
289
- const fnExport = code.match(/export\s+default\s+function\s+([A-Z]\w*)/);
290
- if (fnExport && fnExport[1] !== "App") return fnExport[1];
291
- const constExport = code.match(/export\s+default\s+(?:const|let)\s+([A-Z]\w*)/);
292
- if (constExport && constExport[1] !== "App") return constExport[1];
293
- const allDefs = [
294
- ...code.matchAll(
295
- /^(?:function|const|let)\s+([A-Z]\w*)\s*(?:=\s*(?:\(|React\.memo\()|[\(<(])/gm
296
- )
297
- ].map((m) => m[1]).filter((n) => n !== "App");
298
- if (allDefs.length === 1) return allDefs[0];
299
- return null;
300
- }
301
- function sanitizeCode(raw) {
302
- let code = raw;
303
- code = code.replace(/^```[a-zA-Z]*\r?\n?/, "").replace(/\r?\n?```\s*$/, "");
304
- code = code.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\r/g, "\r");
305
- code = code.replace(/^\s*['"]use (client|server)['"]\s*;?\s*\n?/gm, "");
306
- code = stripTypeScriptDeclarations(code);
307
- code = stripAsAssertions(code);
308
- const originalName = detectMainComponentName(code);
309
- if (originalName) {
310
- code = code.replace(new RegExp(`\\b${originalName}\\b`, "g"), "App");
311
- }
312
- code = code.replace(/\bexport\s+default\s+(function\s+App\b)/, "$1");
313
- code = code.replace(/\bexport\s+default\s+((?:const|let)\s+App\b)/, "$1");
314
- code = code.replace(/\bexport\s+(function\s+App\b)/, "$1");
315
- code = code.replace(/\bexport\s+((?:const|let)\s+App\b)/, "$1");
316
- const importLineRegex = /^import\s+(?:type\s+)?(?:[^"'\n]+\s+from\s+)?["']([^"']+)["'];?\s*$/gm;
317
- code = code.replace(importLineRegex, (line, modulePath) => {
318
- if (TEMPLATE_PROVIDED_MODULES.has(modulePath)) return "";
319
- if (!IMPORTMAP_MODULES.has(modulePath)) return "";
320
- const namedMatch = line.match(/\{([^}]+)\}/);
321
- if (namedMatch) {
322
- const names = namedMatch[1].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean);
323
- if (names.length > 0 && names.every((n) => TEMPLATE_PROVIDED_NAMES.has(n))) {
324
- return "";
325
- }
326
- }
327
- return line;
328
- });
329
- code = code.replace(
330
- /^(?:const|let|var)\s+[\w\s,{}]+\s*=\s*require\s*\(["'][^"']+["']\)\s*;?\s*$/gm,
331
- ""
332
- );
333
- code = code.replace(/\n{3,}/g, "\n\n");
334
- return code.trim();
335
- }
336
-
337
- // src/Renderize.tsx
338
- import { jsx } from "react/jsx-runtime";
339
- function Renderize({
340
- code,
341
- height = "100%",
342
- width = "100%",
343
- className,
344
- style,
345
- onError
346
- }) {
347
- const iframeRef = useRef(null);
348
- const [srcDoc, setSrcDoc] = useState(null);
349
- useEffect(() => {
350
- if (!code?.trim()) return;
351
- setSrcDoc(buildTemplate(sanitizeCode(code)));
352
- }, [code]);
353
- useEffect(() => {
354
- const handler = async (event) => {
355
- if (event.data?.source !== "renderize") return;
356
- if (event.data.type === "fetch-request") {
357
- const { id, url, options } = event.data;
358
- try {
359
- const res = await fetch(url, {
360
- method: options.method,
361
- headers: options.headers,
362
- body: options.body ?? void 0
363
- });
364
- const body = await res.text();
365
- iframeRef.current?.contentWindow?.postMessage(
366
- {
367
- source: "renderize",
368
- type: "fetch-response",
369
- id,
370
- status: res.status,
371
- statusText: res.statusText,
372
- // Convert Headers to a plain object for structured clone
373
- headers: Object.fromEntries(res.headers.entries()),
374
- body
375
- },
376
- "*"
377
- );
378
- } catch (err) {
379
- iframeRef.current?.contentWindow?.postMessage(
380
- {
381
- source: "renderize",
382
- type: "fetch-response",
383
- id,
384
- error: err instanceof Error ? err.message : String(err)
385
- },
386
- "*"
387
- );
388
- }
389
- }
390
- if (event.data.type === "error" && onError) {
391
- onError(event.data.message);
392
- }
393
- };
394
- window.addEventListener("message", handler);
395
- return () => window.removeEventListener("message", handler);
396
- }, [onError]);
397
- return /* @__PURE__ */ jsx(
398
- "div",
399
- {
400
- className,
401
- style: { width, height, overflow: "hidden", ...style },
402
- children: srcDoc && /* @__PURE__ */ jsx(
403
- "iframe",
404
- {
405
- ref: iframeRef,
406
- srcDoc,
407
- title: "Renderize Sandbox",
408
- sandbox: "allow-scripts allow-forms allow-modals allow-popups allow-downloads",
409
- style: {
410
- width: "100%",
411
- height: "100%",
412
- border: "none",
413
- display: "block"
414
- }
415
- },
416
- srcDoc
417
- )
418
- }
419
- );
420
- }
421
- export {
422
- Renderize
423
- };