@easybits.cloud/html-tailwind-generator 0.2.126 → 0.2.128
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/{ViewportToggle-DefLZWrz.d.ts → ViewportToggle-Dszv1gsL.d.ts} +3 -3
- package/dist/buildHtmlV4.d.ts +14 -0
- package/dist/buildHtmlV4.js +8 -0
- package/dist/buildHtmlV4.js.map +1 -0
- package/dist/chunk-7LKFTF32.js +54 -0
- package/dist/chunk-7LKFTF32.js.map +1 -0
- package/dist/{chunk-ZARS4VIK.js → chunk-CQXBVGVC.js} +4 -4
- package/dist/chunk-KXOAEC33.js +67 -0
- package/dist/chunk-KXOAEC33.js.map +1 -0
- package/dist/chunk-LI4UDDMT.js +117 -0
- package/dist/chunk-LI4UDDMT.js.map +1 -0
- package/dist/{chunk-T4F6F3HD.js → chunk-RJQKHWIH.js} +4 -4
- package/dist/components.d.ts +1 -1
- package/dist/components4.d.ts +121 -0
- package/dist/components4.js +1876 -0
- package/dist/components4.js.map +1 -0
- package/dist/generate.js +2 -2
- package/dist/grapesToSections.d.ts +12 -0
- package/dist/grapesToSections.js +7 -0
- package/dist/grapesToSections.js.map +1 -0
- package/dist/hooks/useThumbnailCapture.d.ts +12 -0
- package/dist/hooks/useThumbnailCapture.js +7 -0
- package/dist/hooks/useThumbnailCapture.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +23 -11
- package/dist/refine.js +2 -2
- package/package.json +26 -2
- /package/dist/{chunk-ZARS4VIK.js.map → chunk-CQXBVGVC.js.map} +0 -0
- /package/dist/{chunk-T4F6F3HD.js.map → chunk-RJQKHWIH.js.map} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React__default from 'react';
|
|
2
2
|
import { S as Section3, I as IframeMessage } from './types-BIpbpCJr.js';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import { C as CustomColors, a as LandingTheme } from './themes-DNTBHJUH.js';
|
|
@@ -11,10 +11,10 @@ interface CanvasProps {
|
|
|
11
11
|
sections: Section3[];
|
|
12
12
|
theme?: string;
|
|
13
13
|
onMessage: (msg: IframeMessage) => void;
|
|
14
|
-
iframeRectRef:
|
|
14
|
+
iframeRectRef: React__default.MutableRefObject<DOMRect | null>;
|
|
15
15
|
onReady?: () => void;
|
|
16
16
|
}
|
|
17
|
-
declare const Canvas:
|
|
17
|
+
declare const Canvas: React__default.ForwardRefExoticComponent<CanvasProps & React__default.RefAttributes<CanvasHandle>>;
|
|
18
18
|
|
|
19
19
|
interface SectionListProps {
|
|
20
20
|
sections: Section3[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { S as Section3 } from './types-BIpbpCJr.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build deploy HTML for Landings v4 (GrapesJS).
|
|
5
|
+
* Includes Tailwind CDN + theme CSS variables + any GrapesJS-generated CSS.
|
|
6
|
+
*/
|
|
7
|
+
declare function buildDeployHtmlV4(sections: Section3[], opts?: {
|
|
8
|
+
showBranding?: boolean;
|
|
9
|
+
title?: string;
|
|
10
|
+
themeName?: string;
|
|
11
|
+
customColors?: Record<string, string>;
|
|
12
|
+
}): string;
|
|
13
|
+
|
|
14
|
+
export { buildDeployHtmlV4 };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/grapesToSections.ts
|
|
2
|
+
var idCounter = 0;
|
|
3
|
+
function stableId() {
|
|
4
|
+
return `s4_${Date.now().toString(36)}_${(idCounter++).toString(36)}`;
|
|
5
|
+
}
|
|
6
|
+
function grapesToSections(html) {
|
|
7
|
+
if (!html || !html.trim()) return [];
|
|
8
|
+
const parser = new DOMParser();
|
|
9
|
+
const doc = parser.parseFromString(html, "text/html");
|
|
10
|
+
const styleTags = [
|
|
11
|
+
...Array.from(doc.head.querySelectorAll("style")),
|
|
12
|
+
...Array.from(doc.body.querySelectorAll("style"))
|
|
13
|
+
];
|
|
14
|
+
const cssContent = styleTags.map((s) => s.textContent || "").join("\n").trim();
|
|
15
|
+
doc.body.querySelectorAll("style").forEach((s) => s.remove());
|
|
16
|
+
const children = Array.from(doc.body.children);
|
|
17
|
+
const sections = [];
|
|
18
|
+
if (cssContent) {
|
|
19
|
+
sections.push({
|
|
20
|
+
id: "__grapes_css__",
|
|
21
|
+
order: -1,
|
|
22
|
+
html: `<style>${cssContent}</style>`,
|
|
23
|
+
label: "__css__"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (children.length === 0 && doc.body.innerHTML.trim()) {
|
|
27
|
+
sections.push({
|
|
28
|
+
id: stableId(),
|
|
29
|
+
order: 0,
|
|
30
|
+
html: `<section>${doc.body.innerHTML.trim()}</section>`,
|
|
31
|
+
label: "Section 1"
|
|
32
|
+
});
|
|
33
|
+
return sections;
|
|
34
|
+
}
|
|
35
|
+
let order = 0;
|
|
36
|
+
for (const el of children) {
|
|
37
|
+
if (el.tagName !== "SECTION" && !el.getAttribute("data-section-id") && !el.textContent?.trim() && !el.querySelector("img, video, svg, canvas, iframe")) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const id = el.getAttribute("data-section-id") || stableId();
|
|
41
|
+
sections.push({
|
|
42
|
+
id,
|
|
43
|
+
order: order++,
|
|
44
|
+
html: el.outerHTML,
|
|
45
|
+
label: el.getAttribute("data-label") || (el.tagName === "SECTION" ? `Section ${order}` : `Block ${order}`)
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return sections;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
grapesToSections
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=chunk-7LKFTF32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/grapesToSections.ts"],"sourcesContent":["import type { Section3 } from \"./types\";\n\n/** Simple counter-based ID to keep stable across saves within a session */\nlet idCounter = 0;\nfunction stableId() {\n return `s4_${Date.now().toString(36)}_${(idCounter++).toString(36)}`;\n}\n\n/**\n * Extract Section3[] from GrapesJS editor HTML (which may include a <style> block).\n * The <style> block with GrapesJS CSS is stored as a special first section.\n * Each top-level <section> or element becomes one Section3 entry.\n *\n * NOTE: Browser-only — uses DOMParser.\n */\nexport function grapesToSections(html: string): Section3[] {\n if (!html || !html.trim()) return [];\n\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, \"text/html\");\n\n // Extract <style> tags (GrapesJS CSS) from head and body\n const styleTags = [\n ...Array.from(doc.head.querySelectorAll(\"style\")),\n ...Array.from(doc.body.querySelectorAll(\"style\")),\n ];\n const cssContent = styleTags.map((s) => s.textContent || \"\").join(\"\\n\").trim();\n // Remove style tags from body before processing elements\n doc.body.querySelectorAll(\"style\").forEach((s) => s.remove());\n\n const children = Array.from(doc.body.children);\n const sections: Section3[] = [];\n\n // Store CSS as a special section if present\n if (cssContent) {\n sections.push({\n id: \"__grapes_css__\",\n order: -1,\n html: `<style>${cssContent}</style>`,\n label: \"__css__\",\n });\n }\n\n if (children.length === 0 && doc.body.innerHTML.trim()) {\n sections.push({\n id: stableId(),\n order: 0,\n html: `<section>${doc.body.innerHTML.trim()}</section>`,\n label: \"Section 1\",\n });\n return sections;\n }\n\n let order = 0;\n for (const el of children) {\n // Skip empty non-section elements without data-section-id (GrapesJS ghost wrappers)\n if (\n el.tagName !== \"SECTION\" &&\n !el.getAttribute(\"data-section-id\") &&\n !el.textContent?.trim() &&\n !el.querySelector(\"img, video, svg, canvas, iframe\")\n ) {\n continue;\n }\n\n const id =\n el.getAttribute(\"data-section-id\") || stableId();\n\n sections.push({\n id,\n order: order++,\n html: el.outerHTML,\n label:\n el.getAttribute(\"data-label\") ||\n (el.tagName === \"SECTION\" ? `Section ${order}` : `Block ${order}`),\n });\n }\n\n return sections;\n}\n"],"mappings":";AAGA,IAAI,YAAY;AAChB,SAAS,WAAW;AAClB,SAAO,MAAM,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,KAAK,aAAa,SAAS,EAAE,CAAC;AACpE;AASO,SAAS,iBAAiB,MAA0B;AACzD,MAAI,CAAC,QAAQ,CAAC,KAAK,KAAK,EAAG,QAAO,CAAC;AAEnC,QAAM,SAAS,IAAI,UAAU;AAC7B,QAAM,MAAM,OAAO,gBAAgB,MAAM,WAAW;AAGpD,QAAM,YAAY;AAAA,IAChB,GAAG,MAAM,KAAK,IAAI,KAAK,iBAAiB,OAAO,CAAC;AAAA,IAChD,GAAG,MAAM,KAAK,IAAI,KAAK,iBAAiB,OAAO,CAAC;AAAA,EAClD;AACA,QAAM,aAAa,UAAU,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,KAAK,IAAI,EAAE,KAAK;AAE7E,MAAI,KAAK,iBAAiB,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;AAE5D,QAAM,WAAW,MAAM,KAAK,IAAI,KAAK,QAAQ;AAC7C,QAAM,WAAuB,CAAC;AAG9B,MAAI,YAAY;AACd,aAAS,KAAK;AAAA,MACZ,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM,UAAU,UAAU;AAAA,MAC1B,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,WAAW,KAAK,IAAI,KAAK,UAAU,KAAK,GAAG;AACtD,aAAS,KAAK;AAAA,MACZ,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,MAAM,YAAY,IAAI,KAAK,UAAU,KAAK,CAAC;AAAA,MAC3C,OAAO;AAAA,IACT,CAAC;AACD,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ;AACZ,aAAW,MAAM,UAAU;AAEzB,QACE,GAAG,YAAY,aACf,CAAC,GAAG,aAAa,iBAAiB,KAClC,CAAC,GAAG,aAAa,KAAK,KACtB,CAAC,GAAG,cAAc,iCAAiC,GACnD;AACA;AAAA,IACF;AAEA,UAAM,KACJ,GAAG,aAAa,iBAAiB,KAAK,SAAS;AAEjD,aAAS,KAAK;AAAA,MACZ;AAAA,MACA,OAAO;AAAA,MACP,MAAM,GAAG;AAAA,MACT,OACE,GAAG,aAAa,YAAY,MAC3B,GAAG,YAAY,YAAY,WAAW,KAAK,KAAK,SAAS,KAAK;AAAA,IACnE,CAAC;AAAA,EACH;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildThemePromptContext
|
|
3
|
-
} from "./chunk-VV5I53WR.js";
|
|
4
1
|
import {
|
|
5
2
|
dataUrlToImagePart,
|
|
6
3
|
streamGenerate
|
|
7
4
|
} from "./chunk-YZHRLDQF.js";
|
|
5
|
+
import {
|
|
6
|
+
buildThemePromptContext
|
|
7
|
+
} from "./chunk-VV5I53WR.js";
|
|
8
8
|
|
|
9
9
|
// src/generate.ts
|
|
10
10
|
var SYSTEM_PROMPT = `You are a world-class web designer who creates AWARD-WINNING landing pages. Your designs win Awwwards, FWA, and CSS Design Awards. You think in terms of visual hierarchy, whitespace, and emotional impact.
|
|
@@ -196,4 +196,4 @@ export {
|
|
|
196
196
|
PROMPT_SUFFIX,
|
|
197
197
|
generateLanding
|
|
198
198
|
};
|
|
199
|
-
//# sourceMappingURL=chunk-
|
|
199
|
+
//# sourceMappingURL=chunk-CQXBVGVC.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildCustomTheme,
|
|
3
|
+
buildSingleThemeCss
|
|
4
|
+
} from "./chunk-VV5I53WR.js";
|
|
5
|
+
|
|
6
|
+
// src/buildHtmlV4.ts
|
|
7
|
+
function buildDeployHtmlV4(sections, opts) {
|
|
8
|
+
const cssSection = sections.find((s) => s.id === "__grapes_css__");
|
|
9
|
+
const contentSections = sections.filter((s) => s.id !== "__grapes_css__").sort((a, b) => a.order - b.order);
|
|
10
|
+
let grapesCSS = "";
|
|
11
|
+
if (cssSection) {
|
|
12
|
+
const match = cssSection.html.match(/<style[^>]*>([\s\S]*?)<\/style>/i);
|
|
13
|
+
grapesCSS = match?.[1] || "";
|
|
14
|
+
}
|
|
15
|
+
let themeCss = "";
|
|
16
|
+
if (opts?.customColors && Object.keys(opts.customColors).length) {
|
|
17
|
+
let colors = opts.customColors;
|
|
18
|
+
if (!colors["on-primary"] && colors.primary) {
|
|
19
|
+
const full = buildCustomTheme(colors);
|
|
20
|
+
colors = full.colors;
|
|
21
|
+
}
|
|
22
|
+
const vars = Object.entries(colors).map(([k, v]) => ` --color-${k}: ${v};`).join("\n");
|
|
23
|
+
themeCss = `:root {
|
|
24
|
+
${vars}
|
|
25
|
+
}`;
|
|
26
|
+
} else if (opts?.themeName && opts.themeName !== "custom") {
|
|
27
|
+
try {
|
|
28
|
+
themeCss = buildSingleThemeCss(opts.themeName).css || "";
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const body = contentSections.map((s) => {
|
|
33
|
+
return s.html.replace(/\s+contenteditable="[^"]*"/gi, "").replace(/\s+data-section-id="[^"]*"/gi, "").replace(/\s+data-label="[^"]*"/gi, "").replace(/\s+data-gjs[^=]*="[^"]*"/gi, "");
|
|
34
|
+
}).join("\n");
|
|
35
|
+
const branding = opts?.showBranding !== false ? `<div style="text-align:center;padding:12px;font-size:11px;opacity:.5;font-family:system-ui">
|
|
36
|
+
Built with <a href="https://easybits.cloud" target="_blank" style="color:inherit;text-decoration:underline">EasyBits</a>
|
|
37
|
+
</div>` : "";
|
|
38
|
+
return `<!DOCTYPE html>
|
|
39
|
+
<html lang="es">
|
|
40
|
+
<head>
|
|
41
|
+
<meta charset="UTF-8"/>
|
|
42
|
+
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
43
|
+
<title>${opts?.title || "Landing Page"}</title>
|
|
44
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
45
|
+
<script>
|
|
46
|
+
tailwind.config={theme:{extend:{colors:{primary:'var(--color-primary)','primary-light':'var(--color-primary-light)','primary-dark':'var(--color-primary-dark)',secondary:'var(--color-secondary)',accent:'var(--color-accent)',surface:'var(--color-surface)','surface-alt':'var(--color-surface-alt)','on-primary':'var(--color-on-primary)','on-secondary':'var(--color-on-secondary)','on-accent':'var(--color-on-accent)','on-surface':'var(--color-on-surface)','on-surface-muted':'var(--color-on-surface-muted)'}}}}
|
|
47
|
+
</script>
|
|
48
|
+
<style>
|
|
49
|
+
${themeCss}
|
|
50
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
51
|
+
html{scroll-behavior:smooth}
|
|
52
|
+
body{font-family:system-ui,-apple-system,sans-serif}
|
|
53
|
+
img{max-width:100%;height:auto}
|
|
54
|
+
${grapesCSS}
|
|
55
|
+
</style>
|
|
56
|
+
</head>
|
|
57
|
+
<body>
|
|
58
|
+
${body}
|
|
59
|
+
${branding}
|
|
60
|
+
</body>
|
|
61
|
+
</html>`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
buildDeployHtmlV4
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=chunk-KXOAEC33.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/buildHtmlV4.ts"],"sourcesContent":["import type { Section3 } from \"./types\";\nimport { buildSingleThemeCss, buildCustomTheme } from \"./themes\";\n\n/**\n * Build deploy HTML for Landings v4 (GrapesJS).\n * Includes Tailwind CDN + theme CSS variables + any GrapesJS-generated CSS.\n */\nexport function buildDeployHtmlV4(\n sections: Section3[],\n opts?: {\n showBranding?: boolean;\n title?: string;\n themeName?: string;\n customColors?: Record<string, string>;\n }\n): string {\n const cssSection = sections.find((s) => s.id === \"__grapes_css__\");\n const contentSections = sections\n .filter((s) => s.id !== \"__grapes_css__\")\n .sort((a, b) => a.order - b.order);\n\n // Extract raw CSS from the <style> wrapper\n let grapesCSS = \"\";\n if (cssSection) {\n const match = cssSection.html.match(/<style[^>]*>([\\s\\S]*?)<\\/style>/i);\n grapesCSS = match?.[1] || \"\";\n }\n\n // Build theme CSS variables\n let themeCss = \"\";\n if (opts?.customColors && Object.keys(opts.customColors).length) {\n let colors = opts.customColors;\n // Brand kit (only 4 colors) → derive full theme with on-* colors\n if (!colors[\"on-primary\"] && colors.primary) {\n const full = buildCustomTheme(colors as any);\n colors = full.colors;\n }\n const vars = Object.entries(colors)\n .map(([k, v]) => ` --color-${k}: ${v};`)\n .join(\"\\n\");\n themeCss = `:root {\\n${vars}\\n}`;\n } else if (opts?.themeName && opts.themeName !== \"custom\") {\n try {\n themeCss = buildSingleThemeCss(opts.themeName).css || \"\";\n } catch { /* fallback: no theme */ }\n }\n\n const body = contentSections\n .map((s) => {\n return s.html\n .replace(/\\s+contenteditable=\"[^\"]*\"/gi, \"\")\n .replace(/\\s+data-section-id=\"[^\"]*\"/gi, \"\")\n .replace(/\\s+data-label=\"[^\"]*\"/gi, \"\")\n .replace(/\\s+data-gjs[^=]*=\"[^\"]*\"/gi, \"\");\n })\n .join(\"\\n\");\n\n const branding = opts?.showBranding !== false\n ? `<div style=\"text-align:center;padding:12px;font-size:11px;opacity:.5;font-family:system-ui\">\n Built with <a href=\"https://easybits.cloud\" target=\"_blank\" style=\"color:inherit;text-decoration:underline\">EasyBits</a>\n </div>`\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n<meta charset=\"UTF-8\"/>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\n<title>${opts?.title || \"Landing Page\"}</title>\n<script src=\"https://cdn.tailwindcss.com\"><\\/script>\n<script>\ntailwind.config={theme:{extend:{colors:{primary:'var(--color-primary)','primary-light':'var(--color-primary-light)','primary-dark':'var(--color-primary-dark)',secondary:'var(--color-secondary)',accent:'var(--color-accent)',surface:'var(--color-surface)','surface-alt':'var(--color-surface-alt)','on-primary':'var(--color-on-primary)','on-secondary':'var(--color-on-secondary)','on-accent':'var(--color-on-accent)','on-surface':'var(--color-on-surface)','on-surface-muted':'var(--color-on-surface-muted)'}}}}\n<\\/script>\n<style>\n${themeCss}\n*{margin:0;padding:0;box-sizing:border-box}\nhtml{scroll-behavior:smooth}\nbody{font-family:system-ui,-apple-system,sans-serif}\nimg{max-width:100%;height:auto}\n${grapesCSS}\n</style>\n</head>\n<body>\n${body}\n${branding}\n</body>\n</html>`;\n}\n"],"mappings":";;;;;;AAOO,SAAS,kBACd,UACA,MAMQ;AACR,QAAM,aAAa,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,gBAAgB;AACjE,QAAM,kBAAkB,SACrB,OAAO,CAAC,MAAM,EAAE,OAAO,gBAAgB,EACvC,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAGnC,MAAI,YAAY;AAChB,MAAI,YAAY;AACd,UAAM,QAAQ,WAAW,KAAK,MAAM,kCAAkC;AACtE,gBAAY,QAAQ,CAAC,KAAK;AAAA,EAC5B;AAGA,MAAI,WAAW;AACf,MAAI,MAAM,gBAAgB,OAAO,KAAK,KAAK,YAAY,EAAE,QAAQ;AAC/D,QAAI,SAAS,KAAK;AAElB,QAAI,CAAC,OAAO,YAAY,KAAK,OAAO,SAAS;AAC3C,YAAM,OAAO,iBAAiB,MAAa;AAC3C,eAAS,KAAK;AAAA,IAChB;AACA,UAAM,OAAO,OAAO,QAAQ,MAAM,EAC/B,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,GAAG,EACvC,KAAK,IAAI;AACZ,eAAW;AAAA,EAAY,IAAI;AAAA;AAAA,EAC7B,WAAW,MAAM,aAAa,KAAK,cAAc,UAAU;AACzD,QAAI;AACF,iBAAW,oBAAoB,KAAK,SAAS,EAAE,OAAO;AAAA,IACxD,QAAQ;AAAA,IAA2B;AAAA,EACrC;AAEA,QAAM,OAAO,gBACV,IAAI,CAAC,MAAM;AACV,WAAO,EAAE,KACN,QAAQ,gCAAgC,EAAE,EAC1C,QAAQ,gCAAgC,EAAE,EAC1C,QAAQ,2BAA2B,EAAE,EACrC,QAAQ,8BAA8B,EAAE;AAAA,EAC7C,CAAC,EACA,KAAK,IAAI;AAEZ,QAAM,WAAW,MAAM,iBAAiB,QACpC;AAAA;AAAA,gBAGA;AAEJ,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,SAKA,MAAM,SAAS,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpC,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,EAKR,SAAS;AAAA;AAAA;AAAA;AAAA,EAIT,IAAI;AAAA,EACJ,QAAQ;AAAA;AAAA;AAGV;","names":[]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// src/hooks/useThumbnailCapture.ts
|
|
2
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
3
|
+
function buildCaptureHtml(sectionHtml, themeCssData) {
|
|
4
|
+
return `<!DOCTYPE html><html><head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
7
|
+
${themeCssData ? `<script>tailwind.config = ${themeCssData.tailwindConfig}</script>` : ""}
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
|
9
|
+
<style>
|
|
10
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
11
|
+
body { font-family: 'Inter', sans-serif; width: 8.5in; height: 11in; overflow: hidden; }
|
|
12
|
+
${themeCssData?.css || ""}
|
|
13
|
+
</style>
|
|
14
|
+
</head><body>${sectionHtml}</body></html>`;
|
|
15
|
+
}
|
|
16
|
+
var THUMB_W = 200;
|
|
17
|
+
var THUMB_H = Math.round(THUMB_W * (11 / 8.5));
|
|
18
|
+
function useThumbnailCapture(sections, themeCssData) {
|
|
19
|
+
const [thumbs, setThumbs] = useState({});
|
|
20
|
+
const iframeRef = useRef(null);
|
|
21
|
+
const queueRef = useRef([]);
|
|
22
|
+
const busyRef = useRef(false);
|
|
23
|
+
const lastHtmlRef = useRef({});
|
|
24
|
+
const debounceRef = useRef(void 0);
|
|
25
|
+
const processQueue = useCallback(() => {
|
|
26
|
+
if (busyRef.current || queueRef.current.length === 0) return;
|
|
27
|
+
busyRef.current = true;
|
|
28
|
+
const item = queueRef.current.shift();
|
|
29
|
+
if (!iframeRef.current) {
|
|
30
|
+
const iframe2 = document.createElement("iframe");
|
|
31
|
+
iframe2.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:816px;height:1056px;opacity:0;pointer-events:none;border:none;";
|
|
32
|
+
document.body.appendChild(iframe2);
|
|
33
|
+
iframeRef.current = iframe2;
|
|
34
|
+
}
|
|
35
|
+
const iframe = iframeRef.current;
|
|
36
|
+
const html = buildCaptureHtml(item.html, themeCssData);
|
|
37
|
+
const onLoad = () => {
|
|
38
|
+
iframe.removeEventListener("load", onLoad);
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
try {
|
|
41
|
+
const doc = iframe.contentDocument;
|
|
42
|
+
if (!doc?.body) throw new Error("no doc");
|
|
43
|
+
const canvas = document.createElement("canvas");
|
|
44
|
+
canvas.width = THUMB_W * 2;
|
|
45
|
+
canvas.height = THUMB_H * 2;
|
|
46
|
+
const ctx = canvas.getContext("2d");
|
|
47
|
+
ctx.scale(2, 2);
|
|
48
|
+
const svgData = `<svg xmlns="http://www.w3.org/2000/svg" width="${THUMB_W}" height="${THUMB_H}">
|
|
49
|
+
<foreignObject width="816" height="1056" transform="scale(${THUMB_W / 816})">
|
|
50
|
+
${new XMLSerializer().serializeToString(doc.documentElement)}
|
|
51
|
+
</foreignObject>
|
|
52
|
+
</svg>`;
|
|
53
|
+
const img = new Image();
|
|
54
|
+
img.onload = () => {
|
|
55
|
+
ctx.drawImage(img, 0, 0, THUMB_W, THUMB_H);
|
|
56
|
+
const dataUrl = canvas.toDataURL("image/png");
|
|
57
|
+
setThumbs((prev) => ({ ...prev, [item.id]: dataUrl }));
|
|
58
|
+
busyRef.current = false;
|
|
59
|
+
processQueue();
|
|
60
|
+
};
|
|
61
|
+
img.onerror = () => {
|
|
62
|
+
busyRef.current = false;
|
|
63
|
+
processQueue();
|
|
64
|
+
};
|
|
65
|
+
img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgData);
|
|
66
|
+
} catch {
|
|
67
|
+
busyRef.current = false;
|
|
68
|
+
processQueue();
|
|
69
|
+
}
|
|
70
|
+
}, 800);
|
|
71
|
+
};
|
|
72
|
+
iframe.addEventListener("load", onLoad);
|
|
73
|
+
iframe.srcdoc = html;
|
|
74
|
+
}, [themeCssData]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
clearTimeout(debounceRef.current);
|
|
77
|
+
debounceRef.current = setTimeout(() => {
|
|
78
|
+
const content = sections.filter((s) => s.id !== "__grapes_css__" && s.label !== "__css__");
|
|
79
|
+
let changed = false;
|
|
80
|
+
for (const s of content) {
|
|
81
|
+
if (lastHtmlRef.current[s.id] !== s.html) {
|
|
82
|
+
lastHtmlRef.current[s.id] = s.html;
|
|
83
|
+
queueRef.current = queueRef.current.filter((q) => q.id !== s.id);
|
|
84
|
+
queueRef.current.push({ id: s.id, html: s.html });
|
|
85
|
+
changed = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const ids = new Set(content.map((s) => s.id));
|
|
89
|
+
for (const id of Object.keys(lastHtmlRef.current)) {
|
|
90
|
+
if (!ids.has(id)) delete lastHtmlRef.current[id];
|
|
91
|
+
}
|
|
92
|
+
if (changed) processQueue();
|
|
93
|
+
}, 500);
|
|
94
|
+
return () => clearTimeout(debounceRef.current);
|
|
95
|
+
}, [sections, processQueue]);
|
|
96
|
+
const prevThemeRef = useRef(themeCssData);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (prevThemeRef.current === themeCssData) return;
|
|
99
|
+
prevThemeRef.current = themeCssData;
|
|
100
|
+
lastHtmlRef.current = {};
|
|
101
|
+
setThumbs({});
|
|
102
|
+
}, [themeCssData]);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
return () => {
|
|
105
|
+
if (iframeRef.current) {
|
|
106
|
+
iframeRef.current.remove();
|
|
107
|
+
iframeRef.current = null;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}, []);
|
|
111
|
+
return thumbs;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
useThumbnailCapture
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=chunk-LI4UDDMT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useThumbnailCapture.ts"],"sourcesContent":["import { useState, useRef, useCallback, useEffect } from \"react\";\nimport type { Section3 } from \"../types\";\n\n/** Build HTML for the off-screen capture iframe */\nfunction buildCaptureHtml(sectionHtml: string, themeCssData?: { css: string; tailwindConfig: string }): string {\n return `<!DOCTYPE html><html><head>\n<meta charset=\"UTF-8\">\n<script src=\"https://cdn.tailwindcss.com\"><\\/script>\n${themeCssData ? `<script>tailwind.config = ${themeCssData.tailwindConfig}<\\/script>` : \"\"}\n<link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap\" rel=\"stylesheet\">\n<style>\n* { box-sizing: border-box; margin: 0; padding: 0; }\nbody { font-family: 'Inter', sans-serif; width: 8.5in; height: 11in; overflow: hidden; }\n${themeCssData?.css || \"\"}\n</style>\n</head><body>${sectionHtml}</body></html>`;\n}\n\nconst THUMB_W = 200;\nconst THUMB_H = Math.round(THUMB_W * (11 / 8.5));\n\n/**\n * Captures static thumbnail images from sections using a single off-screen iframe.\n * Processes one section at a time via a queue to avoid N simultaneous Tailwind CDN loads.\n */\nexport function useThumbnailCapture(\n sections: Section3[],\n themeCssData?: { css: string; tailwindConfig: string }\n) {\n const [thumbs, setThumbs] = useState<Record<string, string>>({});\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const queueRef = useRef<{ id: string; html: string }[]>([]);\n const busyRef = useRef(false);\n const lastHtmlRef = useRef<Record<string, string>>({});\n const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n const processQueue = useCallback(() => {\n if (busyRef.current || queueRef.current.length === 0) return;\n busyRef.current = true;\n\n const item = queueRef.current.shift()!;\n\n // Create iframe on demand\n if (!iframeRef.current) {\n const iframe = document.createElement(\"iframe\");\n iframe.style.cssText = \"position:fixed;top:-9999px;left:-9999px;width:816px;height:1056px;opacity:0;pointer-events:none;border:none;\";\n document.body.appendChild(iframe);\n iframeRef.current = iframe;\n }\n\n const iframe = iframeRef.current;\n const html = buildCaptureHtml(item.html, themeCssData);\n\n const onLoad = () => {\n iframe.removeEventListener(\"load\", onLoad);\n // Wait for Tailwind CDN to process + images to load\n setTimeout(() => {\n try {\n const doc = iframe.contentDocument;\n if (!doc?.body) throw new Error(\"no doc\");\n const canvas = document.createElement(\"canvas\");\n canvas.width = THUMB_W * 2; // 2x for retina\n canvas.height = THUMB_H * 2;\n const ctx = canvas.getContext(\"2d\")!;\n ctx.scale(2, 2);\n\n // Use svg foreignObject to render HTML to canvas\n const svgData = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${THUMB_W}\" height=\"${THUMB_H}\">\n <foreignObject width=\"816\" height=\"1056\" transform=\"scale(${THUMB_W / 816})\">\n ${new XMLSerializer().serializeToString(doc.documentElement)}\n </foreignObject>\n </svg>`;\n const img = new Image();\n img.onload = () => {\n ctx.drawImage(img, 0, 0, THUMB_W, THUMB_H);\n const dataUrl = canvas.toDataURL(\"image/png\");\n setThumbs((prev) => ({ ...prev, [item.id]: dataUrl }));\n busyRef.current = false;\n processQueue();\n };\n img.onerror = () => {\n busyRef.current = false;\n processQueue();\n };\n img.src = \"data:image/svg+xml;charset=utf-8,\" + encodeURIComponent(svgData);\n } catch {\n busyRef.current = false;\n processQueue();\n }\n }, 800);\n };\n\n iframe.addEventListener(\"load\", onLoad);\n iframe.srcdoc = html;\n }, [themeCssData]);\n\n // Queue sections that changed\n useEffect(() => {\n clearTimeout(debounceRef.current);\n debounceRef.current = setTimeout(() => {\n const content = sections.filter((s) => s.id !== \"__grapes_css__\" && s.label !== \"__css__\");\n let changed = false;\n for (const s of content) {\n if (lastHtmlRef.current[s.id] !== s.html) {\n lastHtmlRef.current[s.id] = s.html;\n queueRef.current = queueRef.current.filter((q) => q.id !== s.id);\n queueRef.current.push({ id: s.id, html: s.html });\n changed = true;\n }\n }\n // Clean up removed sections\n const ids = new Set(content.map((s) => s.id));\n for (const id of Object.keys(lastHtmlRef.current)) {\n if (!ids.has(id)) delete lastHtmlRef.current[id];\n }\n if (changed) processQueue();\n }, 500);\n\n return () => clearTimeout(debounceRef.current);\n }, [sections, processQueue]);\n\n // Re-capture all when theme changes\n const prevThemeRef = useRef(themeCssData);\n useEffect(() => {\n if (prevThemeRef.current === themeCssData) return;\n prevThemeRef.current = themeCssData;\n lastHtmlRef.current = {};\n setThumbs({});\n }, [themeCssData]);\n\n // Cleanup iframe on unmount\n useEffect(() => {\n return () => {\n if (iframeRef.current) {\n iframeRef.current.remove();\n iframeRef.current = null;\n }\n };\n }, []);\n\n return thumbs;\n}\n"],"mappings":"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","names":["iframe"]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildThemePromptContext
|
|
3
|
-
} from "./chunk-VV5I53WR.js";
|
|
4
1
|
import {
|
|
5
2
|
currentDateLine,
|
|
6
3
|
enrichImages,
|
|
7
4
|
resolveModel,
|
|
8
5
|
sanitizeSemanticColors
|
|
9
6
|
} from "./chunk-YZHRLDQF.js";
|
|
7
|
+
import {
|
|
8
|
+
buildThemePromptContext
|
|
9
|
+
} from "./chunk-VV5I53WR.js";
|
|
10
10
|
|
|
11
11
|
// src/refine.ts
|
|
12
12
|
import { streamText } from "ai";
|
|
@@ -161,4 +161,4 @@ export {
|
|
|
161
161
|
extractSectionDescription,
|
|
162
162
|
refineLanding
|
|
163
163
|
};
|
|
164
|
-
//# sourceMappingURL=chunk-
|
|
164
|
+
//# sourceMappingURL=chunk-RJQKHWIH.js.map
|
package/dist/components.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { C as Canvas, a as CanvasHandle, b as CodeEditor, F as FloatingToolbar, S as SectionList, V as Viewport, c as ViewportToggle } from './ViewportToggle-
|
|
1
|
+
export { C as Canvas, a as CanvasHandle, b as CodeEditor, F as FloatingToolbar, S as SectionList, V as Viewport, c as ViewportToggle } from './ViewportToggle-Dszv1gsL.js';
|
|
2
2
|
import 'react';
|
|
3
3
|
import './types-BIpbpCJr.js';
|
|
4
4
|
import 'react/jsx-runtime';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Editor } from 'grapesjs';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
interface AiAction {
|
|
6
|
+
type: "refine-element";
|
|
7
|
+
componentId: string;
|
|
8
|
+
html: string;
|
|
9
|
+
sectionHtml?: string;
|
|
10
|
+
sectionComponentId?: string;
|
|
11
|
+
isSection?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface GrapesEditorHandle {
|
|
14
|
+
getEditor: () => Editor | null;
|
|
15
|
+
getHtml: () => string;
|
|
16
|
+
setHtml: (html: string) => void;
|
|
17
|
+
/** Replace a component's HTML by its GrapesJS ID */
|
|
18
|
+
replaceComponent: (componentId: string, newHtml: string) => void;
|
|
19
|
+
/** Toggle preview mode, returns new state */
|
|
20
|
+
togglePreview: () => boolean;
|
|
21
|
+
/** Toggle sw-visibility (component border guides), returns new state */
|
|
22
|
+
toggleSwVisibility: () => boolean;
|
|
23
|
+
/** Scroll the canvas iframe to a section by data-section-id */
|
|
24
|
+
scrollToSection: (sectionId: string) => void;
|
|
25
|
+
/** Set canvas zoom level (percentage, e.g. 50 = 50%) */
|
|
26
|
+
setZoom: (value: number) => void;
|
|
27
|
+
/** Get current canvas zoom level */
|
|
28
|
+
getZoom: () => number;
|
|
29
|
+
}
|
|
30
|
+
interface BrandKitItem {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
colors: Record<string, string>;
|
|
34
|
+
fonts?: {
|
|
35
|
+
heading?: string;
|
|
36
|
+
body?: string;
|
|
37
|
+
} | null;
|
|
38
|
+
mood?: string | null;
|
|
39
|
+
logoUrl?: string | null;
|
|
40
|
+
isDefault?: boolean;
|
|
41
|
+
}
|
|
42
|
+
interface Props$1 {
|
|
43
|
+
initialHtml: string;
|
|
44
|
+
theme?: string;
|
|
45
|
+
customColors?: Record<string, string>;
|
|
46
|
+
brandKits?: BrandKitItem[];
|
|
47
|
+
onChange?: (html: string) => void;
|
|
48
|
+
onAiAction?: (action: AiAction) => void;
|
|
49
|
+
onThemeChange?: (themeId: string, customColors?: Record<string, string>, brandKitId?: string) => void;
|
|
50
|
+
onBrandKitChange?: (brandKit: BrandKitItem | null) => void;
|
|
51
|
+
/** Persisted brand kit ID — restored from metadata on load */
|
|
52
|
+
initialBrandKitId?: string;
|
|
53
|
+
/** Hide specific panel tabs (e.g. ["blocks"] for documents) */
|
|
54
|
+
hiddenTabs?: PanelId[];
|
|
55
|
+
/** Extra CSS injected into the canvas iframe (e.g. document page sizing) */
|
|
56
|
+
canvasStyles?: string;
|
|
57
|
+
/** Set to false to hide device switcher (always single viewport) */
|
|
58
|
+
devices?: false;
|
|
59
|
+
/** Which side to render the panel sidebar — default "left" */
|
|
60
|
+
panelSide?: "left" | "right";
|
|
61
|
+
/** Override default blocks (LANDING_BLOCKS). Pass custom blocks for different editors (e.g. presentations). */
|
|
62
|
+
blocks?: {
|
|
63
|
+
id: string;
|
|
64
|
+
label: string;
|
|
65
|
+
category: string;
|
|
66
|
+
content: string | object;
|
|
67
|
+
media?: string;
|
|
68
|
+
}[];
|
|
69
|
+
/** Called when the most visible section changes due to canvas scroll */
|
|
70
|
+
onVisibleSectionChange?: (sectionId: string) => void;
|
|
71
|
+
}
|
|
72
|
+
declare const PANEL_TABS: readonly [{
|
|
73
|
+
readonly id: "blocks";
|
|
74
|
+
readonly label: "Bloques";
|
|
75
|
+
readonly icon: "⊞";
|
|
76
|
+
}, {
|
|
77
|
+
readonly id: "layers";
|
|
78
|
+
readonly label: "Capas";
|
|
79
|
+
readonly icon: "☰";
|
|
80
|
+
}, {
|
|
81
|
+
readonly id: "styles";
|
|
82
|
+
readonly label: "Estilos";
|
|
83
|
+
readonly icon: "◑";
|
|
84
|
+
}, {
|
|
85
|
+
readonly id: "themes";
|
|
86
|
+
readonly label: "Temas";
|
|
87
|
+
readonly icon: "◈";
|
|
88
|
+
}];
|
|
89
|
+
type PanelId = (typeof PANEL_TABS)[number]["id"];
|
|
90
|
+
declare const GrapesEditor: React.ForwardRefExoticComponent<Props$1 & React.RefAttributes<GrapesEditorHandle>>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Predefined blocks for GrapesJS editor.
|
|
94
|
+
* All section blocks use semantic colors (bg-primary, text-on-surface, etc.)
|
|
95
|
+
*/
|
|
96
|
+
declare const LANDING_BLOCKS: ({
|
|
97
|
+
id: string;
|
|
98
|
+
label: string;
|
|
99
|
+
category: string;
|
|
100
|
+
content: string;
|
|
101
|
+
media: string;
|
|
102
|
+
} | {
|
|
103
|
+
id: string;
|
|
104
|
+
label: string;
|
|
105
|
+
category: string;
|
|
106
|
+
content: {
|
|
107
|
+
type: string;
|
|
108
|
+
};
|
|
109
|
+
media: string;
|
|
110
|
+
})[];
|
|
111
|
+
|
|
112
|
+
interface Props {
|
|
113
|
+
editor: Editor | null;
|
|
114
|
+
/** Bumped when theme/customColors change — forces color preview re-resolve */
|
|
115
|
+
themeVersion?: number;
|
|
116
|
+
/** Resolved theme colors keyed by semantic name (e.g. "primary" → "#6366f1") */
|
|
117
|
+
themeColors?: Record<string, string>;
|
|
118
|
+
}
|
|
119
|
+
declare function TailwindClassEditor({ editor, themeVersion, themeColors }: Props): react_jsx_runtime.JSX.Element;
|
|
120
|
+
|
|
121
|
+
export { type AiAction, GrapesEditor, type GrapesEditorHandle, LANDING_BLOCKS, type PanelId, TailwindClassEditor };
|