@easybits.cloud/html-tailwind-generator 0.2.70 → 0.2.71

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  dataUrlToImagePart,
3
3
  streamGenerate
4
- } from "./chunk-ED6RAB2Z.js";
4
+ } from "./chunk-DHQSXOEG.js";
5
5
 
6
6
  // src/generateDocument.ts
7
7
  var DOCUMENT_SYSTEM_PROMPT = `You are a professional document designer who creates stunning letter-sized (8.5" \xD7 11") document pages using HTML + Tailwind CSS.
@@ -227,4 +227,4 @@ export {
227
227
  DOCUMENT_PROMPT_SUFFIX,
228
228
  generateDocument
229
229
  };
230
- //# sourceMappingURL=chunk-XZN2UQ3E.js.map
230
+ //# sourceMappingURL=chunk-5MKMIFTI.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  dataUrlToImagePart,
3
3
  streamGenerate
4
- } from "./chunk-ED6RAB2Z.js";
4
+ } from "./chunk-DHQSXOEG.js";
5
5
 
6
6
  // src/generate.ts
7
7
  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.
@@ -130,4 +130,4 @@ export {
130
130
  PROMPT_SUFFIX,
131
131
  generateLanding
132
132
  };
133
- //# sourceMappingURL=chunk-VB2IDGPB.js.map
133
+ //# sourceMappingURL=chunk-BM7QJCU5.js.map
@@ -11,6 +11,47 @@ import {
11
11
  import { streamText } from "ai";
12
12
  import { createAnthropic } from "@ai-sdk/anthropic";
13
13
  import { nanoid } from "nanoid";
14
+
15
+ // src/sanitizeColors.ts
16
+ var replacements = [
17
+ // Background
18
+ [/\bbg-(blue|indigo)-(500|600|700)\b/g, "bg-primary"],
19
+ [/\bbg-(blue|indigo)-(50|100)\b/g, "bg-primary-light"],
20
+ [/\bbg-(blue|indigo)-(800|900)\b/g, "bg-primary-dark"],
21
+ [/\bbg-(blue|indigo)-(200|300|400)\b/g, "bg-primary"],
22
+ // Text
23
+ [/\btext-(blue|indigo)-(500|600|700)\b/g, "text-primary"],
24
+ [/\btext-(blue|indigo)-(800|900)\b/g, "text-primary-dark"],
25
+ [/\btext-(blue|indigo)-(50|100|200|300)\b/g, "text-on-primary"],
26
+ [/\btext-(blue|indigo)-(400)\b/g, "text-primary"],
27
+ // Border
28
+ [/\bborder-(blue|indigo)-\d{2,3}\b/g, "border-primary"],
29
+ // Ring
30
+ [/\bring-(blue|indigo)-\d{2,3}\b/g, "ring-primary"],
31
+ // Gradients
32
+ [/\bfrom-(blue|indigo)-\d{2,3}\b/g, "from-primary"],
33
+ [/\bto-(blue|indigo)-\d{2,3}\b/g, "to-primary"],
34
+ [/\bvia-(blue|indigo)-\d{2,3}\b/g, "via-primary"],
35
+ // Hover/focus variants
36
+ [/\bhover:bg-(blue|indigo)-(500|600|700|800|900)\b/g, "hover:bg-primary-dark"],
37
+ [/\bhover:bg-(blue|indigo)-(50|100|200|300|400)\b/g, "hover:bg-primary-light"],
38
+ [/\bhover:text-(blue|indigo)-\d{2,3}\b/g, "hover:text-primary"],
39
+ [/\bfocus:ring-(blue|indigo)-\d{2,3}\b/g, "focus:ring-primary"],
40
+ [/\bfocus:border-(blue|indigo)-\d{2,3}\b/g, "focus:border-primary"],
41
+ // Divide
42
+ [/\bdivide-(blue|indigo)-\d{2,3}\b/g, "divide-primary"],
43
+ // Placeholder
44
+ [/\bplaceholder-(blue|indigo)-\d{2,3}\b/g, "placeholder-primary"]
45
+ ];
46
+ function sanitizeSemanticColors(html) {
47
+ let result = html;
48
+ for (const [pattern, replacement] of replacements) {
49
+ result = result.replace(pattern, replacement);
50
+ }
51
+ return result;
52
+ }
53
+
54
+ // src/streamCore.ts
14
55
  async function resolveModel(opts) {
15
56
  const anthropicKey = opts.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
16
57
  if (anthropicKey) {
@@ -213,7 +254,7 @@ async function streamGenerate(options) {
213
254
  const section = {
214
255
  id: nanoid(8),
215
256
  order: sectionOrder++,
216
- html: addSvgLoadingPlaceholders(addLoadingPlaceholders(obj.html)),
257
+ html: sanitizeSemanticColors(addSvgLoadingPlaceholders(addLoadingPlaceholders(obj.html))),
217
258
  label: obj.label
218
259
  };
219
260
  allSections.push(section);
@@ -279,4 +320,4 @@ export {
279
320
  extractJsonObjects,
280
321
  streamGenerate
281
322
  };
282
- //# sourceMappingURL=chunk-ED6RAB2Z.js.map
323
+ //# sourceMappingURL=chunk-DHQSXOEG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/streamCore.ts","../src/sanitizeColors.ts"],"sourcesContent":["import { streamText } from \"ai\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { nanoid } from \"nanoid\";\nimport { findImageSlots } from \"./images/enrichImages\";\nimport { searchImage } from \"./images/pexels\";\nimport { generateImage } from \"./images/dalleImages\";\nimport { generateSvg } from \"./images/svgGenerator\";\nimport type { Section3 } from \"./types\";\nimport { sanitizeSemanticColors } from \"./sanitizeColors\";\n\n/**\n * Resolve AI model from available keys.\n * Prefers Anthropic, falls back to OpenAI.\n */\nexport async function resolveModel(opts: {\n openaiApiKey?: string;\n anthropicApiKey?: string;\n modelId?: string;\n defaultOpenai: string;\n defaultAnthropic: string;\n}) {\n const anthropicKey = opts.anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n if (anthropicKey) {\n const anthropic = createAnthropic({ apiKey: anthropicKey });\n return anthropic(opts.modelId || opts.defaultAnthropic);\n }\n const openaiKey = opts.openaiApiKey || process.env.OPENAI_API_KEY;\n if (openaiKey) {\n const { createOpenAI } = await import(\"@ai-sdk/openai\");\n const openai = createOpenAI({ apiKey: openaiKey });\n return openai(opts.modelId || opts.defaultOpenai);\n }\n return createAnthropic()(opts.modelId || opts.defaultAnthropic);\n}\n\n/**\n * Convert data URL to Uint8Array for AI SDK vision.\n */\nexport function dataUrlToImagePart(dataUrl: string): { image: Uint8Array; mimeType: string } | null {\n const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);\n if (!match) return null;\n return {\n image: new Uint8Array(Buffer.from(match[2], \"base64\")),\n mimeType: match[1],\n };\n}\n\n/**\n * Extract complete JSON objects from accumulated text using brace-depth tracking.\n */\nexport function extractJsonObjects(text: string): [any[], string] {\n const objects: any[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n remaining = remaining.trimStart();\n if (!remaining.startsWith(\"{\")) {\n const nextBrace = remaining.indexOf(\"{\");\n if (nextBrace === -1) break;\n remaining = remaining.slice(nextBrace);\n continue;\n }\n\n let depth = 0;\n let inString = false;\n let escape = false;\n let end = -1;\n\n for (let i = 0; i < remaining.length; i++) {\n const ch = remaining[i];\n if (escape) { escape = false; continue; }\n if (ch === \"\\\\\") { escape = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n if (ch === \"{\") depth++;\n if (ch === \"}\") { depth--; if (depth === 0) { end = i; break; } }\n }\n\n if (end === -1) break;\n\n const candidate = remaining.slice(0, end + 1);\n remaining = remaining.slice(end + 1);\n\n try {\n objects.push(JSON.parse(candidate));\n } catch {\n // malformed, skip\n }\n }\n\n return [objects, remaining];\n}\n\n/** Inline shimmer SVG used as src for loading image placeholders */\nconst LOADING_PLACEHOLDER_SRC = `data:image/svg+xml,${encodeURIComponent('<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"800\" height=\"500\" viewBox=\"0 0 800 500\"><rect fill=\"#f3f4f6\" width=\"800\" height=\"500\" rx=\"12\"/><g opacity=\".4\"><rect x=\"320\" y=\"200\" width=\"160\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" repeatCount=\"indefinite\"/></rect><rect x=\"280\" y=\"215\" width=\"240\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" begin=\".3s\" repeatCount=\"indefinite\"/></rect><rect x=\"340\" y=\"230\" width=\"120\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" begin=\".6s\" repeatCount=\"indefinite\"/></rect></g><g transform=\"translate(376,150)\" opacity=\".3\"><path d=\"M0 28V4a4 4 0 014-4h40a4 4 0 014 4v24a4 4 0 01-4 4H4a4 4 0 01-4-4z\" fill=\"#d1d5db\"/><circle cx=\"14\" cy=\"12\" r=\"4\" fill=\"#9ca3af\"/><path d=\"M4 28l10-10 6 6 8-8 16 16H4z\" fill=\"#9ca3af\" opacity=\".5\"/></g></svg>')}`;\n\n/** Inline SVG placeholder for loading charts */\nconst SVG_LOADING_PLACEHOLDER = `<div class=\"w-full h-48 bg-gray-50 rounded-lg flex items-center justify-center animate-pulse\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#9ca3af\" stroke-width=\"1.5\"><rect x=\"3\" y=\"12\" width=\"4\" height=\"9\" rx=\"1\"/><rect x=\"10\" y=\"7\" width=\"4\" height=\"14\" rx=\"1\"/><rect x=\"17\" y=\"3\" width=\"4\" height=\"18\" rx=\"1\"/></svg></div>`;\n\n/** Replace data-svg-chart divs with loading placeholders */\nexport function addSvgLoadingPlaceholders(html: string): string {\n return html.replace(\n /<div\\s([^>]*?)data-svg-chart=\"([^\"]+)\"([^>]*?)>[\\s\\S]*?<\\/div>/gi,\n (_match, before, chart, after) => {\n return `<div ${before}data-svg-chart=\"${chart}\"${after}>${SVG_LOADING_PLACEHOLDER}</div>`;\n }\n );\n}\n\n/** Replace data-image-query attrs with animated loading placeholders */\nexport function addLoadingPlaceholders(html: string): string {\n return html.replace(\n /(<img\\s[^>]*)data-image-query=\"([^\"]+)\"([^>]*?)(?:\\s*\\/?>)/gi,\n (_match, before, query, after) => {\n if (before.includes('src=') || after.includes('src=')) return _match;\n return `${before}src=\"${LOADING_PLACEHOLDER_SRC}\" data-image-query=\"${query}\" alt=\"${query}\"${after}>`;\n }\n );\n}\n\nexport interface StreamGenerateOptions {\n /** Anthropic API key */\n anthropicApiKey?: string;\n /** OpenAI API key */\n openaiApiKey?: string;\n /** Model ID override */\n model?: string;\n /** System prompt */\n systemPrompt: string;\n /** User message content (text or multimodal parts) */\n userContent: any[];\n /** Pexels API key for image enrichment */\n pexelsApiKey?: string;\n /** Persist DALL-E images to permanent storage */\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n /** Called when a new section is parsed */\n onSection?: (section: Section3) => void;\n /** Called when a section's images are enriched */\n onImageUpdate?: (sectionId: string, html: string) => void;\n /** Called with raw text buffer for real-time partial streaming */\n onRawChunk?: (buffer: string, completedCount: number) => void;\n /** Called when generation is complete */\n onDone?: (sections: Section3[]) => void;\n /** Called on error */\n onError?: (error: Error) => void;\n}\n\n/**\n * Core streaming generation: stream AI text → parse NDJSON → emit sections → enrich images.\n * Used by both generateLanding and generateDocument.\n */\nexport async function streamGenerate(options: StreamGenerateOptions): Promise<Section3[]> {\n const {\n anthropicApiKey,\n openaiApiKey: _openaiApiKey,\n model: modelId,\n systemPrompt,\n userContent,\n pexelsApiKey,\n persistImage,\n onSection,\n onImageUpdate,\n onRawChunk,\n onDone,\n onError,\n } = options;\n\n const openaiApiKey = _openaiApiKey || process.env.OPENAI_API_KEY;\n const model = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId,\n defaultOpenai: \"gpt-4o\",\n defaultAnthropic: \"claude-sonnet-4-6\",\n });\n\n const result = streamText({\n model,\n system: systemPrompt,\n messages: [{ role: \"user\", content: userContent }],\n });\n\n const allSections: Section3[] = [];\n const imagePromises: Promise<void>[] = [];\n let sectionOrder = 0;\n let buffer = \"\";\n\n function enrichSvgCharts(sectionRef: Section3) {\n const svgRegex = /<div\\s[^>]*data-svg-chart=\"([^\"]+)\"[^>]*>[\\s\\S]*?<\\/div>/gi;\n const svgMatches: { fullMatch: string; prompt: string }[] = [];\n let svgM: RegExpExecArray | null;\n while ((svgM = svgRegex.exec(sectionRef.html)) !== null) {\n svgMatches.push({ fullMatch: svgM[0], prompt: svgM[1] });\n }\n if (svgMatches.length === 0) return;\n\n const anthropicKey = anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n imagePromises.push(\n (async () => {\n const results = await Promise.allSettled(\n svgMatches.map(async ({ fullMatch, prompt }) => {\n try {\n const svg = await generateSvg(prompt, anthropicKey);\n return { fullMatch, svg };\n } catch (e) {\n console.warn(`[svg] failed for \"${prompt}\":`, e);\n return { fullMatch, svg: `<div class=\"w-full h-48 bg-gray-100 rounded-lg flex items-center justify-center text-gray-400 text-sm\">${prompt}</div>` };\n }\n })\n );\n let html = sectionRef.html;\n for (const r of results) {\n if (r.status === \"fulfilled\" && r.value) {\n html = html.replace(r.value.fullMatch, r.value.svg);\n }\n }\n if (html !== sectionRef.html) {\n sectionRef.html = html;\n onImageUpdate?.(sectionRef.id, html);\n }\n })()\n );\n }\n\n function enrichSection(sectionRef: Section3) {\n const slots = findImageSlots(sectionRef.html);\n if (slots.length === 0) return;\n const slotsSnapshot = slots.map((s) => ({ ...s }));\n imagePromises.push(\n (async () => {\n const results = await Promise.allSettled(\n slotsSnapshot.map(async (slot) => {\n let url: string | null = null;\n // 1. Pexels first (free, fast)\n if (pexelsApiKey) {\n const img = await searchImage(slot.query, pexelsApiKey).catch(() => null);\n url = img?.url || null;\n }\n // 2. DALL-E fallback\n if (!url && openaiApiKey) {\n try {\n const tempUrl = await generateImage(slot.query, openaiApiKey);\n url = persistImage ? await persistImage(tempUrl, slot.query) : tempUrl;\n } catch (e) {\n console.warn(`[dalle] failed for \"${slot.query}\":`, e);\n }\n }\n url ??= `https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(slot.query.slice(0, 30))}`;\n return { slot, url };\n })\n );\n let html = sectionRef.html;\n for (const r of results) {\n if (r.status === \"fulfilled\" && r.value) {\n const { slot, url } = r.value;\n const replacement = slot.replaceStr.replace(\"{url}\", url);\n html = html.replaceAll(slot.searchStr, replacement);\n }\n }\n if (html !== sectionRef.html) {\n sectionRef.html = html;\n onImageUpdate?.(sectionRef.id, html);\n }\n })()\n );\n }\n\n function processObject(obj: any) {\n if (!obj.html || !obj.label) return;\n const section: Section3 = {\n id: nanoid(8),\n order: sectionOrder++,\n html: sanitizeSemanticColors(addSvgLoadingPlaceholders(addLoadingPlaceholders(obj.html))),\n label: obj.label,\n };\n allSections.push(section);\n onSection?.(section);\n enrichSection(section);\n enrichSvgCharts(section);\n }\n\n try {\n let chunkCount = 0;\n for await (const chunk of result.textStream) {\n buffer += chunk;\n chunkCount++;\n\n const [objects, remaining] = extractJsonObjects(buffer);\n buffer = remaining;\n for (const obj of objects) {\n chunkCount = 0;\n processObject(obj);\n }\n\n if (onRawChunk && chunkCount % 5 === 0 && buffer.length > 20) {\n onRawChunk(buffer, allSections.length);\n }\n }\n\n // Parse remaining buffer\n if (buffer.trim()) {\n let cleaned = buffer.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n const [lastObjects] = extractJsonObjects(cleaned);\n for (const obj of lastObjects) processObject(obj);\n }\n\n // Wait for image enrichment\n await Promise.allSettled(imagePromises);\n\n // Final fallback for images without src\n for (const section of allSections) {\n const before = section.html;\n section.html = section.html.replace(\n /<img\\s(?![^>]*\\bsrc=)([^>]*?)>/gi,\n (_match, attrs) => {\n const altMatch = attrs.match(/alt=\"([^\"]*?)\"/);\n const query = altMatch?.[1] || \"image\";\n return `<img src=\"https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}\" ${attrs}>`;\n }\n );\n section.html = section.html.replace(\n /data-image-query=\"([^\"]+)\"/g,\n (_match, query) => {\n return `src=\"https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}\" data-enriched=\"placeholder\"`;\n }\n );\n if (section.html !== before) {\n onImageUpdate?.(section.id, section.html);\n }\n }\n\n onDone?.(allSections);\n return allSections;\n } catch (err: any) {\n const error = err instanceof Error ? err : new Error(err?.message || \"Generation failed\");\n onError?.(error);\n throw error;\n }\n}\n","/**\n * Replace hardcoded Tailwind color classes (blue-*, indigo-*) with semantic\n * color classes (bg-primary, text-primary, etc.). Gray/white/black stay intact.\n */\n\nconst replacements: [RegExp, string][] = [\n // Background\n [/\\bbg-(blue|indigo)-(500|600|700)\\b/g, \"bg-primary\"],\n [/\\bbg-(blue|indigo)-(50|100)\\b/g, \"bg-primary-light\"],\n [/\\bbg-(blue|indigo)-(800|900)\\b/g, \"bg-primary-dark\"],\n [/\\bbg-(blue|indigo)-(200|300|400)\\b/g, \"bg-primary\"],\n\n // Text\n [/\\btext-(blue|indigo)-(500|600|700)\\b/g, \"text-primary\"],\n [/\\btext-(blue|indigo)-(800|900)\\b/g, \"text-primary-dark\"],\n [/\\btext-(blue|indigo)-(50|100|200|300)\\b/g, \"text-on-primary\"],\n [/\\btext-(blue|indigo)-(400)\\b/g, \"text-primary\"],\n\n // Border\n [/\\bborder-(blue|indigo)-\\d{2,3}\\b/g, \"border-primary\"],\n\n // Ring\n [/\\bring-(blue|indigo)-\\d{2,3}\\b/g, \"ring-primary\"],\n\n // Gradients\n [/\\bfrom-(blue|indigo)-\\d{2,3}\\b/g, \"from-primary\"],\n [/\\bto-(blue|indigo)-\\d{2,3}\\b/g, \"to-primary\"],\n [/\\bvia-(blue|indigo)-\\d{2,3}\\b/g, \"via-primary\"],\n\n // Hover/focus variants\n [/\\bhover:bg-(blue|indigo)-(500|600|700|800|900)\\b/g, \"hover:bg-primary-dark\"],\n [/\\bhover:bg-(blue|indigo)-(50|100|200|300|400)\\b/g, \"hover:bg-primary-light\"],\n [/\\bhover:text-(blue|indigo)-\\d{2,3}\\b/g, \"hover:text-primary\"],\n [/\\bfocus:ring-(blue|indigo)-\\d{2,3}\\b/g, \"focus:ring-primary\"],\n [/\\bfocus:border-(blue|indigo)-\\d{2,3}\\b/g, \"focus:border-primary\"],\n\n // Divide\n [/\\bdivide-(blue|indigo)-\\d{2,3}\\b/g, \"divide-primary\"],\n\n // Placeholder\n [/\\bplaceholder-(blue|indigo)-\\d{2,3}\\b/g, \"placeholder-primary\"],\n];\n\nexport function sanitizeSemanticColors(html: string): string {\n let result = html;\n for (const [pattern, replacement] of replacements) {\n result = result.replace(pattern, replacement);\n }\n return result;\n}\n"],"mappings":"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","names":[]}
package/dist/generate.js CHANGED
@@ -2,10 +2,10 @@ import {
2
2
  PROMPT_SUFFIX,
3
3
  SYSTEM_PROMPT,
4
4
  generateLanding
5
- } from "./chunk-VB2IDGPB.js";
5
+ } from "./chunk-BM7QJCU5.js";
6
6
  import {
7
7
  extractJsonObjects
8
- } from "./chunk-ED6RAB2Z.js";
8
+ } from "./chunk-DHQSXOEG.js";
9
9
  import "./chunk-4NOYN34W.js";
