@aquiles-ai/renderize 1.0.0 → 1.3.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/dist/index.cjs +178 -0
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +151 -0
- package/package.json +6 -3
- package/src/Renderize.tsx +28 -28
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
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 (
|
|
33
|
+
/* html */
|
|
34
|
+
`
|
|
35
|
+
<!DOCTYPE html>
|
|
36
|
+
<html lang="en">
|
|
37
|
+
<head>
|
|
38
|
+
<meta charset="UTF-8" />
|
|
39
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
40
|
+
|
|
41
|
+
<!-- Tailwind CSS Play CDN -->
|
|
42
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
43
|
+
|
|
44
|
+
<!-- Babel standalone: transpiles JSX at runtime -->
|
|
45
|
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
46
|
+
|
|
47
|
+
<!-- Import map: React + Radix UI + Lucide -->
|
|
48
|
+
<script type="importmap">
|
|
49
|
+
{
|
|
50
|
+
"imports": {
|
|
51
|
+
"react": "https://esm.sh/react@18",
|
|
52
|
+
"react/jsx-runtime": "https://esm.sh/react@18/jsx-runtime",
|
|
53
|
+
"react-dom": "https://esm.sh/react-dom@18",
|
|
54
|
+
"react-dom/client": "https://esm.sh/react-dom@18/client",
|
|
55
|
+
"lucide-react": "https://esm.sh/lucide-react@latest",
|
|
56
|
+
"clsx": "https://esm.sh/clsx",
|
|
57
|
+
"class-variance-authority": "https://esm.sh/class-variance-authority",
|
|
58
|
+
"tailwind-merge": "https://esm.sh/tailwind-merge",
|
|
59
|
+
"@radix-ui/react-accordion": "https://esm.sh/@radix-ui/react-accordion",
|
|
60
|
+
"@radix-ui/react-alert-dialog": "https://esm.sh/@radix-ui/react-alert-dialog",
|
|
61
|
+
"@radix-ui/react-avatar": "https://esm.sh/@radix-ui/react-avatar",
|
|
62
|
+
"@radix-ui/react-checkbox": "https://esm.sh/@radix-ui/react-checkbox",
|
|
63
|
+
"@radix-ui/react-collapsible": "https://esm.sh/@radix-ui/react-collapsible",
|
|
64
|
+
"@radix-ui/react-context-menu": "https://esm.sh/@radix-ui/react-context-menu",
|
|
65
|
+
"@radix-ui/react-dialog": "https://esm.sh/@radix-ui/react-dialog",
|
|
66
|
+
"@radix-ui/react-dropdown-menu": "https://esm.sh/@radix-ui/react-dropdown-menu",
|
|
67
|
+
"@radix-ui/react-hover-card": "https://esm.sh/@radix-ui/react-hover-card",
|
|
68
|
+
"@radix-ui/react-label": "https://esm.sh/@radix-ui/react-label",
|
|
69
|
+
"@radix-ui/react-menubar": "https://esm.sh/@radix-ui/react-menubar",
|
|
70
|
+
"@radix-ui/react-navigation-menu": "https://esm.sh/@radix-ui/react-navigation-menu",
|
|
71
|
+
"@radix-ui/react-popover": "https://esm.sh/@radix-ui/react-popover",
|
|
72
|
+
"@radix-ui/react-progress": "https://esm.sh/@radix-ui/react-progress",
|
|
73
|
+
"@radix-ui/react-radio-group": "https://esm.sh/@radix-ui/react-radio-group",
|
|
74
|
+
"@radix-ui/react-scroll-area": "https://esm.sh/@radix-ui/react-scroll-area",
|
|
75
|
+
"@radix-ui/react-select": "https://esm.sh/@radix-ui/react-select",
|
|
76
|
+
"@radix-ui/react-separator": "https://esm.sh/@radix-ui/react-separator",
|
|
77
|
+
"@radix-ui/react-slider": "https://esm.sh/@radix-ui/react-slider",
|
|
78
|
+
"@radix-ui/react-slot": "https://esm.sh/@radix-ui/react-slot",
|
|
79
|
+
"@radix-ui/react-switch": "https://esm.sh/@radix-ui/react-switch",
|
|
80
|
+
"@radix-ui/react-tabs": "https://esm.sh/@radix-ui/react-tabs",
|
|
81
|
+
"@radix-ui/react-toast": "https://esm.sh/@radix-ui/react-toast",
|
|
82
|
+
"@radix-ui/react-toggle": "https://esm.sh/@radix-ui/react-toggle",
|
|
83
|
+
"@radix-ui/react-toggle-group": "https://esm.sh/@radix-ui/react-toggle-group",
|
|
84
|
+
"@radix-ui/react-toolbar": "https://esm.sh/@radix-ui/react-toolbar",
|
|
85
|
+
"@radix-ui/react-tooltip": "https://esm.sh/@radix-ui/react-tooltip"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<style>
|
|
91
|
+
* { box-sizing: border-box; }
|
|
92
|
+
body { margin: 0; padding: 0; }
|
|
93
|
+
</style>
|
|
94
|
+
</head>
|
|
95
|
+
<body>
|
|
96
|
+
<div id="root"></div>
|
|
97
|
+
|
|
98
|
+
<script type="text/babel" data-type="module">
|
|
99
|
+
import { createRoot } from "react-dom/client";
|
|
100
|
+
|
|
101
|
+
// \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
|
|
102
|
+
${code}
|
|
103
|
+
// \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
|
|
104
|
+
|
|
105
|
+
const container = document.getElementById("root");
|
|
106
|
+
createRoot(container).render(<App />);
|
|
107
|
+
</script>
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|
|
110
|
+
`.trim()
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/Renderize.tsx
|
|
115
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
116
|
+
function Renderize({
|
|
117
|
+
code,
|
|
118
|
+
height = "100%",
|
|
119
|
+
width = "100%",
|
|
120
|
+
className,
|
|
121
|
+
style,
|
|
122
|
+
onError
|
|
123
|
+
}) {
|
|
124
|
+
const prevUrlRef = (0, import_react.useRef)(null);
|
|
125
|
+
const [blobUrl, setBlobUrl] = (0, import_react.useState)(null);
|
|
126
|
+
(0, import_react.useEffect)(() => {
|
|
127
|
+
if (!code?.trim()) return;
|
|
128
|
+
if (prevUrlRef.current) {
|
|
129
|
+
URL.revokeObjectURL(prevUrlRef.current);
|
|
130
|
+
}
|
|
131
|
+
const html = buildTemplate(code);
|
|
132
|
+
const blob = new Blob([html], { type: "text/html" });
|
|
133
|
+
const url = URL.createObjectURL(blob);
|
|
134
|
+
prevUrlRef.current = url;
|
|
135
|
+
setBlobUrl(url);
|
|
136
|
+
return () => {
|
|
137
|
+
if (prevUrlRef.current) {
|
|
138
|
+
URL.revokeObjectURL(prevUrlRef.current);
|
|
139
|
+
prevUrlRef.current = null;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}, [code]);
|
|
143
|
+
(0, import_react.useEffect)(() => {
|
|
144
|
+
const handler = (event) => {
|
|
145
|
+
if (event.data?.source === "renderize" && event.data?.type === "error" && onError) {
|
|
146
|
+
onError(event.data.message);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
window.addEventListener("message", handler);
|
|
150
|
+
return () => window.removeEventListener("message", handler);
|
|
151
|
+
}, [onError]);
|
|
152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
153
|
+
"div",
|
|
154
|
+
{
|
|
155
|
+
className,
|
|
156
|
+
style: { width, height, overflow: "hidden", ...style },
|
|
157
|
+
children: blobUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
158
|
+
"iframe",
|
|
159
|
+
{
|
|
160
|
+
src: blobUrl,
|
|
161
|
+
title: "Renderize Sandbox",
|
|
162
|
+
sandbox: "allow-scripts",
|
|
163
|
+
style: {
|
|
164
|
+
width: "100%",
|
|
165
|
+
height: "100%",
|
|
166
|
+
border: "none",
|
|
167
|
+
display: "block"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
blobUrl
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
176
|
+
0 && (module.exports = {
|
|
177
|
+
Renderize
|
|
178
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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 export a default `App` function component. */
|
|
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
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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 export a default `App` function component. */
|
|
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
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// src/Renderize.tsx
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
|
|
4
|
+
// src/template.ts
|
|
5
|
+
function buildTemplate(code) {
|
|
6
|
+
return (
|
|
7
|
+
/* html */
|
|
8
|
+
`
|
|
9
|
+
<!DOCTYPE html>
|
|
10
|
+
<html lang="en">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="UTF-8" />
|
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
14
|
+
|
|
15
|
+
<!-- Tailwind CSS Play CDN -->
|
|
16
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
17
|
+
|
|
18
|
+
<!-- Babel standalone: transpiles JSX at runtime -->
|
|
19
|
+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
20
|
+
|
|
21
|
+
<!-- Import map: React + Radix UI + Lucide -->
|
|
22
|
+
<script type="importmap">
|
|
23
|
+
{
|
|
24
|
+
"imports": {
|
|
25
|
+
"react": "https://esm.sh/react@18",
|
|
26
|
+
"react/jsx-runtime": "https://esm.sh/react@18/jsx-runtime",
|
|
27
|
+
"react-dom": "https://esm.sh/react-dom@18",
|
|
28
|
+
"react-dom/client": "https://esm.sh/react-dom@18/client",
|
|
29
|
+
"lucide-react": "https://esm.sh/lucide-react@latest",
|
|
30
|
+
"clsx": "https://esm.sh/clsx",
|
|
31
|
+
"class-variance-authority": "https://esm.sh/class-variance-authority",
|
|
32
|
+
"tailwind-merge": "https://esm.sh/tailwind-merge",
|
|
33
|
+
"@radix-ui/react-accordion": "https://esm.sh/@radix-ui/react-accordion",
|
|
34
|
+
"@radix-ui/react-alert-dialog": "https://esm.sh/@radix-ui/react-alert-dialog",
|
|
35
|
+
"@radix-ui/react-avatar": "https://esm.sh/@radix-ui/react-avatar",
|
|
36
|
+
"@radix-ui/react-checkbox": "https://esm.sh/@radix-ui/react-checkbox",
|
|
37
|
+
"@radix-ui/react-collapsible": "https://esm.sh/@radix-ui/react-collapsible",
|
|
38
|
+
"@radix-ui/react-context-menu": "https://esm.sh/@radix-ui/react-context-menu",
|
|
39
|
+
"@radix-ui/react-dialog": "https://esm.sh/@radix-ui/react-dialog",
|
|
40
|
+
"@radix-ui/react-dropdown-menu": "https://esm.sh/@radix-ui/react-dropdown-menu",
|
|
41
|
+
"@radix-ui/react-hover-card": "https://esm.sh/@radix-ui/react-hover-card",
|
|
42
|
+
"@radix-ui/react-label": "https://esm.sh/@radix-ui/react-label",
|
|
43
|
+
"@radix-ui/react-menubar": "https://esm.sh/@radix-ui/react-menubar",
|
|
44
|
+
"@radix-ui/react-navigation-menu": "https://esm.sh/@radix-ui/react-navigation-menu",
|
|
45
|
+
"@radix-ui/react-popover": "https://esm.sh/@radix-ui/react-popover",
|
|
46
|
+
"@radix-ui/react-progress": "https://esm.sh/@radix-ui/react-progress",
|
|
47
|
+
"@radix-ui/react-radio-group": "https://esm.sh/@radix-ui/react-radio-group",
|
|
48
|
+
"@radix-ui/react-scroll-area": "https://esm.sh/@radix-ui/react-scroll-area",
|
|
49
|
+
"@radix-ui/react-select": "https://esm.sh/@radix-ui/react-select",
|
|
50
|
+
"@radix-ui/react-separator": "https://esm.sh/@radix-ui/react-separator",
|
|
51
|
+
"@radix-ui/react-slider": "https://esm.sh/@radix-ui/react-slider",
|
|
52
|
+
"@radix-ui/react-slot": "https://esm.sh/@radix-ui/react-slot",
|
|
53
|
+
"@radix-ui/react-switch": "https://esm.sh/@radix-ui/react-switch",
|
|
54
|
+
"@radix-ui/react-tabs": "https://esm.sh/@radix-ui/react-tabs",
|
|
55
|
+
"@radix-ui/react-toast": "https://esm.sh/@radix-ui/react-toast",
|
|
56
|
+
"@radix-ui/react-toggle": "https://esm.sh/@radix-ui/react-toggle",
|
|
57
|
+
"@radix-ui/react-toggle-group": "https://esm.sh/@radix-ui/react-toggle-group",
|
|
58
|
+
"@radix-ui/react-toolbar": "https://esm.sh/@radix-ui/react-toolbar",
|
|
59
|
+
"@radix-ui/react-tooltip": "https://esm.sh/@radix-ui/react-tooltip"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<style>
|
|
65
|
+
* { box-sizing: border-box; }
|
|
66
|
+
body { margin: 0; padding: 0; }
|
|
67
|
+
</style>
|
|
68
|
+
</head>
|
|
69
|
+
<body>
|
|
70
|
+
<div id="root"></div>
|
|
71
|
+
|
|
72
|
+
<script type="text/babel" data-type="module">
|
|
73
|
+
import { createRoot } from "react-dom/client";
|
|
74
|
+
|
|
75
|
+
// \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
|
|
76
|
+
${code}
|
|
77
|
+
// \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
|
|
78
|
+
|
|
79
|
+
const container = document.getElementById("root");
|
|
80
|
+
createRoot(container).render(<App />);
|
|
81
|
+
</script>
|
|
82
|
+
</body>
|
|
83
|
+
</html>
|
|
84
|
+
`.trim()
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/Renderize.tsx
|
|
89
|
+
import { jsx } from "react/jsx-runtime";
|
|
90
|
+
function Renderize({
|
|
91
|
+
code,
|
|
92
|
+
height = "100%",
|
|
93
|
+
width = "100%",
|
|
94
|
+
className,
|
|
95
|
+
style,
|
|
96
|
+
onError
|
|
97
|
+
}) {
|
|
98
|
+
const prevUrlRef = useRef(null);
|
|
99
|
+
const [blobUrl, setBlobUrl] = useState(null);
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (!code?.trim()) return;
|
|
102
|
+
if (prevUrlRef.current) {
|
|
103
|
+
URL.revokeObjectURL(prevUrlRef.current);
|
|
104
|
+
}
|
|
105
|
+
const html = buildTemplate(code);
|
|
106
|
+
const blob = new Blob([html], { type: "text/html" });
|
|
107
|
+
const url = URL.createObjectURL(blob);
|
|
108
|
+
prevUrlRef.current = url;
|
|
109
|
+
setBlobUrl(url);
|
|
110
|
+
return () => {
|
|
111
|
+
if (prevUrlRef.current) {
|
|
112
|
+
URL.revokeObjectURL(prevUrlRef.current);
|
|
113
|
+
prevUrlRef.current = null;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}, [code]);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
const handler = (event) => {
|
|
119
|
+
if (event.data?.source === "renderize" && event.data?.type === "error" && onError) {
|
|
120
|
+
onError(event.data.message);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
window.addEventListener("message", handler);
|
|
124
|
+
return () => window.removeEventListener("message", handler);
|
|
125
|
+
}, [onError]);
|
|
126
|
+
return /* @__PURE__ */ jsx(
|
|
127
|
+
"div",
|
|
128
|
+
{
|
|
129
|
+
className,
|
|
130
|
+
style: { width, height, overflow: "hidden", ...style },
|
|
131
|
+
children: blobUrl && /* @__PURE__ */ jsx(
|
|
132
|
+
"iframe",
|
|
133
|
+
{
|
|
134
|
+
src: blobUrl,
|
|
135
|
+
title: "Renderize Sandbox",
|
|
136
|
+
sandbox: "allow-scripts",
|
|
137
|
+
style: {
|
|
138
|
+
width: "100%",
|
|
139
|
+
height: "100%",
|
|
140
|
+
border: "none",
|
|
141
|
+
display: "block"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
blobUrl
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
Renderize
|
|
151
|
+
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aquiles-ai/renderize",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Drop-in sandbox component that executes AI-generated React code with zero configuration.",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
6
8
|
"scripts": {
|
|
7
9
|
"build": "tsup src/index.ts --format cjs,esm --dts --external react react-dom",
|
|
8
10
|
"dev": "tsup src/index.ts --format cjs,esm --dts --external react react-dom --watch"
|
|
@@ -35,6 +37,7 @@
|
|
|
35
37
|
},
|
|
36
38
|
"dependencies": {
|
|
37
39
|
"react": "^19.2.4",
|
|
38
|
-
"react-dom": "^19.2.4"
|
|
40
|
+
"react-dom": "^19.2.4",
|
|
41
|
+
"tsup": "^8.5.1"
|
|
39
42
|
}
|
|
40
43
|
}
|
package/src/Renderize.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
1
2
|
import React, { useEffect, useRef, useState } from "react";
|
|
2
3
|
import { buildTemplate } from "./template.js";
|
|
3
4
|
|
|
@@ -24,35 +25,30 @@ export function Renderize({
|
|
|
24
25
|
style,
|
|
25
26
|
onError,
|
|
26
27
|
}: RenderizeProps) {
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const [key, setKey] = useState(0);
|
|
28
|
+
const prevUrlRef = useRef<string | null>(null);
|
|
29
|
+
const [blobUrl, setBlobUrl] = useState<string | null>(null);
|
|
30
30
|
|
|
31
31
|
useEffect(() => {
|
|
32
32
|
if (!code?.trim()) return;
|
|
33
33
|
|
|
34
34
|
// Revoke previous blob URL to free memory
|
|
35
|
-
if (
|
|
36
|
-
URL.revokeObjectURL(
|
|
35
|
+
if (prevUrlRef.current) {
|
|
36
|
+
URL.revokeObjectURL(prevUrlRef.current);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const html = buildTemplate(code);
|
|
40
40
|
const blob = new Blob([html], { type: "text/html" });
|
|
41
41
|
const url = URL.createObjectURL(blob);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Force iframe remount when code changes to ensure clean state
|
|
50
|
-
setKey((prev) => prev + 1);
|
|
43
|
+
prevUrlRef.current = url;
|
|
44
|
+
// Storing the URL in state ensures the iframe src prop
|
|
45
|
+
// and the actual DOM element are always in sync
|
|
46
|
+
setBlobUrl(url);
|
|
51
47
|
|
|
52
48
|
return () => {
|
|
53
|
-
if (
|
|
54
|
-
URL.revokeObjectURL(
|
|
55
|
-
|
|
49
|
+
if (prevUrlRef.current) {
|
|
50
|
+
URL.revokeObjectURL(prevUrlRef.current);
|
|
51
|
+
prevUrlRef.current = null;
|
|
56
52
|
}
|
|
57
53
|
};
|
|
58
54
|
}, [code]);
|
|
@@ -77,18 +73,22 @@ export function Renderize({
|
|
|
77
73
|
className={className}
|
|
78
74
|
style={{ width, height, overflow: "hidden", ...style }}
|
|
79
75
|
>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
76
|
+
{blobUrl && (
|
|
77
|
+
<iframe
|
|
78
|
+
// key forces a full remount when the URL changes,
|
|
79
|
+
// guaranteeing a clean JS context on every new code
|
|
80
|
+
key={blobUrl}
|
|
81
|
+
src={blobUrl}
|
|
82
|
+
title="Renderize Sandbox"
|
|
83
|
+
sandbox="allow-scripts"
|
|
84
|
+
style={{
|
|
85
|
+
width: "100%",
|
|
86
|
+
height: "100%",
|
|
87
|
+
border: "none",
|
|
88
|
+
display: "block",
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
92
|
</div>
|
|
93
93
|
);
|
|
94
94
|
}
|