@easybits.cloud/html-tailwind-generator 0.1.5 → 0.2.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/{src/images/enrichImages.ts → dist/chunk-5HSVOF2J.js} +65 -53
- package/dist/chunk-5HSVOF2J.js.map +1 -0
- package/{src/iframeScript.ts → dist/chunk-5TYGSZAF.js} +259 -10
- package/dist/chunk-5TYGSZAF.js.map +1 -0
- package/{src/refine.ts → dist/chunk-GMJR2GXL.js} +30 -60
- package/dist/chunk-GMJR2GXL.js.map +1 -0
- package/dist/chunk-LQ65H4AO.js +41 -0
- package/dist/chunk-LQ65H4AO.js.map +1 -0
- package/{src/generate.ts → dist/chunk-PK26CWDO.js} +67 -108
- package/dist/chunk-PK26CWDO.js.map +1 -0
- package/dist/chunk-RTGCZUNJ.js +1 -0
- package/dist/chunk-RTGCZUNJ.js.map +1 -0
- package/dist/chunk-XM3D3TTJ.js +852 -0
- package/dist/chunk-XM3D3TTJ.js.map +1 -0
- package/dist/components.d.ts +57 -0
- package/dist/components.js +14 -0
- package/dist/components.js.map +1 -0
- package/dist/deploy.d.ts +39 -0
- package/dist/deploy.js +10 -0
- package/dist/deploy.js.map +1 -0
- package/dist/generate.d.ts +41 -0
- package/dist/generate.js +14 -0
- package/dist/generate.js.map +1 -0
- package/dist/images.d.ts +30 -0
- package/dist/images.js +14 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/refine.d.ts +32 -0
- package/dist/refine.js +10 -0
- package/dist/refine.js.map +1 -0
- package/dist/themes-DOoj19c8.d.ts +35 -0
- package/dist/types-Flpl4wDs.d.ts +31 -0
- package/package.json +53 -50
- package/src/buildHtml.ts +0 -78
- package/src/components/Canvas.tsx +0 -162
- package/src/components/CodeEditor.tsx +0 -239
- package/src/components/FloatingToolbar.tsx +0 -350
- package/src/components/SectionList.tsx +0 -217
- package/src/components/index.ts +0 -4
- package/src/deploy.ts +0 -73
- package/src/images/dalleImages.ts +0 -29
- package/src/images/index.ts +0 -3
- package/src/images/pexels.ts +0 -27
- package/src/index.ts +0 -58
- package/src/themes.ts +0 -204
- package/src/types.ts +0 -30
|
@@ -1,13 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/images/pexels.ts
|
|
2
|
+
async function searchImage(query, apiKey) {
|
|
3
|
+
const key = apiKey || process.env.PEXELS_API_KEY;
|
|
4
|
+
if (!key) return null;
|
|
5
|
+
try {
|
|
6
|
+
const res = await fetch(
|
|
7
|
+
`https://api.pexels.com/v1/search?query=${encodeURIComponent(query)}&per_page=1&orientation=landscape`,
|
|
8
|
+
{ headers: { Authorization: key } }
|
|
9
|
+
);
|
|
10
|
+
if (!res.ok) return null;
|
|
11
|
+
const data = await res.json();
|
|
12
|
+
const photo = data.photos?.[0];
|
|
13
|
+
if (!photo) return null;
|
|
14
|
+
return {
|
|
15
|
+
url: photo.src.large,
|
|
16
|
+
photographer: photo.photographer,
|
|
17
|
+
alt: photo.alt || query
|
|
18
|
+
};
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
3
23
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
24
|
+
// src/images/dalleImages.ts
|
|
25
|
+
async function generateImage(query, openaiApiKey) {
|
|
26
|
+
const res = await fetch("https://api.openai.com/v1/images/generations", {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${openaiApiKey}`,
|
|
30
|
+
"Content-Type": "application/json"
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
model: "dall-e-3",
|
|
34
|
+
prompt: query,
|
|
35
|
+
n: 1,
|
|
36
|
+
size: "1792x1024"
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const err = await res.text().catch(() => "Unknown error");
|
|
41
|
+
throw new Error(`DALL-E API error ${res.status}: ${err}`);
|
|
42
|
+
}
|
|
43
|
+
const data = await res.json();
|
|
44
|
+
return data.data[0].url;
|
|
8
45
|
}
|
|
9
46
|
|
|
10
|
-
|
|
47
|
+
// src/images/enrichImages.ts
|
|
48
|
+
var FAKE_DOMAINS = [
|
|
11
49
|
"images.unsplash.com",
|
|
12
50
|
"unsplash.com",
|
|
13
51
|
"via.placeholder.com",
|
|
@@ -22,22 +60,13 @@ const FAKE_DOMAINS = [
|
|
|
22
60
|
"fakeimg.pl",
|
|
23
61
|
"example.com",
|
|
24
62
|
"img.freepik.com",
|
|
25
|
-
"cdn.pixabay.com"
|
|
63
|
+
"cdn.pixabay.com"
|
|
26
64
|
];
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* Two strategies:
|
|
31
|
-
* 1. data-image-query="..." — AI followed instructions
|
|
32
|
-
* 2. <img src="fake-url" — detect fake domains, use alt/class/nearby text as query
|
|
33
|
-
*/
|
|
34
|
-
export function findImageSlots(html: string): ImageMatch[] {
|
|
35
|
-
const matches: ImageMatch[] = [];
|
|
36
|
-
const seen = new Set<string>();
|
|
37
|
-
|
|
38
|
-
// 1. data-image-query="..."
|
|
65
|
+
function findImageSlots(html) {
|
|
66
|
+
const matches = [];
|
|
67
|
+
const seen = /* @__PURE__ */ new Set();
|
|
39
68
|
const diqRegex = /data-image-query="([^"]+)"/g;
|
|
40
|
-
let m
|
|
69
|
+
let m;
|
|
41
70
|
while ((m = diqRegex.exec(html)) !== null) {
|
|
42
71
|
const query = m[1];
|
|
43
72
|
if (seen.has(query)) continue;
|
|
@@ -45,21 +74,16 @@ export function findImageSlots(html: string): ImageMatch[] {
|
|
|
45
74
|
matches.push({
|
|
46
75
|
query,
|
|
47
76
|
searchStr: `data-image-query="${query}"`,
|
|
48
|
-
replaceStr: `src="{url}" data-enriched="true"
|
|
77
|
+
replaceStr: `src="{url}" data-enriched="true"`
|
|
49
78
|
});
|
|
50
79
|
}
|
|
51
|
-
|
|
52
|
-
// 2. <img with fake/non-existent src URLs
|
|
53
80
|
const imgRegex = /<img\s[^>]*src="(https?:\/\/[^"]+)"[^>]*>/gi;
|
|
54
81
|
while ((m = imgRegex.exec(html)) !== null) {
|
|
55
82
|
const fullTag = m[0];
|
|
56
83
|
const srcUrl = m[1];
|
|
57
|
-
|
|
58
84
|
if (fullTag.includes("data-enriched")) continue;
|
|
59
85
|
if (srcUrl.includes("pexels.com")) continue;
|
|
60
86
|
if (seen.has(srcUrl)) continue;
|
|
61
|
-
|
|
62
|
-
// Check if domain is fake
|
|
63
87
|
let isFake = false;
|
|
64
88
|
try {
|
|
65
89
|
const domain = new URL(srcUrl).hostname;
|
|
@@ -68,48 +92,32 @@ export function findImageSlots(html: string): ImageMatch[] {
|
|
|
68
92
|
isFake = true;
|
|
69
93
|
}
|
|
70
94
|
if (!isFake) continue;
|
|
71
|
-
|
|
72
|
-
// Extract query: try alt, then class context, then URL path words
|
|
73
95
|
const altMatch = fullTag.match(/alt="([^"]*?)"/);
|
|
74
96
|
let query = altMatch?.[1]?.trim() || "";
|
|
75
|
-
|
|
76
97
|
if (!query) {
|
|
77
|
-
// Try to extract meaningful words from the URL path
|
|
78
98
|
try {
|
|
79
99
|
const path = new URL(srcUrl).pathname;
|
|
80
|
-
const words = path
|
|
81
|
-
.replace(/[^a-zA-Z]/g, " ")
|
|
82
|
-
.split(/\s+/)
|
|
83
|
-
.filter((w) => w.length > 2)
|
|
84
|
-
.slice(0, 4)
|
|
85
|
-
.join(" ");
|
|
100
|
+
const words = path.replace(/[^a-zA-Z]/g, " ").split(/\s+/).filter((w) => w.length > 2).slice(0, 4).join(" ");
|
|
86
101
|
if (words.length > 3) query = words;
|
|
87
|
-
} catch {
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
88
104
|
}
|
|
89
|
-
|
|
90
105
|
if (!query) query = "professional website hero image";
|
|
91
|
-
|
|
92
106
|
seen.add(srcUrl);
|
|
93
107
|
matches.push({
|
|
94
108
|
query,
|
|
95
109
|
searchStr: `src="${srcUrl}"`,
|
|
96
|
-
replaceStr: `src="{url}" data-enriched="true"
|
|
110
|
+
replaceStr: `src="{url}" data-enriched="true"`
|
|
97
111
|
});
|
|
98
112
|
}
|
|
99
|
-
|
|
100
113
|
return matches;
|
|
101
114
|
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Enrich all images in an HTML string with Pexels photos.
|
|
105
|
-
*/
|
|
106
|
-
export async function enrichImages(html: string, pexelsApiKey?: string, openaiApiKey?: string): Promise<string> {
|
|
115
|
+
async function enrichImages(html, pexelsApiKey, openaiApiKey) {
|
|
107
116
|
const slots = findImageSlots(html);
|
|
108
117
|
if (slots.length === 0) return html;
|
|
109
|
-
|
|
110
118
|
let result = html;
|
|
111
119
|
const promises = slots.map(async (slot) => {
|
|
112
|
-
let url
|
|
120
|
+
let url = null;
|
|
113
121
|
if (openaiApiKey) {
|
|
114
122
|
url = await generateImage(slot.query, openaiApiKey).catch(() => null);
|
|
115
123
|
}
|
|
@@ -121,15 +129,19 @@ export async function enrichImages(html: string, pexelsApiKey?: string, openaiAp
|
|
|
121
129
|
const replacement = slot.replaceStr.replace("{url}", url);
|
|
122
130
|
result = result.replaceAll(slot.searchStr, replacement);
|
|
123
131
|
});
|
|
124
|
-
|
|
125
132
|
await Promise.allSettled(promises);
|
|
126
|
-
|
|
127
|
-
// Catch any remaining <img> tags without src (AI didn't follow instructions)
|
|
128
133
|
result = result.replace(/<img\s(?![^>]*\bsrc=)([^>]*?)>/gi, (_match, attrs) => {
|
|
129
134
|
const altMatch = attrs.match(/alt="([^"]*?)"/);
|
|
130
135
|
const query = altMatch?.[1] || "professional image";
|
|
131
136
|
return `<img src="https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}" ${attrs}>`;
|
|
132
137
|
});
|
|
133
|
-
|
|
134
138
|
return result;
|
|
135
139
|
}
|
|
140
|
+
|
|
141
|
+
export {
|
|
142
|
+
searchImage,
|
|
143
|
+
generateImage,
|
|
144
|
+
findImageSlots,
|
|
145
|
+
enrichImages
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=chunk-5HSVOF2J.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/images/pexels.ts","../src/images/dalleImages.ts","../src/images/enrichImages.ts"],"sourcesContent":["export interface PexelsResult {\n url: string;\n photographer: string;\n alt: string;\n}\n\nexport async function searchImage(query: string, apiKey?: string): Promise<PexelsResult | null> {\n const key = apiKey || process.env.PEXELS_API_KEY;\n if (!key) return null;\n try {\n const res = await fetch(\n `https://api.pexels.com/v1/search?query=${encodeURIComponent(query)}&per_page=1&orientation=landscape`,\n { headers: { Authorization: key } }\n );\n if (!res.ok) return null;\n const data = await res.json();\n const photo = data.photos?.[0];\n if (!photo) return null;\n return {\n url: photo.src.large,\n photographer: photo.photographer,\n alt: photo.alt || query,\n };\n } catch {\n return null;\n }\n}\n","/**\n * Generate an image using DALL-E 3 API.\n */\nexport async function generateImage(\n query: string,\n openaiApiKey: string\n): Promise<string> {\n const res = await fetch(\"https://api.openai.com/v1/images/generations\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${openaiApiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model: \"dall-e-3\",\n prompt: query,\n n: 1,\n size: \"1792x1024\",\n }),\n });\n\n if (!res.ok) {\n const err = await res.text().catch(() => \"Unknown error\");\n throw new Error(`DALL-E API error ${res.status}: ${err}`);\n }\n\n const data = await res.json();\n return data.data[0].url;\n}\n","import { searchImage } from \"./pexels\";\nimport { generateImage } from \"./dalleImages\";\n\ninterface ImageMatch {\n query: string;\n searchStr: string;\n replaceStr: string;\n}\n\nconst FAKE_DOMAINS = [\n \"images.unsplash.com\",\n \"unsplash.com\",\n \"via.placeholder.com\",\n \"placeholder.com\",\n \"placehold.co\",\n \"placehold.it\",\n \"placekitten.com\",\n \"picsum.photos\",\n \"loremflickr.com\",\n \"source.unsplash.com\",\n \"dummyimage.com\",\n \"fakeimg.pl\",\n \"example.com\",\n \"img.freepik.com\",\n \"cdn.pixabay.com\",\n];\n\n/**\n * Find all images in HTML that need Pexels enrichment.\n * Two strategies:\n * 1. data-image-query=\"...\" — AI followed instructions\n * 2. <img src=\"fake-url\" — detect fake domains, use alt/class/nearby text as query\n */\nexport function findImageSlots(html: string): ImageMatch[] {\n const matches: ImageMatch[] = [];\n const seen = new Set<string>();\n\n // 1. data-image-query=\"...\"\n const diqRegex = /data-image-query=\"([^\"]+)\"/g;\n let m: RegExpExecArray | null;\n while ((m = diqRegex.exec(html)) !== null) {\n const query = m[1];\n if (seen.has(query)) continue;\n seen.add(query);\n matches.push({\n query,\n searchStr: `data-image-query=\"${query}\"`,\n replaceStr: `src=\"{url}\" data-enriched=\"true\"`,\n });\n }\n\n // 2. <img with fake/non-existent src URLs\n const imgRegex = /<img\\s[^>]*src=\"(https?:\\/\\/[^\"]+)\"[^>]*>/gi;\n while ((m = imgRegex.exec(html)) !== null) {\n const fullTag = m[0];\n const srcUrl = m[1];\n\n if (fullTag.includes(\"data-enriched\")) continue;\n if (srcUrl.includes(\"pexels.com\")) continue;\n if (seen.has(srcUrl)) continue;\n\n // Check if domain is fake\n let isFake = false;\n try {\n const domain = new URL(srcUrl).hostname;\n isFake = FAKE_DOMAINS.some((d) => domain.includes(d));\n } catch {\n isFake = true;\n }\n if (!isFake) continue;\n\n // Extract query: try alt, then class context, then URL path words\n const altMatch = fullTag.match(/alt=\"([^\"]*?)\"/);\n let query = altMatch?.[1]?.trim() || \"\";\n\n if (!query) {\n // Try to extract meaningful words from the URL path\n try {\n const path = new URL(srcUrl).pathname;\n const words = path\n .replace(/[^a-zA-Z]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length > 2)\n .slice(0, 4)\n .join(\" \");\n if (words.length > 3) query = words;\n } catch { /* ignore */ }\n }\n\n if (!query) query = \"professional website hero image\";\n\n seen.add(srcUrl);\n matches.push({\n query,\n searchStr: `src=\"${srcUrl}\"`,\n replaceStr: `src=\"{url}\" data-enriched=\"true\"`,\n });\n }\n\n return matches;\n}\n\n/**\n * Enrich all images in an HTML string with Pexels photos.\n */\nexport async function enrichImages(html: string, pexelsApiKey?: string, openaiApiKey?: string): Promise<string> {\n const slots = findImageSlots(html);\n if (slots.length === 0) return html;\n\n let result = html;\n const promises = slots.map(async (slot) => {\n let url: string | null = null;\n if (openaiApiKey) {\n url = await generateImage(slot.query, openaiApiKey).catch(() => null);\n }\n if (!url) {\n const img = await searchImage(slot.query, pexelsApiKey).catch(() => null);\n url = img?.url || null;\n }\n url ??= `https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(slot.query.slice(0, 30))}`;\n const replacement = slot.replaceStr.replace(\"{url}\", url);\n result = result.replaceAll(slot.searchStr, replacement);\n });\n\n await Promise.allSettled(promises);\n\n // Catch any remaining <img> tags without src (AI didn't follow instructions)\n result = result.replace(/<img\\s(?![^>]*\\bsrc=)([^>]*?)>/gi, (_match, attrs) => {\n const altMatch = attrs.match(/alt=\"([^\"]*?)\"/);\n const query = altMatch?.[1] || \"professional image\";\n return `<img src=\"https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}\" ${attrs}>`;\n });\n\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,10 +1,173 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
// src/themes.ts
|
|
2
|
+
var LANDING_THEMES = [
|
|
3
|
+
{
|
|
4
|
+
id: "default",
|
|
5
|
+
label: "Neutral",
|
|
6
|
+
colors: {
|
|
7
|
+
primary: "#18181b",
|
|
8
|
+
"primary-light": "#3f3f46",
|
|
9
|
+
"primary-dark": "#09090b",
|
|
10
|
+
secondary: "#a1a1aa",
|
|
11
|
+
accent: "#18181b",
|
|
12
|
+
surface: "#ffffff",
|
|
13
|
+
"surface-alt": "#fafafa",
|
|
14
|
+
"on-surface": "#18181b",
|
|
15
|
+
"on-surface-muted": "#71717a",
|
|
16
|
+
"on-primary": "#ffffff"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "dark",
|
|
21
|
+
label: "Dark",
|
|
22
|
+
colors: {
|
|
23
|
+
primary: "#e4e4e7",
|
|
24
|
+
"primary-light": "#f4f4f5",
|
|
25
|
+
"primary-dark": "#a1a1aa",
|
|
26
|
+
secondary: "#71717a",
|
|
27
|
+
accent: "#a78bfa",
|
|
28
|
+
surface: "#09090b",
|
|
29
|
+
"surface-alt": "#18181b",
|
|
30
|
+
"on-surface": "#fafafa",
|
|
31
|
+
"on-surface-muted": "#a1a1aa",
|
|
32
|
+
"on-primary": "#09090b"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "slate",
|
|
37
|
+
label: "Slate",
|
|
38
|
+
colors: {
|
|
39
|
+
primary: "#3b82f6",
|
|
40
|
+
"primary-light": "#60a5fa",
|
|
41
|
+
"primary-dark": "#2563eb",
|
|
42
|
+
secondary: "#64748b",
|
|
43
|
+
accent: "#3b82f6",
|
|
44
|
+
surface: "#ffffff",
|
|
45
|
+
"surface-alt": "#f8fafc",
|
|
46
|
+
"on-surface": "#0f172a",
|
|
47
|
+
"on-surface-muted": "#64748b",
|
|
48
|
+
"on-primary": "#ffffff"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "midnight",
|
|
53
|
+
label: "Midnight",
|
|
54
|
+
colors: {
|
|
55
|
+
primary: "#6366f1",
|
|
56
|
+
"primary-light": "#818cf8",
|
|
57
|
+
"primary-dark": "#4f46e5",
|
|
58
|
+
secondary: "#94a3b8",
|
|
59
|
+
accent: "#a78bfa",
|
|
60
|
+
surface: "#0f172a",
|
|
61
|
+
"surface-alt": "#1e293b",
|
|
62
|
+
"on-surface": "#e2e8f0",
|
|
63
|
+
"on-surface-muted": "#94a3b8",
|
|
64
|
+
"on-primary": "#ffffff"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "warm",
|
|
69
|
+
label: "Warm",
|
|
70
|
+
colors: {
|
|
71
|
+
primary: "#b45309",
|
|
72
|
+
"primary-light": "#d97706",
|
|
73
|
+
"primary-dark": "#92400e",
|
|
74
|
+
secondary: "#78716c",
|
|
75
|
+
accent: "#b45309",
|
|
76
|
+
surface: "#fafaf9",
|
|
77
|
+
"surface-alt": "#f5f5f4",
|
|
78
|
+
"on-surface": "#1c1917",
|
|
79
|
+
"on-surface-muted": "#78716c",
|
|
80
|
+
"on-primary": "#ffffff"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
];
|
|
84
|
+
function parseHex(hex) {
|
|
85
|
+
return {
|
|
86
|
+
r: parseInt(hex.slice(1, 3), 16),
|
|
87
|
+
g: parseInt(hex.slice(3, 5), 16),
|
|
88
|
+
b: parseInt(hex.slice(5, 7), 16)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function toHex(r, g, b) {
|
|
92
|
+
return `#${[r, g, b].map((c) => Math.max(0, Math.min(255, c)).toString(16).padStart(2, "0")).join("")}`;
|
|
93
|
+
}
|
|
94
|
+
function luminance(hex) {
|
|
95
|
+
const { r, g, b } = parseHex(hex);
|
|
96
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
97
|
+
}
|
|
98
|
+
function lighten(hex, amount = 40) {
|
|
99
|
+
const { r, g, b } = parseHex(hex);
|
|
100
|
+
return toHex(r + amount, g + amount, b + amount);
|
|
101
|
+
}
|
|
102
|
+
function darken(hex, amount = 40) {
|
|
103
|
+
const { r, g, b } = parseHex(hex);
|
|
104
|
+
return toHex(r - amount, g - amount, b - amount);
|
|
105
|
+
}
|
|
106
|
+
function buildCustomTheme(colors) {
|
|
107
|
+
const { primary, secondary = "#f59e0b", accent = "#06b6d4", surface = "#ffffff" } = colors;
|
|
108
|
+
const onPrimary = luminance(primary) > 0.5 ? "#111827" : "#ffffff";
|
|
109
|
+
const surfaceLum = luminance(surface);
|
|
110
|
+
const isDarkSurface = surfaceLum < 0.5;
|
|
111
|
+
return {
|
|
112
|
+
id: "custom",
|
|
113
|
+
label: "Custom",
|
|
114
|
+
colors: {
|
|
115
|
+
primary,
|
|
116
|
+
"primary-light": lighten(primary),
|
|
117
|
+
"primary-dark": darken(primary),
|
|
118
|
+
secondary,
|
|
119
|
+
accent,
|
|
120
|
+
surface,
|
|
121
|
+
"surface-alt": isDarkSurface ? lighten(surface, 20) : darken(surface, 5),
|
|
122
|
+
"on-surface": isDarkSurface ? "#f1f5f9" : "#111827",
|
|
123
|
+
"on-surface-muted": isDarkSurface ? "#94a3b8" : "#6b7280",
|
|
124
|
+
"on-primary": onPrimary
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function buildCustomThemeCss(colors) {
|
|
129
|
+
const theme = buildCustomTheme(colors);
|
|
130
|
+
return `[data-theme="custom"] {
|
|
131
|
+
${buildCssVars(theme.colors)}
|
|
132
|
+
}`;
|
|
133
|
+
}
|
|
134
|
+
function buildCssVars(colors) {
|
|
135
|
+
return Object.entries(colors).map(([k, v]) => ` --color-${k}: ${v};`).join("\n");
|
|
136
|
+
}
|
|
137
|
+
function buildTailwindConfig() {
|
|
138
|
+
const colorEntries = Object.keys(LANDING_THEMES[0].colors).map((k) => ` '${k}': 'var(--color-${k})'`).join(",\n");
|
|
139
|
+
return `{
|
|
140
|
+
theme: {
|
|
141
|
+
extend: {
|
|
142
|
+
colors: {
|
|
143
|
+
${colorEntries}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}`;
|
|
148
|
+
}
|
|
149
|
+
function buildThemeCss() {
|
|
150
|
+
const defaultTheme = LANDING_THEMES[0];
|
|
151
|
+
const overrides = LANDING_THEMES.slice(1).map((t) => `[data-theme="${t.id}"] {
|
|
152
|
+
${buildCssVars(t.colors)}
|
|
153
|
+
}`).join("\n\n");
|
|
154
|
+
const css = `:root {
|
|
155
|
+
${buildCssVars(defaultTheme.colors)}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
${overrides}`;
|
|
159
|
+
return { css, tailwindConfig: buildTailwindConfig() };
|
|
160
|
+
}
|
|
161
|
+
function buildSingleThemeCss(themeId) {
|
|
162
|
+
const theme = LANDING_THEMES.find((t) => t.id === themeId) || LANDING_THEMES[0];
|
|
163
|
+
const css = `:root {
|
|
164
|
+
${buildCssVars(theme.colors)}
|
|
165
|
+
}`;
|
|
166
|
+
return { css, tailwindConfig: buildTailwindConfig() };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/iframeScript.ts
|
|
170
|
+
function getIframeScript() {
|
|
8
171
|
return `
|
|
9
172
|
(function() {
|
|
10
173
|
let hoveredEl = null;
|
|
@@ -70,7 +233,7 @@ export function getIframeScript(): string {
|
|
|
70
233
|
hoveredEl = null;
|
|
71
234
|
});
|
|
72
235
|
|
|
73
|
-
// Click
|
|
236
|
+
// Click \u2014 select element
|
|
74
237
|
document.addEventListener('click', function(e) {
|
|
75
238
|
e.preventDefault();
|
|
76
239
|
e.stopPropagation();
|
|
@@ -120,7 +283,7 @@ export function getIframeScript(): string {
|
|
|
120
283
|
}, '*');
|
|
121
284
|
}, true);
|
|
122
285
|
|
|
123
|
-
// Double-click
|
|
286
|
+
// Double-click \u2014 contentEditable for text
|
|
124
287
|
document.addEventListener('dblclick', function(e) {
|
|
125
288
|
e.preventDefault();
|
|
126
289
|
e.stopPropagation();
|
|
@@ -178,7 +341,19 @@ export function getIframeScript(): string {
|
|
|
178
341
|
|
|
179
342
|
if (msg.action === 'update-section') {
|
|
180
343
|
var el = getSectionElement(msg.id);
|
|
181
|
-
if (el
|
|
344
|
+
if (el && typeof window.morphdom === 'function') {
|
|
345
|
+
var tmp = document.createElement('div');
|
|
346
|
+
tmp.innerHTML = msg.html;
|
|
347
|
+
window.morphdom(el, tmp, {
|
|
348
|
+
childrenOnly: true,
|
|
349
|
+
onBeforeElUpdated: function(fromEl, toEl) {
|
|
350
|
+
if (fromEl.isEqualNode(toEl)) return false;
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
} else if (el) {
|
|
355
|
+
el.innerHTML = msg.html;
|
|
356
|
+
}
|
|
182
357
|
}
|
|
183
358
|
|
|
184
359
|
if (msg.action === 'remove-section') {
|
|
@@ -259,3 +434,77 @@ export function getIframeScript(): string {
|
|
|
259
434
|
})();
|
|
260
435
|
`;
|
|
261
436
|
}
|
|
437
|
+
|
|
438
|
+
// src/buildHtml.ts
|
|
439
|
+
function buildPreviewHtml(sections, theme) {
|
|
440
|
+
const sorted = [...sections].sort((a, b) => a.order - b.order);
|
|
441
|
+
const body = sorted.map((s) => `<div data-section-id="${s.id}">${s.html}</div>`).join("\n");
|
|
442
|
+
const dataTheme = theme && theme !== "default" ? ` data-theme="${theme}"` : "";
|
|
443
|
+
const { css, tailwindConfig } = buildThemeCss();
|
|
444
|
+
return `<!DOCTYPE html>
|
|
445
|
+
<html lang="es"${dataTheme}>
|
|
446
|
+
<head>
|
|
447
|
+
<meta charset="UTF-8"/>
|
|
448
|
+
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
449
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
450
|
+
<script src="https://unpkg.com/morphdom@2.7.4/dist/morphdom-umd.min.js"></script>
|
|
451
|
+
<script>tailwind.config = ${tailwindConfig}</script>
|
|
452
|
+
<style>
|
|
453
|
+
${css}
|
|
454
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
455
|
+
html{scroll-behavior:smooth}
|
|
456
|
+
body{font-family:system-ui,-apple-system,sans-serif;background-color:var(--color-surface);color:var(--color-on-surface)}
|
|
457
|
+
img{max-width:100%}
|
|
458
|
+
[contenteditable="true"]{cursor:text}
|
|
459
|
+
</style>
|
|
460
|
+
</head>
|
|
461
|
+
<body class="bg-surface text-on-surface">
|
|
462
|
+
${body}
|
|
463
|
+
<script>${getIframeScript()}</script>
|
|
464
|
+
</body>
|
|
465
|
+
</html>`;
|
|
466
|
+
}
|
|
467
|
+
function buildDeployHtml(sections, theme, customColors) {
|
|
468
|
+
const sorted = [...sections].sort((a, b) => a.order - b.order);
|
|
469
|
+
const body = sorted.map((s) => s.html).join("\n");
|
|
470
|
+
const isCustom = theme === "custom" && customColors;
|
|
471
|
+
const dataTheme = theme && theme !== "default" && !isCustom ? ` data-theme="${theme}"` : "";
|
|
472
|
+
const { css: baseCss, tailwindConfig } = isCustom ? (() => {
|
|
473
|
+
const ct = buildCustomTheme(customColors);
|
|
474
|
+
const vars = Object.entries(ct.colors).map(([k, v]) => ` --color-${k}: ${v};`).join("\n");
|
|
475
|
+
return { css: `:root {
|
|
476
|
+
${vars}
|
|
477
|
+
}`, tailwindConfig: buildSingleThemeCss("default").tailwindConfig };
|
|
478
|
+
})() : buildSingleThemeCss(theme || "default");
|
|
479
|
+
return `<!DOCTYPE html>
|
|
480
|
+
<html lang="es"${dataTheme}>
|
|
481
|
+
<head>
|
|
482
|
+
<meta charset="UTF-8"/>
|
|
483
|
+
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
484
|
+
<title>Landing Page</title>
|
|
485
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
486
|
+
<script>tailwind.config = ${tailwindConfig}</script>
|
|
487
|
+
<style>
|
|
488
|
+
${baseCss}
|
|
489
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
490
|
+
html{scroll-behavior:smooth}
|
|
491
|
+
body{font-family:system-ui,-apple-system,sans-serif;background-color:var(--color-surface);color:var(--color-on-surface)}
|
|
492
|
+
</style>
|
|
493
|
+
</head>
|
|
494
|
+
<body class="bg-surface text-on-surface">
|
|
495
|
+
${body}
|
|
496
|
+
</body>
|
|
497
|
+
</html>`;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export {
|
|
501
|
+
LANDING_THEMES,
|
|
502
|
+
buildCustomTheme,
|
|
503
|
+
buildCustomThemeCss,
|
|
504
|
+
buildThemeCss,
|
|
505
|
+
buildSingleThemeCss,
|
|
506
|
+
getIframeScript,
|
|
507
|
+
buildPreviewHtml,
|
|
508
|
+
buildDeployHtml
|
|
509
|
+
};
|
|
510
|
+
//# sourceMappingURL=chunk-5TYGSZAF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/themes.ts","../src/iframeScript.ts","../src/buildHtml.ts"],"sourcesContent":["export interface LandingTheme {\n id: string;\n label: string;\n colors: {\n primary: string;\n \"primary-light\": string;\n \"primary-dark\": string;\n secondary: string;\n accent: string;\n surface: string;\n \"surface-alt\": string;\n \"on-surface\": string;\n \"on-surface-muted\": string;\n \"on-primary\": string;\n };\n}\n\nexport const LANDING_THEMES: LandingTheme[] = [\n {\n id: \"default\",\n label: \"Neutral\",\n colors: {\n primary: \"#18181b\",\n \"primary-light\": \"#3f3f46\",\n \"primary-dark\": \"#09090b\",\n secondary: \"#a1a1aa\",\n accent: \"#18181b\",\n surface: \"#ffffff\",\n \"surface-alt\": \"#fafafa\",\n \"on-surface\": \"#18181b\",\n \"on-surface-muted\": \"#71717a\",\n \"on-primary\": \"#ffffff\",\n },\n },\n {\n id: \"dark\",\n label: \"Dark\",\n colors: {\n primary: \"#e4e4e7\",\n \"primary-light\": \"#f4f4f5\",\n \"primary-dark\": \"#a1a1aa\",\n secondary: \"#71717a\",\n accent: \"#a78bfa\",\n surface: \"#09090b\",\n \"surface-alt\": \"#18181b\",\n \"on-surface\": \"#fafafa\",\n \"on-surface-muted\": \"#a1a1aa\",\n \"on-primary\": \"#09090b\",\n },\n },\n {\n id: \"slate\",\n label: \"Slate\",\n colors: {\n primary: \"#3b82f6\",\n \"primary-light\": \"#60a5fa\",\n \"primary-dark\": \"#2563eb\",\n secondary: \"#64748b\",\n accent: \"#3b82f6\",\n surface: \"#ffffff\",\n \"surface-alt\": \"#f8fafc\",\n \"on-surface\": \"#0f172a\",\n \"on-surface-muted\": \"#64748b\",\n \"on-primary\": \"#ffffff\",\n },\n },\n {\n id: \"midnight\",\n label: \"Midnight\",\n colors: {\n primary: \"#6366f1\",\n \"primary-light\": \"#818cf8\",\n \"primary-dark\": \"#4f46e5\",\n secondary: \"#94a3b8\",\n accent: \"#a78bfa\",\n surface: \"#0f172a\",\n \"surface-alt\": \"#1e293b\",\n \"on-surface\": \"#e2e8f0\",\n \"on-surface-muted\": \"#94a3b8\",\n \"on-primary\": \"#ffffff\",\n },\n },\n {\n id: \"warm\",\n label: \"Warm\",\n colors: {\n primary: \"#b45309\",\n \"primary-light\": \"#d97706\",\n \"primary-dark\": \"#92400e\",\n secondary: \"#78716c\",\n accent: \"#b45309\",\n surface: \"#fafaf9\",\n \"surface-alt\": \"#f5f5f4\",\n \"on-surface\": \"#1c1917\",\n \"on-surface-muted\": \"#78716c\",\n \"on-primary\": \"#ffffff\",\n },\n },\n];\n\nexport interface CustomColors {\n primary: string;\n secondary?: string;\n accent?: string;\n surface?: string;\n}\n\nfunction parseHex(hex: string) {\n return {\n r: parseInt(hex.slice(1, 3), 16),\n g: parseInt(hex.slice(3, 5), 16),\n b: parseInt(hex.slice(5, 7), 16),\n };\n}\n\nfunction toHex(r: number, g: number, b: number) {\n return `#${[r, g, b].map((c) => Math.max(0, Math.min(255, c)).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nfunction luminance(hex: string) {\n const { r, g, b } = parseHex(hex);\n return (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n}\n\nfunction lighten(hex: string, amount = 40) {\n const { r, g, b } = parseHex(hex);\n return toHex(r + amount, g + amount, b + amount);\n}\n\nfunction darken(hex: string, amount = 40) {\n const { r, g, b } = parseHex(hex);\n return toHex(r - amount, g - amount, b - amount);\n}\n\nexport function buildCustomTheme(colors: CustomColors): LandingTheme {\n const { primary, secondary = \"#f59e0b\", accent = \"#06b6d4\", surface = \"#ffffff\" } = colors;\n\n const onPrimary = luminance(primary) > 0.5 ? \"#111827\" : \"#ffffff\";\n const surfaceLum = luminance(surface);\n const isDarkSurface = surfaceLum < 0.5;\n\n return {\n id: \"custom\",\n label: \"Custom\",\n colors: {\n primary,\n \"primary-light\": lighten(primary),\n \"primary-dark\": darken(primary),\n secondary,\n accent,\n surface,\n \"surface-alt\": isDarkSurface ? lighten(surface, 20) : darken(surface, 5),\n \"on-surface\": isDarkSurface ? \"#f1f5f9\" : \"#111827\",\n \"on-surface-muted\": isDarkSurface ? \"#94a3b8\" : \"#6b7280\",\n \"on-primary\": onPrimary,\n },\n };\n}\n\nexport function buildCustomThemeCss(colors: CustomColors): string {\n const theme = buildCustomTheme(colors);\n return `[data-theme=\"custom\"] {\\n${buildCssVars(theme.colors)}\\n}`;\n}\n\n/** CSS custom properties for a theme */\nfunction buildCssVars(colors: LandingTheme[\"colors\"]): string {\n return Object.entries(colors)\n .map(([k, v]) => ` --color-${k}: ${v};`)\n .join(\"\\n\");\n}\n\n/** Build the tailwind.config JS object string for TW v3 CDN */\nfunction buildTailwindConfig(): string {\n const colorEntries = Object.keys(LANDING_THEMES[0].colors)\n .map((k) => ` '${k}': 'var(--color-${k})'`)\n .join(\",\\n\");\n\n return `{\n theme: {\n extend: {\n colors: {\n${colorEntries}\n }\n }\n }\n }`;\n}\n\nexport function buildThemeCss(): { css: string; tailwindConfig: string } {\n const defaultTheme = LANDING_THEMES[0];\n\n const overrides = LANDING_THEMES.slice(1)\n .map((t) => `[data-theme=\"${t.id}\"] {\\n${buildCssVars(t.colors)}\\n}`)\n .join(\"\\n\\n\");\n\n const css = `:root {\\n${buildCssVars(defaultTheme.colors)}\\n}\\n\\n${overrides}`;\n return { css, tailwindConfig: buildTailwindConfig() };\n}\n\nexport function buildSingleThemeCss(themeId: string): { css: string; tailwindConfig: string } {\n const theme = LANDING_THEMES.find((t) => t.id === themeId) || LANDING_THEMES[0];\n const css = `:root {\\n${buildCssVars(theme.colors)}\\n}`;\n return { css, tailwindConfig: buildTailwindConfig() };\n}\n","/**\n * JavaScript injected into the landing v3 iframe.\n * Handles hover highlights, click selection, contentEditable text editing,\n * postMessage communication with the parent editor,\n * and incremental section injection from parent.\n */\nexport function getIframeScript(): string {\n return `\n(function() {\n let hoveredEl = null;\n let selectedEl = null;\n const OUTLINE_HOVER = '2px solid #3B82F6';\n const OUTLINE_SELECTED = '2px solid #8B5CF6';\n\n function getSectionId(el) {\n let node = el;\n while (node && node !== document.body) {\n if (node.dataset && node.dataset.sectionId) {\n return node.dataset.sectionId;\n }\n node = node.parentElement;\n }\n return null;\n }\n\n function getSectionElement(sectionId) {\n return document.querySelector('[data-section-id=\"' + sectionId + '\"]');\n }\n\n function getElementPath(el) {\n const parts = [];\n let node = el;\n while (node && node !== document.body) {\n let tag = node.tagName.toLowerCase();\n if (node.id) { tag += '#' + node.id; }\n const siblings = node.parentElement ? Array.from(node.parentElement.children).filter(function(c) { return c.tagName === node.tagName; }) : [];\n if (siblings.length > 1) { tag += ':nth(' + siblings.indexOf(node) + ')'; }\n parts.unshift(tag);\n node = node.parentElement;\n }\n return parts.join(' > ');\n }\n\n function isTextElement(el) {\n var textTags = ['H1','H2','H3','H4','H5','H6','P','SPAN','LI','A','BLOCKQUOTE','LABEL','TD','TH','FIGCAPTION','BUTTON'];\n return textTags.indexOf(el.tagName) !== -1;\n }\n\n // Hover\n document.addEventListener('mouseover', function(e) {\n var el = e.target;\n if (el === document.body || el === document.documentElement) return;\n if (el === selectedEl) return;\n if (hoveredEl && hoveredEl !== selectedEl) {\n hoveredEl.style.outline = '';\n hoveredEl.style.outlineOffset = '';\n }\n hoveredEl = el;\n if (el !== selectedEl) {\n el.style.outline = OUTLINE_HOVER;\n el.style.outlineOffset = '-2px';\n }\n });\n\n document.addEventListener('mouseout', function(e) {\n if (hoveredEl && hoveredEl !== selectedEl) {\n hoveredEl.style.outline = '';\n hoveredEl.style.outlineOffset = '';\n }\n hoveredEl = null;\n });\n\n // Click — select element\n document.addEventListener('click', function(e) {\n e.preventDefault();\n e.stopPropagation();\n var el = e.target;\n\n // Deselect previous\n if (selectedEl) {\n selectedEl.style.outline = '';\n selectedEl.style.outlineOffset = '';\n }\n\n if (selectedEl === el) {\n selectedEl = null;\n window.parent.postMessage({ type: 'element-deselected' }, '*');\n return;\n }\n\n selectedEl = el;\n\n // Clear hover styles BEFORE capturing openTag (so it matches source HTML)\n el.style.outline = '';\n el.style.outlineOffset = '';\n var openTag = el.outerHTML.substring(0, el.outerHTML.indexOf('>') + 1).substring(0, 120);\n\n el.style.outline = OUTLINE_SELECTED;\n el.style.outlineOffset = '-2px';\n\n var rect = el.getBoundingClientRect();\n var attrs = {};\n if (el.tagName === 'IMG') {\n attrs = { src: el.getAttribute('src') || '', alt: el.getAttribute('alt') || '' };\n }\n if (el.tagName === 'A') {\n attrs = { href: el.getAttribute('href') || '', target: el.getAttribute('target') || '' };\n }\n\n window.parent.postMessage({\n type: 'element-selected',\n sectionId: getSectionId(el),\n tagName: el.tagName,\n rect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },\n text: (el.textContent || '').substring(0, 200),\n openTag: openTag,\n elementPath: getElementPath(el),\n isSectionRoot: el.dataset && el.dataset.sectionId ? true : false,\n attrs: attrs,\n }, '*');\n }, true);\n\n // Double-click — contentEditable for text\n document.addEventListener('dblclick', function(e) {\n e.preventDefault();\n e.stopPropagation();\n var el = e.target;\n if (!isTextElement(el)) return;\n\n el.contentEditable = 'true';\n el.focus();\n el.style.outline = '2px dashed #F59E0B';\n el.style.outlineOffset = '-2px';\n\n function onBlur() {\n el.contentEditable = 'false';\n el.style.outline = '';\n el.style.outlineOffset = '';\n el.removeEventListener('blur', onBlur);\n el.removeEventListener('keydown', onKeydown);\n\n var sid = getSectionId(el);\n var sectionEl = sid ? getSectionElement(sid) : null;\n window.parent.postMessage({\n type: 'text-edited',\n sectionId: sid,\n elementPath: getElementPath(el),\n newText: el.innerHTML,\n sectionHtml: sectionEl ? sectionEl.innerHTML : null,\n }, '*');\n\n selectedEl = null;\n }\n\n function onKeydown(ev) {\n if (ev.key === 'Escape') {\n el.blur();\n }\n }\n\n el.addEventListener('blur', onBlur);\n el.addEventListener('keydown', onKeydown);\n }, true);\n\n // Listen for messages FROM parent (incremental section injection)\n window.addEventListener('message', function(e) {\n var msg = e.data;\n if (!msg || !msg.action) return;\n\n if (msg.action === 'add-section') {\n var wrapper = document.createElement('div');\n wrapper.setAttribute('data-section-id', msg.id);\n wrapper.innerHTML = msg.html;\n wrapper.style.animation = 'fadeInUp 0.4s ease-out';\n document.body.appendChild(wrapper);\n wrapper.scrollIntoView({ behavior: 'smooth', block: 'end' });\n }\n\n if (msg.action === 'update-section') {\n var el = getSectionElement(msg.id);\n if (el && typeof window.morphdom === 'function') {\n var tmp = document.createElement('div');\n tmp.innerHTML = msg.html;\n window.morphdom(el, tmp, {\n childrenOnly: true,\n onBeforeElUpdated: function(fromEl, toEl) {\n if (fromEl.isEqualNode(toEl)) return false;\n return true;\n }\n });\n } else if (el) {\n el.innerHTML = msg.html;\n }\n }\n\n if (msg.action === 'remove-section') {\n var el = getSectionElement(msg.id);\n if (el) { el.remove(); }\n }\n\n if (msg.action === 'reorder-sections') {\n // msg.order = [id1, id2, id3, ...]\n var order = msg.order;\n for (var i = 0; i < order.length; i++) {\n var el = getSectionElement(order[i]);\n if (el) { document.body.appendChild(el); }\n }\n }\n\n if (msg.action === 'update-attribute') {\n var sectionEl = getSectionElement(msg.sectionId);\n if (sectionEl) {\n var target = null;\n if (msg.elementPath) {\n // Find element by matching path\n var allEls = sectionEl.querySelectorAll(msg.tagName || '*');\n for (var i = 0; i < allEls.length; i++) {\n if (getElementPath(allEls[i]) === msg.elementPath) {\n target = allEls[i];\n break;\n }\n }\n }\n if (target) {\n target.setAttribute(msg.attr, msg.value);\n window.parent.postMessage({\n type: 'section-html-updated',\n sectionId: msg.sectionId,\n sectionHtml: sectionEl.innerHTML,\n }, '*');\n }\n }\n }\n\n if (msg.action === 'set-theme') {\n if (msg.theme && msg.theme !== 'default') {\n document.documentElement.setAttribute('data-theme', msg.theme);\n } else {\n document.documentElement.removeAttribute('data-theme');\n }\n }\n\n if (msg.action === 'set-custom-css') {\n var customStyle = document.getElementById('custom-theme-css');\n if (!customStyle) {\n customStyle = document.createElement('style');\n customStyle.id = 'custom-theme-css';\n document.head.appendChild(customStyle);\n }\n customStyle.textContent = msg.css || '';\n }\n\n if (msg.action === 'scroll-to-section') {\n var el = getSectionElement(msg.id);\n if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }\n }\n\n if (msg.action === 'full-rewrite') {\n // Fallback: rewrite everything\n document.body.innerHTML = msg.html;\n }\n });\n\n // Inject animation keyframe\n var style = document.createElement('style');\n style.textContent = '@keyframes fadeInUp { from { opacity:0; transform:translateY(20px); } to { opacity:1; transform:translateY(0); } }';\n document.head.appendChild(style);\n\n // Notify parent we're ready\n window.parent.postMessage({ type: 'ready' }, '*');\n})();\n`;\n}\n","import type { Section3 } from \"./types\";\nimport { getIframeScript } from \"./iframeScript\";\nimport { buildThemeCss, buildSingleThemeCss, buildCustomTheme, type CustomColors } from \"./themes\";\n\n/**\n * Build the full HTML for the iframe preview (with editing script).\n */\nexport function buildPreviewHtml(sections: Section3[], theme?: string): string {\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n const body = sorted\n .map((s) => `<div data-section-id=\"${s.id}\">${s.html}</div>`)\n .join(\"\\n\");\n\n const dataTheme = theme && theme !== \"default\" ? ` data-theme=\"${theme}\"` : \"\";\n const { css, tailwindConfig } = buildThemeCss();\n\n return `<!DOCTYPE html>\n<html lang=\"es\"${dataTheme}>\n<head>\n<meta charset=\"UTF-8\"/>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\n<script src=\"https://cdn.tailwindcss.com\"></script>\n<script src=\"https://unpkg.com/morphdom@2.7.4/dist/morphdom-umd.min.js\"></script>\n<script>tailwind.config = ${tailwindConfig}</script>\n<style>\n${css}\n*{margin:0;padding:0;box-sizing:border-box}\nhtml{scroll-behavior:smooth}\nbody{font-family:system-ui,-apple-system,sans-serif;background-color:var(--color-surface);color:var(--color-on-surface)}\nimg{max-width:100%}\n[contenteditable=\"true\"]{cursor:text}\n</style>\n</head>\n<body class=\"bg-surface text-on-surface\">\n${body}\n<script>${getIframeScript()}</script>\n</body>\n</html>`;\n}\n\n/**\n * Build the deploy HTML (no editing script, clean output).\n */\nexport function buildDeployHtml(sections: Section3[], theme?: string, customColors?: CustomColors): string {\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n const body = sorted.map((s) => s.html).join(\"\\n\");\n\n const isCustom = theme === \"custom\" && customColors;\n const dataTheme = theme && theme !== \"default\" && !isCustom ? ` data-theme=\"${theme}\"` : \"\";\n\n // For custom theme, build CSS from the custom colors directly (no data-theme needed, inject as :root)\n const { css: baseCss, tailwindConfig } = isCustom\n ? (() => {\n const ct = buildCustomTheme(customColors);\n const vars = Object.entries(ct.colors).map(([k, v]) => ` --color-${k}: ${v};`).join(\"\\n\");\n return { css: `:root {\\n${vars}\\n}`, tailwindConfig: buildSingleThemeCss(\"default\").tailwindConfig };\n })()\n : buildSingleThemeCss(theme || \"default\");\n\n return `<!DOCTYPE html>\n<html lang=\"es\"${dataTheme}>\n<head>\n<meta charset=\"UTF-8\"/>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\n<title>Landing Page</title>\n<script src=\"https://cdn.tailwindcss.com\"></script>\n<script>tailwind.config = ${tailwindConfig}</script>\n<style>\n${baseCss}\n*{margin:0;padding:0;box-sizing:border-box}\nhtml{scroll-behavior:smooth}\nbody{font-family:system-ui,-apple-system,sans-serif;background-color:var(--color-surface);color:var(--color-on-surface)}\n</style>\n</head>\n<body class=\"bg-surface text-on-surface\">\n${body}\n</body>\n</html>`;\n}\n"],"mappings":"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","names":[]}
|