10
10
  import "./chunk-GW5ZUYWT.js";
11
11
  export {
@@ -2,8 +2,8 @@ import {
2
2
  DOCUMENT_PROMPT_SUFFIX,
3
3
  DOCUMENT_SYSTEM_PROMPT,
4
4
  generateDocument
5
- } from "./chunk-XZN2UQ3E.js";
6
- import "./chunk-ED6RAB2Z.js";
5
+ } from "./chunk-5MKMIFTI.js";
6
+ import "./chunk-DHQSXOEG.js";
7
7
  import "./chunk-4NOYN34W.js";
8
8
  import "./chunk-GW5ZUYWT.js";
9
9
  export {
package/dist/index.js CHANGED
@@ -10,15 +10,15 @@ import {
10
10
  PROMPT_SUFFIX,
11
11
  SYSTEM_PROMPT,
12
12
  generateLanding
13
- } from "./chunk-VB2IDGPB.js";
13
+ } from "./chunk-BM7QJCU5.js";
14
14
  import {
15
15
  DOCUMENT_PROMPT_SUFFIX,
16
16
  DOCUMENT_SYSTEM_PROMPT,
17
17
  generateDocument
18
- } from "./chunk-XZN2UQ3E.js";
18
+ } from "./chunk-5MKMIFTI.js";
19
19
  import {
20
20
  extractJsonObjects
21
- } from "./chunk-ED6RAB2Z.js";
21
+ } from "./chunk-DHQSXOEG.js";
22
22
  import {
23
23
  generateSvg
24
24
  } from "./chunk-4NOYN34W.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/html-tailwind-generator",
3
- "version": "0.2.70",
3
+ "version": "0.2.71",
4
4
  "description": "AI-powered landing page generator with Tailwind CSS — canvas editor, streaming generation, and one-click deploy",
5
5
  "license": "PolyForm-Noncommercial-1.0.0",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/streamCore.ts"],"sourcesContent":["import { streamText } from \"ai\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { nanoid } from \"nanoid\";\nimport { findImageSlots } from \"./images/enrichImages\";\nimport { searchImage } from \"./images/pexels\";\nimport { generateImage } from \"./images/dalleImages\";\nimport { generateSvg } from \"./images/svgGenerator\";\nimport type { Section3 } from \"./types\";\n\n/**\n * Resolve AI model from available keys.\n * Prefers Anthropic, falls back to OpenAI.\n */\nexport async function resolveModel(opts: {\n openaiApiKey?: string;\n anthropicApiKey?: string;\n modelId?: string;\n defaultOpenai: string;\n defaultAnthropic: string;\n}) {\n const anthropicKey = opts.anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n if (anthropicKey) {\n const anthropic = createAnthropic({ apiKey: anthropicKey });\n return anthropic(opts.modelId || opts.defaultAnthropic);\n }\n const openaiKey = opts.openaiApiKey || process.env.OPENAI_API_KEY;\n if (openaiKey) {\n const { createOpenAI } = await import(\"@ai-sdk/openai\");\n const openai = createOpenAI({ apiKey: openaiKey });\n return openai(opts.modelId || opts.defaultOpenai);\n }\n return createAnthropic()(opts.modelId || opts.defaultAnthropic);\n}\n\n/**\n * Convert data URL to Uint8Array for AI SDK vision.\n */\nexport function dataUrlToImagePart(dataUrl: string): { image: Uint8Array; mimeType: string } | null {\n const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);\n if (!match) return null;\n return {\n image: new Uint8Array(Buffer.from(match[2], \"base64\")),\n mimeType: match[1],\n };\n}\n\n/**\n * Extract complete JSON objects from accumulated text using brace-depth tracking.\n */\nexport function extractJsonObjects(text: string): [any[], string] {\n const objects: any[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n remaining = remaining.trimStart();\n if (!remaining.startsWith(\"{\")) {\n const nextBrace = remaining.indexOf(\"{\");\n if (nextBrace === -1) break;\n remaining = remaining.slice(nextBrace);\n continue;\n }\n\n let depth = 0;\n let inString = false;\n let escape = false;\n let end = -1;\n\n for (let i = 0; i < remaining.length; i++) {\n const ch = remaining[i];\n if (escape) { escape = false; continue; }\n if (ch === \"\\\\\") { escape = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n if (ch === \"{\") depth++;\n if (ch === \"}\") { depth--; if (depth === 0) { end = i; break; } }\n }\n\n if (end === -1) break;\n\n const candidate = remaining.slice(0, end + 1);\n remaining = remaining.slice(end + 1);\n\n try {\n objects.push(JSON.parse(candidate));\n } catch {\n // malformed, skip\n }\n }\n\n return [objects, remaining];\n}\n\n/** Inline shimmer SVG used as src for loading image placeholders */\nconst LOADING_PLACEHOLDER_SRC = `data:image/svg+xml,${encodeURIComponent('<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"800\" height=\"500\" viewBox=\"0 0 800 500\"><rect fill=\"#f3f4f6\" width=\"800\" height=\"500\" rx=\"12\"/><g opacity=\".4\"><rect x=\"320\" y=\"200\" width=\"160\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" repeatCount=\"indefinite\"/></rect><rect x=\"280\" y=\"215\" width=\"240\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" begin=\".3s\" repeatCount=\"indefinite\"/></rect><rect x=\"340\" y=\"230\" width=\"120\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" begin=\".6s\" repeatCount=\"indefinite\"/></rect></g><g transform=\"translate(376,150)\" opacity=\".3\"><path d=\"M0 28V4a4 4 0 014-4h40a4 4 0 014 4v24a4 4 0 01-4 4H4a4 4 0 01-4-4z\" fill=\"#d1d5db\"/><circle cx=\"14\" cy=\"12\" r=\"4\" fill=\"#9ca3af\"/><path d=\"M4 28l10-10 6 6 8-8 16 16H4z\" fill=\"#9ca3af\" opacity=\".5\"/></g></svg>')}`;\n\n/** Inline SVG placeholder for loading charts */\nconst SVG_LOADING_PLACEHOLDER = `<div class=\"w-full h-48 bg-gray-50 rounded-lg flex items-center justify-center animate-pulse\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#9ca3af\" stroke-width=\"1.5\"><rect x=\"3\" y=\"12\" width=\"4\" height=\"9\" rx=\"1\"/><rect x=\"10\" y=\"7\" width=\"4\" height=\"14\" rx=\"1\"/><rect x=\"17\" y=\"3\" width=\"4\" height=\"18\" rx=\"1\"/></svg></div>`;\n\n/** Replace data-svg-chart divs with loading placeholders */\nexport function addSvgLoadingPlaceholders(html: string): string {\n return html.replace(\n /<div\\s([^>]*?)data-svg-chart=\"([^\"]+)\"([^>]*?)>[\\s\\S]*?<\\/div>/gi,\n (_match, before, chart, after) => {\n return `<div ${before}data-svg-chart=\"${chart}\"${after}>${SVG_LOADING_PLACEHOLDER}</div>`;\n }\n );\n}\n\n/** Replace data-image-query attrs with animated loading placeholders */\nexport function addLoadingPlaceholders(html: string): string {\n return html.replace(\n /(<img\\s[^>]*)data-image-query=\"([^\"]+)\"([^>]*?)(?:\\s*\\/?>)/gi,\n (_match, before, query, after) => {\n if (before.includes('src=') || after.includes('src=')) return _match;\n return `${before}src=\"${LOADING_PLACEHOLDER_SRC}\" data-image-query=\"${query}\" alt=\"${query}\"${after}>`;\n }\n );\n}\n\nexport interface StreamGenerateOptions {\n /** Anthropic API key */\n anthropicApiKey?: string;\n /** OpenAI API key */\n openaiApiKey?: string;\n /** Model ID override */\n model?: string;\n /** System prompt */\n systemPrompt: string;\n /** User message content (text or multimodal parts) */\n userContent: any[];\n /** Pexels API key for image enrichment */\n pexelsApiKey?: string;\n /** Persist DALL-E images to permanent storage */\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n /** Called when a new section is parsed */\n onSection?: (section: Section3) => void;\n /** Called when a section's images are enriched */\n onImageUpdate?: (sectionId: string, html: string) => void;\n /** Called with raw text buffer for real-time partial streaming */\n onRawChunk?: (buffer: string, completedCount: number) => void;\n /** Called when generation is complete */\n onDone?: (sections: Section3[]) => void;\n /** Called on error */\n onError?: (error: Error) => void;\n}\n\n/**\n * Core streaming generation: stream AI text → parse NDJSON → emit sections → enrich images.\n * Used by both generateLanding and generateDocument.\n */\nexport async function streamGenerate(options: StreamGenerateOptions): Promise<Section3[]> {\n const {\n anthropicApiKey,\n openaiApiKey: _openaiApiKey,\n model: modelId,\n systemPrompt,\n userContent,\n pexelsApiKey,\n persistImage,\n onSection,\n onImageUpdate,\n onRawChunk,\n onDone,\n onError,\n } = options;\n\n const openaiApiKey = _openaiApiKey || process.env.OPENAI_API_KEY;\n const model = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId,\n defaultOpenai: \"gpt-4o\",\n defaultAnthropic: \"claude-sonnet-4-6\",\n });\n\n const result = streamText({\n model,\n system: systemPrompt,\n messages: [{ role: \"user\", content: userContent }],\n });\n\n const allSections: Section3[] = [];\n const imagePromises: Promise<void>[] = [];\n let sectionOrder = 0;\n let buffer = \"\";\n\n function enrichSvgCharts(sectionRef: Section3) {\n const svgRegex = /<div\\s[^>]*data-svg-chart=\"([^\"]+)\"[^>]*>[\\s\\S]*?<\\/div>/gi;\n const svgMatches: { fullMatch: string; prompt: string }[] = [];\n let svgM: RegExpExecArray | null;\n while ((svgM = svgRegex.exec(sectionRef.html)) !== null) {\n svgMatches.push({ fullMatch: svgM[0], prompt: svgM[1] });\n }\n if (svgMatches.length === 0) return;\n\n const anthropicKey = anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n imagePromises.push(\n (async () => {\n const results = await Promise.allSettled(\n svgMatches.map(async ({ fullMatch, prompt }) => {\n try {\n const svg = await generateSvg(prompt, anthropicKey);\n return { fullMatch, svg };\n } catch (e) {\n console.warn(`[svg] failed for \"${prompt}\":`, e);\n return { fullMatch, svg: `<div class=\"w-full h-48 bg-gray-100 rounded-lg flex items-center justify-center text-gray-400 text-sm\">${prompt}</div>` };\n }\n })\n );\n let html = sectionRef.html;\n for (const r of results) {\n if (r.status === \"fulfilled\" && r.value) {\n html = html.replace(r.value.fullMatch, r.value.svg);\n }\n }\n if (html !== sectionRef.html) {\n sectionRef.html = html;\n onImageUpdate?.(sectionRef.id, html);\n }\n })()\n );\n }\n\n function enrichSection(sectionRef: Section3) {\n const slots = findImageSlots(sectionRef.html);\n if (slots.length === 0) return;\n const slotsSnapshot = slots.map((s) => ({ ...s }));\n imagePromises.push(\n (async () => {\n const results = await Promise.allSettled(\n slotsSnapshot.map(async (slot) => {\n let url: string | null = null;\n // 1. Pexels first (free, fast)\n if (pexelsApiKey) {\n const img = await searchImage(slot.query, pexelsApiKey).catch(() => null);\n url = img?.url || null;\n }\n // 2. DALL-E fallback\n if (!url && openaiApiKey) {\n try {\n const tempUrl = await generateImage(slot.query, openaiApiKey);\n url = persistImage ? await persistImage(tempUrl, slot.query) : tempUrl;\n } catch (e) {\n console.warn(`[dalle] failed for \"${slot.query}\":`, e);\n }\n }\n url ??= `https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(slot.query.slice(0, 30))}`;\n return { slot, url };\n })\n );\n let html = sectionRef.html;\n for (const r of results) {\n if (r.status === \"fulfilled\" && r.value) {\n const { slot, url } = r.value;\n const replacement = slot.replaceStr.replace(\"{url}\", url);\n html = html.replaceAll(slot.searchStr, replacement);\n }\n }\n if (html !== sectionRef.html) {\n sectionRef.html = html;\n onImageUpdate?.(sectionRef.id, html);\n }\n })()\n );\n }\n\n function processObject(obj: any) {\n if (!obj.html || !obj.label) return;\n const section: Section3 = {\n id: nanoid(8),\n order: sectionOrder++,\n html: addSvgLoadingPlaceholders(addLoadingPlaceholders(obj.html)),\n label: obj.label,\n };\n allSections.push(section);\n onSection?.(section);\n enrichSection(section);\n enrichSvgCharts(section);\n }\n\n try {\n let chunkCount = 0;\n for await (const chunk of result.textStream) {\n buffer += chunk;\n chunkCount++;\n\n const [objects, remaining] = extractJsonObjects(buffer);\n buffer = remaining;\n for (const obj of objects) {\n chunkCount = 0;\n processObject(obj);\n }\n\n if (onRawChunk && chunkCount % 5 === 0 && buffer.length > 20) {\n onRawChunk(buffer, allSections.length);\n }\n }\n\n // Parse remaining buffer\n if (buffer.trim()) {\n let cleaned = buffer.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n const [lastObjects] = extractJsonObjects(cleaned);\n for (const obj of lastObjects) processObject(obj);\n }\n\n // Wait for image enrichment\n await Promise.allSettled(imagePromises);\n\n // Final fallback for images without src\n for (const section of allSections) {\n const before = section.html;\n section.html = section.html.replace(\n /<img\\s(?![^>]*\\bsrc=)([^>]*?)>/gi,\n (_match, attrs) => {\n const altMatch = attrs.match(/alt=\"([^\"]*?)\"/);\n const query = altMatch?.[1] || \"image\";\n return `<img src=\"https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}\" ${attrs}>`;\n }\n );\n section.html = section.html.replace(\n /data-image-query=\"([^\"]+)\"/g,\n (_match, query) => {\n return `src=\"https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}\" data-enriched=\"placeholder\"`;\n }\n );\n if (section.html !== before) {\n onImageUpdate?.(section.id, section.html);\n }\n }\n\n onDone?.(allSections);\n return allSections;\n } catch (err: any) {\n const error = err instanceof Error ? err : new Error(err?.message || \"Generation failed\");\n onError?.(error);\n throw error;\n }\n}\n"],"mappings":"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","names":[]}