@brunoalz/smartgesti-site-editor 1.4.0 → 1.4.1

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.
Files changed (64) hide show
  1. package/dist/editor/BlockSelector.js +4 -2
  2. package/dist/editor/BlockSelector.js.map +1 -1
  3. package/dist/engine/export/exportHtml.d.ts +5 -1
  4. package/dist/engine/export/exportHtml.d.ts.map +1 -1
  5. package/dist/engine/export/exportHtml.js +69 -51
  6. package/dist/engine/export/exportHtml.js.map +1 -1
  7. package/dist/engine/export/exporters/sections/BlogPostExporters.d.ts +2 -0
  8. package/dist/engine/export/exporters/sections/BlogPostExporters.d.ts.map +1 -1
  9. package/dist/engine/export/exporters/sections/BlogPostExporters.js +137 -80
  10. package/dist/engine/export/exporters/sections/BlogPostExporters.js.map +1 -1
  11. package/dist/engine/export/exporters/sections/index.d.ts.map +1 -1
  12. package/dist/engine/export/exporters/sections/index.js +40 -38
  13. package/dist/engine/export/exporters/sections/index.js.map +1 -1
  14. package/dist/engine/index.js +97 -95
  15. package/dist/engine/index.js.map +1 -1
  16. package/dist/engine/plugins/builtin/blog/manifest.d.ts.map +1 -1
  17. package/dist/engine/plugins/builtin/blog/manifest.js +50 -18
  18. package/dist/engine/plugins/builtin/blog/manifest.js.map +1 -1
  19. package/dist/engine/plugins/builtin/blog/mockContentProvider.d.ts.map +1 -1
  20. package/dist/engine/plugins/builtin/blog/mockContentProvider.js +13 -9
  21. package/dist/engine/plugins/builtin/blog/mockContentProvider.js.map +1 -1
  22. package/dist/engine/plugins/contentHydration.d.ts.map +1 -1
  23. package/dist/engine/plugins/contentHydration.js +119 -79
  24. package/dist/engine/plugins/contentHydration.js.map +1 -1
  25. package/dist/engine/plugins/types.d.ts +5 -0
  26. package/dist/engine/plugins/types.d.ts.map +1 -1
  27. package/dist/engine/registry/blocks/sections/blogCategoryFilter.d.ts +3 -0
  28. package/dist/engine/registry/blocks/sections/blogCategoryFilter.d.ts.map +1 -0
  29. package/dist/engine/registry/blocks/sections/blogCategoryFilter.js +53 -0
  30. package/dist/engine/registry/blocks/sections/blogCategoryFilter.js.map +1 -0
  31. package/dist/engine/registry/blocks/sections/blogPostDetail.d.ts.map +1 -1
  32. package/dist/engine/registry/blocks/sections/blogPostDetail.js +4 -1
  33. package/dist/engine/registry/blocks/sections/blogPostDetail.js.map +1 -1
  34. package/dist/engine/registry/blocks/sections/blogSearchBar.d.ts +3 -0
  35. package/dist/engine/registry/blocks/sections/blogSearchBar.d.ts.map +1 -0
  36. package/dist/engine/registry/blocks/sections/blogSearchBar.js +56 -0
  37. package/dist/engine/registry/blocks/sections/blogSearchBar.js.map +1 -0
  38. package/dist/engine/registry/blocks/sections/index.d.ts +2 -0
  39. package/dist/engine/registry/blocks/sections/index.d.ts.map +1 -1
  40. package/dist/engine/render/renderers/sections/BlogCategoryFilterRenderer.d.ts +3 -0
  41. package/dist/engine/render/renderers/sections/BlogCategoryFilterRenderer.d.ts.map +1 -0
  42. package/dist/engine/render/renderers/sections/BlogCategoryFilterRenderer.js +213 -0
  43. package/dist/engine/render/renderers/sections/BlogCategoryFilterRenderer.js.map +1 -0
  44. package/dist/engine/render/renderers/sections/BlogSearchBarRenderer.d.ts +3 -0
  45. package/dist/engine/render/renderers/sections/BlogSearchBarRenderer.d.ts.map +1 -0
  46. package/dist/engine/render/renderers/sections/BlogSearchBarRenderer.js +138 -0
  47. package/dist/engine/render/renderers/sections/BlogSearchBarRenderer.js.map +1 -0
  48. package/dist/engine/render/renderers/sections/index.js +28 -24
  49. package/dist/engine/render/renderers/sections/index.js.map +1 -1
  50. package/dist/engine/schema/siteDocument.d.ts +66 -2
  51. package/dist/engine/schema/siteDocument.d.ts.map +1 -1
  52. package/dist/engine/schema/siteDocument.js.map +1 -1
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +108 -106
  56. package/dist/index.js.map +1 -1
  57. package/dist/utils/blockIcons.d.ts.map +1 -1
  58. package/dist/utils/blockIcons.js +2 -0
  59. package/dist/utils/blockIcons.js.map +1 -1
  60. package/dist/viewer/LandingPageViewer.d.ts +7 -2
  61. package/dist/viewer/LandingPageViewer.d.ts.map +1 -1
  62. package/dist/viewer/LandingPageViewer.js +106 -92
  63. package/dist/viewer/LandingPageViewer.js.map +1 -1
  64. package/package.json +1 -1
@@ -47,6 +47,8 @@ import "../engine/registry/blocks/sections/categoryCardGrid.js";
47
47
  import "../engine/registry/blocks/sections/blogPostCard.js";
48
48
  import "../engine/registry/blocks/sections/blogPostGrid.js";
49
49
  import "../engine/registry/blocks/sections/blogPostDetail.js";
50
+ import "../engine/registry/blocks/sections/blogCategoryFilter.js";
51
+ import "../engine/registry/blocks/sections/blogSearchBar.js";
50
52
  import "../engine/registry/blocks/sections/productShowcase.js";
51
53
  import "../engine/registry/blocks/sections/aboutSection.js";
52
54
  import "../engine/registry/blocks/sections/contactSection.js";
@@ -151,7 +153,7 @@ function b(t, r, c, d, l) {
151
153
  ) })
152
154
  ] }, t.id);
153
155
  }
154
- const Tt = A.memo(function({
156
+ const It = A.memo(function({
155
157
  structure: r,
156
158
  selectedBlockId: c,
157
159
  onSelectBlock: d,
@@ -202,6 +204,6 @@ const Tt = A.memo(function({
202
204
  ] });
203
205
  });
204
206
  export {
205
- Tt as BlockSelector
207
+ It as BlockSelector
206
208
  };
207
209
  //# sourceMappingURL=BlockSelector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BlockSelector.js","sources":["../../src/editor/BlockSelector.tsx"],"sourcesContent":["/**\n * Block Selector\n * Lista blocos da estrutura de forma hierárquica\n */\n\nimport React, { useMemo } from 'react'\nimport { Block } from '../engine'\nimport { componentRegistry } from '../engine'\nimport { Trash2, ChevronRight } from 'lucide-react'\nimport { cn } from '../utils/cn'\n\ninterface BlockSelectorProps {\n structure: Block[]\n selectedBlockId: string | null\n onSelectBlock: (blockId: string) => void\n onDeleteBlock: (blockId: string) => void\n}\n\n/**\n * Obtém nome amigável do tipo de bloco\n */\nfunction getBlockTypeName(type: Block['type']): string {\n const definition = componentRegistry.get(type)\n return definition?.name || type\n}\n\n/**\n * Obtém preview do bloco (texto ou propriedade relevante)\n */\nfunction getBlockPreview(block: Block): string {\n const props = block.props as Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any -- dynamic access for preview\n\n switch (block.type) {\n case 'heading':\n return props.text || 'Heading'\n case 'text':\n return props.text ? String(props.text).substring(0, 30) + (String(props.text).length > 30 ? '...' : '') : 'Text'\n case 'button':\n return props.text || 'Button'\n case 'link':\n return props.text || 'Link'\n case 'image':\n return props.alt || props.src ? 'Image' : 'Image'\n case 'container':\n case 'stack':\n case 'grid':\n case 'box':\n return `${getBlockTypeName(block.type)} (${Array.isArray(props.children) ? props.children.length : 0} children)`\n case 'section':\n return props.id ? `Section: ${props.id}` : 'Section'\n case 'card':\n return 'Card'\n case 'divider':\n return 'Divider'\n default:\n return getBlockTypeName(block.type)\n }\n}\n\n/**\n * Renderiza um bloco e seus children recursivamente\n */\nfunction renderBlockTree(\n block: Block,\n depth: number,\n selectedBlockId: string | null,\n onSelectBlock: (blockId: string) => void,\n onDeleteBlock: (blockId: string) => void\n): React.ReactNode {\n // Verificar se o bloco é válido\n if (!block || typeof block !== 'object' || !block.id || !block.type) {\n return null\n }\n\n const isSelected = block.id === selectedBlockId\n const props = block.props as Record<string, any>\n const hasChildren = props && typeof props === 'object' && props.children && Array.isArray(props.children) && props.children.length > 0\n\n return (\n <div key={block.id}>\n <div\n className={cn(\n 'flex items-center gap-2 p-2 rounded-lg transition-all cursor-pointer group',\n 'hover:scale-[1.01] active:scale-[0.99]',\n isSelected\n ? 'bg-blue-500 text-white shadow-md shadow-blue-500/20'\n : 'bg-gray-100/50 dark:bg-gray-800/50 hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300',\n depth > 0 && 'ml-4'\n )}\n style={{ paddingLeft: `${depth * 12 + 8}px` }}\n onClick={() => onSelectBlock(block.id)}\n >\n {/* Indentação visual */}\n {depth > 0 && (\n <div className=\"flex-shrink-0 w-4 flex items-center justify-center\">\n <ChevronRight className=\"w-3 h-3 opacity-50\" />\n </div>\n )}\n\n {/* Badge de tipo */}\n <div\n className={cn(\n 'flex-shrink-0 w-6 h-6 rounded text-xs font-semibold flex items-center justify-center',\n isSelected\n ? 'bg-white/20 text-white'\n : componentRegistry.get(block.type)?.pluginId\n ? 'bg-purple-100 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400'\n : 'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400'\n )}\n title={getBlockTypeName(block.type)}\n >\n {componentRegistry.get(block.type)?.pluginId ? '🧩' : block.type.substring(0, 2).toUpperCase()}\n </div>\n\n {/* Nome e preview */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-xs font-medium truncate\">{getBlockTypeName(block.type)}</div>\n <div\n className={cn(\n 'text-xs truncate',\n isSelected ? 'text-white/80' : 'text-gray-500 dark:text-gray-400'\n )}\n >\n {getBlockPreview(block)}\n </div>\n </div>\n\n {/* Botão deletar */}\n <button\n onClick={(e) => {\n e.stopPropagation()\n onDeleteBlock(block.id)\n }}\n className={cn(\n 'p-1.5 rounded-full transition-all opacity-0 group-hover:opacity-100',\n 'hover:bg-white/30 dark:hover:bg-gray-700/50',\n 'hover:scale-110 active:scale-95',\n isSelected\n ? 'text-white hover:bg-white/40'\n : 'text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'\n )}\n title=\"Deletar bloco\"\n >\n <Trash2 className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n\n {/* Renderizar children recursivamente */}\n {hasChildren && (\n <div className=\"mt-1\">\n {props.children\n .filter((child: Block) => child && child.id && child.type) // Filtrar children inválidos\n .map((child: Block) =>\n renderBlockTree(child, depth + 1, selectedBlockId, onSelectBlock, onDeleteBlock)\n )}\n </div>\n )}\n </div>\n )\n}\n\nexport const BlockSelector = React.memo(function BlockSelector({\n structure,\n selectedBlockId,\n onSelectBlock,\n onDeleteBlock,\n}: BlockSelectorProps) {\n // Filtrar estrutura inválida ANTES de usar\n const validStructure = useMemo(() => {\n if (!structure || !Array.isArray(structure)) return []\n return structure.filter((b) => b && typeof b === 'object' && b.id && b.type)\n }, [structure])\n\n // Contar total de blocos (incluindo children) - usando useMemo para evitar recálculos\n const totalBlocks = useMemo(() => {\n const count = (blocks: Block[]): number => {\n if (!blocks || !Array.isArray(blocks) || blocks.length === 0) return 0\n \n let total = 0\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i]\n \n // Verificar se o bloco é válido ANTES de acessar qualquer propriedade\n if (!block || typeof block !== 'object' || !block.id || !block.type) {\n continue\n }\n \n total++ // Contar o bloco atual\n \n // Verificar props de forma segura - SEMPRE dentro de try-catch\n try {\n if (!block.props) {\n continue\n }\n \n const props = block.props\n if (props && typeof props === 'object' && !Array.isArray(props)) {\n const children = (props as Record<string, any>).children\n if (children && Array.isArray(children) && children.length > 0) {\n // Filtrar children inválidos antes de contar recursivamente\n const validChildren = children.filter(\n (child: unknown) => child && typeof child === 'object' && (child as Block).id && (child as Block).type\n )\n if (validChildren.length > 0) {\n total += count(validChildren)\n }\n }\n }\n } catch (error) {\n // Se houver erro ao acessar props, apenas continuar\n continue\n }\n }\n return total\n }\n \n return count(validStructure)\n }, [validStructure])\n\n return (\n <div className=\"h-full flex flex-col overflow-hidden\">\n {/* Header */}\n <div className=\"flex-shrink-0 p-3 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900\">\n <h2 className=\"text-sm font-semibold text-gray-800 dark:text-gray-100\">Blocos</h2>\n <p className=\"text-xs text-gray-500 dark:text-gray-400 mt-1\">\n {totalBlocks} {totalBlocks === 1 ? 'bloco' : 'blocos'}\n </p>\n </div>\n\n {/* Lista de blocos */}\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {!validStructure || validStructure.length === 0 ? (\n <div className=\"text-center text-gray-500 dark:text-gray-400 text-xs py-8\">\n Nenhum bloco adicionado ainda\n </div>\n ) : (\n validStructure.map((block) =>\n renderBlockTree(block, 0, selectedBlockId, onSelectBlock, onDeleteBlock)\n )\n )}\n </div>\n </div>\n )\n});\n"],"names":["getBlockTypeName","type","componentRegistry","getBlockPreview","block","props","renderBlockTree","depth","selectedBlockId","onSelectBlock","onDeleteBlock","isSelected","hasChildren","jsxs","cn","jsx","ChevronRight","Trash2","child","BlockSelector","React","structure","validStructure","useMemo","b","totalBlocks","count","blocks","total","i","children","validChildren"],"mappings":"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"}
1
+ {"version":3,"file":"BlockSelector.js","sources":["../../src/editor/BlockSelector.tsx"],"sourcesContent":["/**\n * Block Selector\n * Lista blocos da estrutura de forma hierárquica\n */\n\nimport React, { useMemo } from 'react'\nimport { Block } from '../engine'\nimport { componentRegistry } from '../engine'\nimport { Trash2, ChevronRight } from 'lucide-react'\nimport { cn } from '../utils/cn'\n\ninterface BlockSelectorProps {\n structure: Block[]\n selectedBlockId: string | null\n onSelectBlock: (blockId: string) => void\n onDeleteBlock: (blockId: string) => void\n}\n\n/**\n * Obtém nome amigável do tipo de bloco\n */\nfunction getBlockTypeName(type: Block['type']): string {\n const definition = componentRegistry.get(type)\n return definition?.name || type\n}\n\n/**\n * Obtém preview do bloco (texto ou propriedade relevante)\n */\nfunction getBlockPreview(block: Block): string {\n const props = block.props as Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any -- dynamic access for preview\n\n switch (block.type) {\n case 'heading':\n return props.text || 'Heading'\n case 'text':\n return props.text ? String(props.text).substring(0, 30) + (String(props.text).length > 30 ? '...' : '') : 'Text'\n case 'button':\n return props.text || 'Button'\n case 'link':\n return props.text || 'Link'\n case 'image':\n return props.alt || props.src ? 'Image' : 'Image'\n case 'container':\n case 'stack':\n case 'grid':\n case 'box':\n return `${getBlockTypeName(block.type)} (${Array.isArray(props.children) ? props.children.length : 0} children)`\n case 'section':\n return props.id ? `Section: ${props.id}` : 'Section'\n case 'card':\n return 'Card'\n case 'divider':\n return 'Divider'\n default:\n return getBlockTypeName(block.type)\n }\n}\n\n/**\n * Renderiza um bloco e seus children recursivamente\n */\nfunction renderBlockTree(\n block: Block,\n depth: number,\n selectedBlockId: string | null,\n onSelectBlock: (blockId: string) => void,\n onDeleteBlock: (blockId: string) => void\n): React.ReactNode {\n // Verificar se o bloco é válido\n if (!block || typeof block !== 'object' || !block.id || !block.type) {\n return null\n }\n\n const isSelected = block.id === selectedBlockId\n const props = block.props as Record<string, any>\n const hasChildren = props && typeof props === 'object' && props.children && Array.isArray(props.children) && props.children.length > 0\n\n return (\n <div key={block.id}>\n <div\n className={cn(\n 'flex items-center gap-2 p-2 rounded-lg transition-all cursor-pointer group',\n 'hover:scale-[1.01] active:scale-[0.99]',\n isSelected\n ? 'bg-blue-500 text-white shadow-md shadow-blue-500/20'\n : 'bg-gray-100/50 dark:bg-gray-800/50 hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300',\n depth > 0 && 'ml-4'\n )}\n style={{ paddingLeft: `${depth * 12 + 8}px` }}\n onClick={() => onSelectBlock(block.id)}\n >\n {/* Indentação visual */}\n {depth > 0 && (\n <div className=\"flex-shrink-0 w-4 flex items-center justify-center\">\n <ChevronRight className=\"w-3 h-3 opacity-50\" />\n </div>\n )}\n\n {/* Badge de tipo */}\n <div\n className={cn(\n 'flex-shrink-0 w-6 h-6 rounded text-xs font-semibold flex items-center justify-center',\n isSelected\n ? 'bg-white/20 text-white'\n : componentRegistry.get(block.type)?.pluginId\n ? 'bg-purple-100 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400'\n : 'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400'\n )}\n title={getBlockTypeName(block.type)}\n >\n {componentRegistry.get(block.type)?.pluginId ? '🧩' : block.type.substring(0, 2).toUpperCase()}\n </div>\n\n {/* Nome e preview */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-xs font-medium truncate\">{getBlockTypeName(block.type)}</div>\n <div\n className={cn(\n 'text-xs truncate',\n isSelected ? 'text-white/80' : 'text-gray-500 dark:text-gray-400'\n )}\n >\n {getBlockPreview(block)}\n </div>\n </div>\n\n {/* Botão deletar */}\n <button\n onClick={(e) => {\n e.stopPropagation()\n onDeleteBlock(block.id)\n }}\n className={cn(\n 'p-1.5 rounded-full transition-all opacity-0 group-hover:opacity-100',\n 'hover:bg-white/30 dark:hover:bg-gray-700/50',\n 'hover:scale-110 active:scale-95',\n isSelected\n ? 'text-white hover:bg-white/40'\n : 'text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'\n )}\n title=\"Deletar bloco\"\n >\n <Trash2 className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n\n {/* Renderizar children recursivamente */}\n {hasChildren && (\n <div className=\"mt-1\">\n {props.children\n .filter((child: Block) => child && child.id && child.type) // Filtrar children inválidos\n .map((child: Block) =>\n renderBlockTree(child, depth + 1, selectedBlockId, onSelectBlock, onDeleteBlock)\n )}\n </div>\n )}\n </div>\n )\n}\n\nexport const BlockSelector = React.memo(function BlockSelector({\n structure,\n selectedBlockId,\n onSelectBlock,\n onDeleteBlock,\n}: BlockSelectorProps) {\n // Filtrar estrutura inválida ANTES de usar\n const validStructure = useMemo(() => {\n if (!structure || !Array.isArray(structure)) return []\n return structure.filter((b) => b && typeof b === 'object' && b.id && b.type)\n }, [structure])\n\n // Contar total de blocos (incluindo children) - usando useMemo para evitar recálculos\n const totalBlocks = useMemo(() => {\n const count = (blocks: Block[]): number => {\n if (!blocks || !Array.isArray(blocks) || blocks.length === 0) return 0\n \n let total = 0\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i]\n \n // Verificar se o bloco é válido ANTES de acessar qualquer propriedade\n if (!block || typeof block !== 'object' || !block.id || !block.type) {\n continue\n }\n \n total++ // Contar o bloco atual\n \n // Verificar props de forma segura - SEMPRE dentro de try-catch\n try {\n if (!block.props) {\n continue\n }\n \n const props = block.props\n if (props && typeof props === 'object' && !Array.isArray(props)) {\n const children = (props as Record<string, any>).children\n if (children && Array.isArray(children) && children.length > 0) {\n // Filtrar children inválidos antes de contar recursivamente\n const validChildren = children.filter(\n (child: unknown) => child && typeof child === 'object' && (child as Block).id && (child as Block).type\n )\n if (validChildren.length > 0) {\n total += count(validChildren)\n }\n }\n }\n } catch (error) {\n // Se houver erro ao acessar props, apenas continuar\n continue\n }\n }\n return total\n }\n \n return count(validStructure)\n }, [validStructure])\n\n return (\n <div className=\"h-full flex flex-col overflow-hidden\">\n {/* Header */}\n <div className=\"flex-shrink-0 p-3 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900\">\n <h2 className=\"text-sm font-semibold text-gray-800 dark:text-gray-100\">Blocos</h2>\n <p className=\"text-xs text-gray-500 dark:text-gray-400 mt-1\">\n {totalBlocks} {totalBlocks === 1 ? 'bloco' : 'blocos'}\n </p>\n </div>\n\n {/* Lista de blocos */}\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {!validStructure || validStructure.length === 0 ? (\n <div className=\"text-center text-gray-500 dark:text-gray-400 text-xs py-8\">\n Nenhum bloco adicionado ainda\n </div>\n ) : (\n validStructure.map((block) =>\n renderBlockTree(block, 0, selectedBlockId, onSelectBlock, onDeleteBlock)\n )\n )}\n </div>\n </div>\n )\n});\n"],"names":["getBlockTypeName","type","componentRegistry","getBlockPreview","block","props","renderBlockTree","depth","selectedBlockId","onSelectBlock","onDeleteBlock","isSelected","hasChildren","jsxs","cn","jsx","ChevronRight","Trash2","child","BlockSelector","React","structure","validStructure","useMemo","b","totalBlocks","count","blocks","total","i","children","validChildren"],"mappings":"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"}
@@ -1,8 +1,12 @@
1
- import { SiteDocument, SitePage, Block } from '../schema/siteDocument';
1
+ import { SiteDocument, SitePage, Block, PageSeoConfig, SiteMetadata } from '../schema/siteDocument';
2
2
  import { ThemeTokens } from '../schema/themeTokens';
3
3
  export interface ExportPageToHtmlOptions {
4
4
  /** Página de referência para layout (navbar + footer). Quando a página atual não é a home, inclui navbar e footer desta página. */
5
5
  layoutFromPage?: SitePage;
6
+ /** SEO overrides for this page (highest priority) */
7
+ seo?: PageSeoConfig;
8
+ /** Global site metadata */
9
+ siteMetadata?: SiteMetadata;
6
10
  }
7
11
  /**
8
12
  * Exporta uma página para HTML (com cache)
@@ -1 +1 @@
1
- {"version":3,"file":"exportHtml.d.ts","sourceRoot":"","sources":["../../../src/engine/export/exportHtml.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAA6B,WAAW,EAAE,MAAM,uBAAuB,CAAC;AA4Y/E,MAAM,WAAW,uBAAuB;IACtC,mIAAmI;IACnI,cAAc,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,YAAY,EACtB,QAAQ,GAAE,OAAc,EACxB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CA2HR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,GAClB,MAAM,CAER;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,YAAY,EACtB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAUR;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,YAAY,GACrB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBtC"}
1
+ {"version":3,"file":"exportHtml.d.ts","sourceRoot":"","sources":["../../../src/engine/export/exportHtml.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACpG,OAAO,EAA6B,WAAW,EAAE,MAAM,uBAAuB,CAAC;AA4Y/E,MAAM,WAAW,uBAAuB;IACtC,mIAAmI;IACnI,cAAc,CAAC,EAAE,QAAQ,CAAC;IAC1B,qDAAqD;IACrD,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAuFD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,YAAY,EACtB,QAAQ,GAAE,OAAc,EACxB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,uBAAuB,GAChC,MAAM,CA8HR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,GAClB,MAAM,CAER;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,YAAY,EACtB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAUR;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,YAAY,GACrB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBtC"}
@@ -1,6 +1,6 @@
1
- import { generateThemeCSSVariables as z } from "../schema/themeTokens.js";
2
- import { hashDocument as E } from "../../utils/documentHash.js";
3
- import { initializeExporters as S } from "./exporters/index.js";
1
+ import { generateThemeCSSVariables as I } from "../schema/themeTokens.js";
2
+ import { hashDocument as z } from "../../utils/documentHash.js";
3
+ import { initializeExporters as E } from "./exporters/index.js";
4
4
  import { htmlExportRegistry as H } from "./exporters/HtmlExporter.js";
5
5
  const C = `
6
6
  /* Navbar engine (sg-navbar) - base e variações */
@@ -301,41 +301,59 @@ const C = `
301
301
  font-size: 0.9rem;
302
302
  }
303
303
  }
304
- `, s = /* @__PURE__ */ new Map(), h = 50;
305
- function D() {
306
- if (s.size <= h) return;
307
- const r = Array.from(s.entries()).sort(
304
+ `, v = /* @__PURE__ */ new Map(), S = 50;
305
+ function j() {
306
+ if (v.size <= S) return;
307
+ const e = Array.from(v.entries()).sort(
308
308
  (t, a) => t[1].timestamp - a[1].timestamp
309
309
  );
310
- r.slice(0, r.length - h).forEach(([t]) => s.delete(t));
311
- }
312
- let u = !1;
313
- function l(r, e = 0, t, a) {
314
- u || (S(l), u = !0);
315
- const d = H.get(r.type);
316
- return d ? d(r, e, t, a) : `<div data-block-id="${r.id}" style="color: red; padding: 1rem; border: 2px dashed red;">Bloco desconhecido: ${r.type}</div>`;
317
- }
318
- function j(r) {
319
- return r ? String(r).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;") : "";
320
- }
321
- function $(r, e, t = !0, a, d) {
322
- const o = d?.layoutFromPage, w = E(e), y = o?.id ?? "", c = `${w}-${r.id}-${a ?? ""}-${y}`;
323
- if (t && s.has(c)) {
324
- const n = s.get(c);
325
- return n.timestamp = Date.now(), n.html;
310
+ e.slice(0, e.length - S).forEach(([t]) => v.delete(t));
311
+ }
312
+ let D = !1;
313
+ function h(e, r = 0, t, a) {
314
+ D || (E(h), D = !0);
315
+ const s = H.get(e.type);
316
+ return s ? s(e, r, t, a) : `<div data-block-id="${e.id}" style="color: red; padding: 1rem; border: 2px dashed red;">Bloco desconhecido: ${e.type}</div>`;
317
+ }
318
+ function i(e) {
319
+ return e ? String(e).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;") : "";
320
+ }
321
+ function A(e, r) {
322
+ const t = r?.siteMetadata, a = t?.language || "pt-BR", s = r?.seo || {}, n = e.seo || {};
323
+ let b = {};
324
+ const y = e.structure.find((l) => l.type === "blogPostDetail");
325
+ if (y) {
326
+ const l = y.props;
327
+ b = {
328
+ metaTitle: l.metaTitle || l.title,
329
+ metaDescription: l.metaDescription || l.excerpt,
330
+ ogImage: l.ogImage || l.featuredImage,
331
+ ogType: "article"
332
+ };
326
333
  }
327
- const x = z(e.theme);
328
- let p = r.structure.map((n) => l(n, 0, a, e.theme)).join("");
329
- if (o && o.id !== r.id && o.structure?.length) {
330
- const n = o.structure, _ = r.structure.some((i) => i.type === "navbar"), k = r.structure.some((i) => i.type === "footer"), m = _ ? null : n.find((i) => i.type === "navbar"), v = m ? l(m, 0, a, e.theme) : "", b = k ? null : n.find((i) => i.type === "footer"), f = b && b.type !== "navbar" ? l(b, 0, a, e.theme) : "";
331
- (v || f) && (p = v + p + f);
334
+ const c = s.metaTitle || n.metaTitle || b.metaTitle || e.name, m = s.metaDescription || n.metaDescription || b.metaDescription, p = s.ogImage || n.ogImage || b.ogImage || t?.defaultOgImage, w = s.ogType || n.ogType || b.ogType || "website", u = s.canonicalUrl || n.canonicalUrl, x = s.noIndex ?? n.noIndex, g = t?.siteName, d = g && c !== g ? `${i(c)} | ${i(g)}` : i(c), o = [];
335
+ return m && o.push(`<meta name="description" content="${i(m)}">`), o.push(`<meta property="og:title" content="${i(c)}">`), o.push(`<meta property="og:type" content="${i(w)}">`), m && o.push(`<meta property="og:description" content="${i(m)}">`), p && o.push(`<meta property="og:image" content="${i(p)}">`), g && o.push(`<meta property="og:site_name" content="${i(g)}">`), u && o.push(`<meta property="og:url" content="${i(u)}">`), o.push(`<meta name="twitter:card" content="${p ? "summary_large_image" : "summary"}">`), o.push(`<meta name="twitter:title" content="${i(c)}">`), m && o.push(`<meta name="twitter:description" content="${i(m)}">`), p && o.push(`<meta name="twitter:image" content="${i(p)}">`), u && o.push(`<link rel="canonical" href="${i(u)}">`), x && o.push('<meta name="robots" content="noindex, nofollow">'), { tags: o.join(`
336
+ `), fullTitle: d, langAttr: a };
337
+ }
338
+ function M(e, r, t = !0, a, s) {
339
+ const n = s?.layoutFromPage, b = z(r), y = n?.id ?? "", c = `${b}-${e.id}-${a ?? ""}-${y}`;
340
+ if (t && v.has(c)) {
341
+ const d = v.get(c);
342
+ return d.timestamp = Date.now(), d.html;
332
343
  }
333
- const g = `<!DOCTYPE html>
334
- <html lang="pt-BR">
344
+ const m = I(r.theme);
345
+ let p = e.structure.map((d) => h(d, 0, a, r.theme)).join("");
346
+ if (n && n.id !== e.id && n.structure?.length) {
347
+ const d = n.structure, o = e.structure.some((f) => f.type === "navbar"), l = e.structure.some((f) => f.type === "footer"), k = o ? null : d.find((f) => f.type === "navbar"), $ = k ? h(k, 0, a, r.theme) : "", _ = l ? null : d.find((f) => f.type === "footer"), T = _ && _.type !== "navbar" ? h(_, 0, a, r.theme) : "";
348
+ ($ || T) && (p = $ + p + T);
349
+ }
350
+ const { tags: w, fullTitle: u, langAttr: x } = A(e, s), g = `<!DOCTYPE html>
351
+ <html lang="${x}">
335
352
  <head>
336
353
  <meta charset="UTF-8">
337
354
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
338
- <title>${j(r.name)}</title>
355
+ <title>${u}</title>
356
+ ${w}
339
357
  <style>
340
358
  * {
341
359
  margin: 0;
@@ -350,7 +368,7 @@ function $(r, e, t = !0, a, d) {
350
368
  line-height: 1.6;
351
369
  color: var(--sg-text, #1f2937);
352
370
  }
353
- ${x}
371
+ ${m}
354
372
 
355
373
  /* Landing Page Styles */
356
374
  ${C}
@@ -394,38 +412,38 @@ function $(r, e, t = !0, a, d) {
394
412
  ${p}
395
413
  </body>
396
414
  </html>`;
397
- return t && (s.set(c, { html: g, timestamp: Date.now() }), D()), g;
415
+ return t && (v.set(c, { html: g, timestamp: Date.now() }), j()), g;
398
416
  }
399
- function F(r, e, t) {
400
- return l(r, 0, e, t);
417
+ function L(e, r, t) {
418
+ return h(e, 0, r, t);
401
419
  }
402
- function P() {
403
- s.clear();
420
+ function U() {
421
+ v.clear();
404
422
  }
405
- function R(r, e) {
406
- const t = e ? r.pages.find((a) => a.id === e) : r.pages[0];
423
+ function q(e, r) {
424
+ const t = r ? e.pages.find((a) => a.id === r) : e.pages[0];
407
425
  if (!t)
408
426
  throw new Error("Page not found");
409
- return $(t, r);
427
+ return M(t, e);
410
428
  }
411
- function L(r) {
412
- const e = [];
429
+ function N(e) {
430
+ const r = [];
413
431
  function t(a) {
414
432
  if (a.type === "image") {
415
- const o = a.props.src;
416
- o && e.push({ type: "image", url: o });
433
+ const n = a.props.src;
434
+ n && r.push({ type: "image", url: n });
417
435
  }
418
436
  (a.props?.children || []).forEach(t);
419
437
  }
420
- return r.pages.forEach((a) => {
438
+ return e.pages.forEach((a) => {
421
439
  a.structure.forEach(t);
422
- }), e;
440
+ }), r;
423
441
  }
424
442
  export {
425
- P as clearHtmlCache,
426
- F as exportBlockToHtml,
427
- R as exportDocumentToHtml,
428
- $ as exportPageToHtml,
429
- L as generateAssetsManifest
443
+ U as clearHtmlCache,
444
+ L as exportBlockToHtml,
445
+ q as exportDocumentToHtml,
446
+ M as exportPageToHtml,
447
+ N as generateAssetsManifest
430
448
  };
431
449
  //# sourceMappingURL=exportHtml.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"exportHtml.js","sources":["../../../src/engine/export/exportHtml.ts"],"sourcesContent":["/**\n * HTML Exporter\n * Exporta SiteDocument para HTML estático\n */\n\nimport { SiteDocument, SitePage, Block } from \"../schema/siteDocument\";\nimport { generateThemeCSSVariables, ThemeTokens } from \"../schema/themeTokens\";\nimport { hashDocument } from \"../../utils/documentHash\";\nimport { htmlExportRegistry, initializeExporters } from \"./exporters\";\n\n/**\n * Landing Page CSS crítico para navbar e outros componentes\n * Incluído inline para garantir que sempre esteja disponível\n */\nconst landingPageCSS = `\n/* Navbar engine (sg-navbar) - base e variações */\n.sg-navbar {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 4.5rem;\n color: var(--sg-text, #1f2937);\n position: relative;\n transition: all 0.2s ease;\n background-color: var(--navbar-bg, var(--sg-bg, #fff));\n border-radius: var(--navbar-border-radius, 0);\n box-shadow: var(--navbar-shadow, 0 1px 2px 0 rgba(0, 0, 0, 0.05));\n}\n\n.sg-navbar::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: inherit;\n opacity: var(--navbar-blur-opacity, 0);\n border-radius: inherit;\n z-index: 0;\n pointer-events: none;\n backdrop-filter: blur(var(--navbar-blur-amount, 0px));\n -webkit-backdrop-filter: blur(var(--navbar-blur-amount, 0px));\n}\n\n.sg-navbar--floating {\n margin: 1rem;\n max-width: calc(100% - 2rem);\n border-radius: var(--navbar-border-radius, 12px);\n box-shadow: var(--navbar-shadow, 0 10px 40px rgba(0, 0, 0, 0.15));\n}\n\n/* Compact mode - 20% smaller height and smaller elements */\n.sg-navbar--compact {\n height: 3.6rem;\n}\n\n.sg-navbar--compact .sg-navbar__link {\n font-size: 0.875rem;\n padding: 0.375rem 0.5rem;\n}\n\n.sg-navbar--compact .sg-navbar__btn {\n font-size: 0.875rem;\n padding: 0.375rem 0.875rem;\n}\n\n.sg-navbar__container {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1.5rem;\n max-width: 1200px;\n width: 100%;\n height: 100%;\n margin: 0 auto;\n padding: 0 1rem;\n position: relative;\n z-index: 1;\n}\n\n.sg-navbar__brand {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n height: 100%;\n}\n\n.sg-navbar__brand a {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n height: 100%;\n text-decoration: none;\n color: var(--sg-primary, #3b82f6);\n font-weight: 700;\n font-size: 1.25rem;\n}\n\n.sg-navbar__brand img {\n object-fit: contain;\n object-position: left center;\n}\n\n.sg-navbar__brand-text {\n color: var(--sg-primary, #3b82f6);\n font-weight: 700;\n font-size: 1.25rem;\n}\n\n.sg-navbar__menu {\n display: flex;\n align-items: center;\n gap: 1.5rem;\n}\n\n.sg-navbar__link {\n color: var(--navbar-link-color, var(--sg-text, #1f2937));\n font-size: var(--navbar-link-size, 1rem);\n text-decoration: none;\n font-weight: 500;\n transition: color 0.2s ease;\n}\n\n.sg-navbar__link:hover {\n color: var(--navbar-link-hover-color, var(--sg-primary, #3b82f6));\n}\n\n.sg-navbar__btn {\n padding: 0.5rem 1rem;\n font-weight: 500;\n text-decoration: none;\n display: inline-block;\n transition: all 0.2s ease;\n border-radius: var(--navbar-btn-radius, 0.5rem);\n}\n\n.sg-navbar__btn--solid {\n background-color: var(--navbar-btn-bg, var(--sg-primary, #3b82f6));\n color: var(--navbar-btn-text, var(--sg-primary-text, #fff));\n border: none;\n}\n\n/* Hover effects are now controlled by the hover effects system */\n\n.sg-navbar__btn--outline {\n background-color: transparent;\n color: var(--navbar-btn-bg, var(--sg-primary, #3b82f6));\n border: 2px solid var(--navbar-btn-bg, var(--sg-primary, #3b82f6));\n}\n\n.sg-navbar__btn--ghost {\n background-color: transparent;\n color: var(--navbar-btn-bg, var(--sg-primary, #3b82f6));\n border: none;\n}\n\n.sg-navbar--classic.sg-navbar--floating {\n padding: 1rem 0;\n border-bottom: 1px solid var(--sg-border, #e5e7eb);\n}\n\n.sg-navbar--centered.sg-navbar--floating {\n padding: 1rem 0;\n border-bottom: 1px solid var(--sg-border, #e5e7eb);\n}\n\n.sg-navbar--centered .sg-navbar__container {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n align-items: center;\n}\n\n.sg-navbar--centered .sg-navbar__brand {\n justify-self: start;\n}\n\n.sg-navbar--centered .sg-navbar__menu {\n justify-self: center;\n}\n\n.sg-navbar--centered .sg-navbar__actions {\n justify-self: end;\n}\n\n/* Dropdown wrapper */\n.sg-navbar__dropdown-wrapper {\n position: relative;\n display: inline-block;\n}\n\n/* Hover bridge invisível - conecta o botão ao dropdown (16px gap + overlap) */\n.sg-navbar__dropdown-wrapper::before {\n content: \"\";\n position: absolute;\n top: 100%;\n left: -1rem;\n right: -1rem;\n height: 20px;\n background: transparent;\n}\n\n.sg-navbar__link--has-dropdown {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n background: none;\n border: none;\n cursor: pointer;\n font: inherit;\n color: inherit;\n padding: 0.5rem 0.75rem;\n border-radius: 6px;\n position: relative;\n text-decoration: none;\n font-weight: 500;\n transition: all 0.2s ease;\n}\n\n/* Chevron-down icon (▼) */\n.sg-navbar__link--has-dropdown::after {\n content: \"\";\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.25rem;\n border-left: 4px solid transparent;\n border-right: 4px solid transparent;\n border-top: 5px solid currentColor;\n transition: transform 0.2s ease;\n}\n\n.sg-navbar__dropdown-wrapper:hover .sg-navbar__link--has-dropdown::after {\n transform: rotate(180deg);\n}\n\n/* Dropdown container */\n.sg-navbar-dropdown {\n display: none;\n opacity: 0;\n position: absolute;\n top: calc(100% + 16px); /* Pequeno gap visual abaixo do navbar */\n left: 0;\n z-index: 1000;\n min-width: 200px;\n padding: 0.5rem 0;\n transform: translateY(-5px);\n transition: opacity 0.2s ease, transform 0.2s ease;\n overflow: visible;\n}\n\n/* Frost layer do dropdown - cobre todo o dropdown */\n.sg-navbar-dropdown::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: var(--dropdown-bg, rgba(255, 255, 255, 0.9));\n border-radius: var(--dropdown-radius, 25px);\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);\n pointer-events: none;\n z-index: 0;\n backdrop-filter: blur(var(--navbar-blur-amount, 0px));\n -webkit-backdrop-filter: blur(var(--navbar-blur-amount, 0px));\n}\n\n/* Dropdown items acima do frost layer */\n.sg-navbar-dropdown > * {\n position: relative;\n z-index: 1;\n}\n\n/* Show dropdown on hover/focus with animation */\n.sg-navbar__dropdown-wrapper:hover .sg-navbar-dropdown,\n.sg-navbar__dropdown-wrapper:focus-within .sg-navbar-dropdown {\n display: block;\n opacity: 1;\n transform: translateY(0);\n}\n\n/* Dropdown items */\n.sg-navbar-dropdown__item {\n display: block;\n white-space: nowrap;\n}\n\n/* Mobile - dropdowns sempre visíveis no sidebar */\n@media (max-width: 768px) {\n .sg-navbar-dropdown {\n position: static;\n display: block !important;\n opacity: 1 !important;\n transform: none !important;\n min-width: auto;\n padding-left: 1rem;\n background: transparent !important;\n box-shadow: none !important;\n margin-top: 0.25rem;\n }\n\n .sg-navbar__link--has-dropdown::after {\n content: none;\n }\n\n .sg-navbar-dropdown__item {\n padding: 0.5rem 1.25rem;\n font-size: 0.9rem;\n }\n}\n`;\n\n/**\n * Cache de HTML com limite LRU (Last Recently Used)\n * Limite de 50 entradas para evitar memory leak\n */\nconst htmlCache = new Map<string, { html: string; timestamp: number }>();\nconst MAX_CACHE_SIZE = 50;\n\n/**\n * Limpa entradas antigas do cache quando excede o limite\n */\nfunction cleanCache() {\n if (htmlCache.size <= MAX_CACHE_SIZE) return;\n\n // Ordenar por timestamp e remover os mais antigos\n const entries = Array.from(htmlCache.entries()).sort(\n (a, b) => a[1].timestamp - b[1].timestamp,\n );\n\n const toRemove = entries.slice(0, entries.length - MAX_CACHE_SIZE);\n toRemove.forEach(([key]) => htmlCache.delete(key));\n}\n\n// Inicializar exporters com referência à função de renderização\nlet exportersInitialized = false;\n\n/**\n * Renderiza um bloco diretamente para HTML (sem React)\n * Usa registry pattern para despachar para exporters modulares\n */\nfunction blockToHtmlDirect(\n block: Block,\n depth: number = 0,\n basePath?: string,\n theme?: ThemeTokens,\n): string {\n // Inicializar exporters na primeira execução\n if (!exportersInitialized) {\n initializeExporters(blockToHtmlDirect);\n exportersInitialized = true;\n }\n\n // Buscar exporter no registry\n const exporter = htmlExportRegistry.get(block.type);\n\n if (!exporter) {\n // Fallback para blocos desconhecidos\n return `<div data-block-id=\"${block.id}\" style=\"color: red; padding: 1rem; border: 2px dashed red;\">Bloco desconhecido: ${block.type}</div>`;\n }\n\n // Executar exporter\n return exporter(block, depth, basePath, theme);\n}\n\n/**\n * Escapa HTML para prevenir XSS\n * NOTA: Esta função foi mantida aqui por compatibilidade, mas está duplicada\n * em shared/htmlHelpers.ts onde é usada pelos exporters\n */\nfunction escapeHtml(text: string): string {\n if (!text) return \"\";\n return String(text)\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\n// ============================================================================\n// CÓDIGO ANTIGO REMOVIDO - Agora usamos Registry Pattern\n// ============================================================================\n// Todo o switch gigante de blockToHtmlDirect foi refatorado para exporters\n// modulares em /exporters/. Mantido apenas as linhas abaixo para referência\n// do que foi removido:\n//\n// - ~1200 linhas de código de switch/case\n// - 40+ casos de tipos de blocos\n// - Lógica de renderização HTML inline\n//\n// Agora distribuído em:\n// - /exporters/layout/*.ts (Container, Stack, Grid, Box, Section)\n// - /exporters/content/*.ts (Heading, Text, Image, Button, Link, etc)\n// - /exporters/sections/*.ts (Hero, Navbar, Footer, Marketing sections)\n// - /exporters/forms/*.ts (Form, Input, Textarea, FormSelect)\n// ============================================================================\n\n\nexport interface ExportPageToHtmlOptions {\n /** Página de referência para layout (navbar + footer). Quando a página atual não é a home, inclui navbar e footer desta página. */\n layoutFromPage?: SitePage;\n}\n\n/**\n * Exporta uma página para HTML (com cache)\n * @param basePath - Base path para links (ex.: /site ou /site/escola/:slug)\n * @param options - layoutFromPage: quando informado e diferente da página atual, inclui navbar (primeiro bloco navbar) e footer (último bloco) da página de referência\n */\nexport function exportPageToHtml(\n page: SitePage,\n document: SiteDocument,\n useCache: boolean = true,\n basePath?: string,\n options?: ExportPageToHtmlOptions,\n): string {\n const layoutFromPage = options?.layoutFromPage;\n const docHash = hashDocument(document);\n const layoutId = layoutFromPage?.id ?? \"\";\n const cacheKey = `${docHash}-${page.id}-${basePath ?? \"\"}-${layoutId}`;\n\n // Verificar cache\n if (useCache && htmlCache.has(cacheKey)) {\n const cached = htmlCache.get(cacheKey)!;\n cached.timestamp = Date.now();\n return cached.html;\n }\n\n // Gerar HTML\n const themeCSS = generateThemeCSSVariables(document.theme);\n let bodyHtml = page.structure\n .map((block) => blockToHtmlDirect(block, 0, basePath, document.theme))\n .join(\"\");\n\n // Layout compartilhado: em páginas não-home, incluir navbar e footer da página de referência (ex.: home)\n // Pula injeção se a página já possui seu próprio navbar/footer (ex.: páginas de plugin)\n if (\n layoutFromPage &&\n layoutFromPage.id !== page.id &&\n layoutFromPage.structure?.length\n ) {\n const layoutStructure = layoutFromPage.structure;\n\n const pageHasNavbar = page.structure.some((b) => b.type === \"navbar\");\n const pageHasFooter = page.structure.some((b) => b.type === \"footer\");\n\n const navbarBlock = !pageHasNavbar\n ? layoutStructure.find((b) => b.type === \"navbar\")\n : null;\n const navbarHtml = navbarBlock\n ? blockToHtmlDirect(navbarBlock, 0, basePath, document.theme)\n : \"\";\n\n const footerBlock = !pageHasFooter\n ? layoutStructure.find((b) => b.type === \"footer\")\n : null;\n const footerHtml =\n footerBlock && footerBlock.type !== \"navbar\"\n ? blockToHtmlDirect(footerBlock, 0, basePath, document.theme)\n : \"\";\n\n if (navbarHtml || footerHtml) {\n bodyHtml = navbarHtml + bodyHtml + footerHtml;\n }\n }\n\n const html = `<!DOCTYPE html>\n<html lang=\"pt-BR\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeHtml(page.name)}</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n html {\n scroll-behavior: smooth;\n }\n body {\n font-family: var(--sg-font-body, system-ui, -apple-system, sans-serif);\n line-height: 1.6;\n color: var(--sg-text, #1f2937);\n }\n ${themeCSS}\n\n /* Landing Page Styles */\n ${landingPageCSS}\n </style>\n <script>\n // Smooth scroll para âncoras sem reload\n document.addEventListener('DOMContentLoaded', function() {\n // Interceptar cliques em links com âncoras\n document.querySelectorAll('a[href^=\"#\"]').forEach(function(anchor) {\n anchor.addEventListener('click', function(e) {\n const href = this.getAttribute('href');\n\n // Ignorar # vazio\n if (!href || href === '#') return;\n\n // Prevenir comportamento padrão (reload)\n e.preventDefault();\n\n // Encontrar o elemento alvo\n const targetId = href.substring(1); // Remove o #\n const targetElement = document.getElementById(targetId);\n\n if (targetElement) {\n // Scroll suave até o elemento\n targetElement.scrollIntoView({\n behavior: 'smooth',\n block: 'start'\n });\n\n // Atualizar URL sem reload (para manter histórico de navegação)\n if (history.pushState) {\n history.pushState(null, '', href);\n }\n }\n });\n });\n });\n </script>\n</head>\n<body>\n ${bodyHtml}\n</body>\n</html>`;\n\n // Armazenar no cache\n if (useCache) {\n htmlCache.set(cacheKey, { html, timestamp: Date.now() });\n cleanCache();\n }\n\n return html;\n}\n\n/**\n * Exporta apenas um bloco para HTML (para atualização parcial)\n */\nexport function exportBlockToHtml(\n block: Block,\n basePath?: string,\n theme?: ThemeTokens\n): string {\n return blockToHtmlDirect(block, 0, basePath, theme);\n}\n\n/**\n * Limpa o cache de HTML\n */\nexport function clearHtmlCache(): void {\n htmlCache.clear();\n}\n\n/**\n * Exporta documento completo para HTML\n *\n * Nota: Não sanitiza o HTML pois exportPageToHtml() já gera HTML seguro\n * (conteúdo de usuário é escapado via escapeHtml nos exporters).\n * A sanitização anterior (sanitizeHtml) removia o <head> inteiro,\n * perdendo CSS de tema, hover effects e landing page styles.\n */\nexport function exportDocumentToHtml(\n document: SiteDocument,\n pageId?: string,\n): string {\n const page = pageId\n ? document.pages.find((p) => p.id === pageId)\n : document.pages[0];\n\n if (!page) {\n throw new Error(\"Page not found\");\n }\n\n return exportPageToHtml(page, document);\n}\n\n/**\n * Gera manifest de assets (imagens, fontes, etc)\n */\nexport function generateAssetsManifest(\n document: SiteDocument,\n): Array<{ type: string; url: string }> {\n const assets: Array<{ type: string; url: string }> = [];\n\n function extractAssetsFromBlock(block: Block) {\n if (block.type === \"image\") {\n const src = (block as any).props.src;\n if (src) {\n assets.push({ type: \"image\", url: src });\n }\n }\n\n // Recursivamente extrair de children\n const children = (block as any).props?.children || [];\n children.forEach(extractAssetsFromBlock);\n }\n\n document.pages.forEach((page) => {\n page.structure.forEach(extractAssetsFromBlock);\n });\n\n return assets;\n}\n"],"names":["landingPageCSS","htmlCache","MAX_CACHE_SIZE","cleanCache","entries","a","b","key","exportersInitialized","blockToHtmlDirect","block","depth","basePath","theme","initializeExporters","exporter","htmlExportRegistry","escapeHtml","text","exportPageToHtml","page","document","useCache","options","layoutFromPage","docHash","hashDocument","layoutId","cacheKey","cached","themeCSS","generateThemeCSSVariables","bodyHtml","layoutStructure","pageHasNavbar","pageHasFooter","navbarBlock","navbarHtml","footerBlock","footerHtml","html","exportBlockToHtml","clearHtmlCache","exportDocumentToHtml","pageId","p","generateAssetsManifest","assets","extractAssetsFromBlock","src"],"mappings":";;;;AAcA,MAAMA,IAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAiTjBC,wBAAgB,IAAA,GAChBC,IAAiB;AAKvB,SAASC,IAAa;AACpB,MAAIF,EAAU,QAAQC,EAAgB;AAGtC,QAAME,IAAU,MAAM,KAAKH,EAAU,QAAA,CAAS,EAAE;AAAA,IAC9C,CAACI,GAAGC,MAAMD,EAAE,CAAC,EAAE,YAAYC,EAAE,CAAC,EAAE;AAAA,EAAA;AAIlC,EADiBF,EAAQ,MAAM,GAAGA,EAAQ,SAASF,CAAc,EACxD,QAAQ,CAAC,CAACK,CAAG,MAAMN,EAAU,OAAOM,CAAG,CAAC;AACnD;AAGA,IAAIC,IAAuB;AAM3B,SAASC,EACPC,GACAC,IAAgB,GAChBC,GACAC,GACQ;AAER,EAAKL,MACHM,EAAoBL,CAAiB,GACrCD,IAAuB;AAIzB,QAAMO,IAAWC,EAAmB,IAAIN,EAAM,IAAI;AAElD,SAAKK,IAMEA,EAASL,GAAOC,GAAOC,GAAUC,CAAK,IAJpC,uBAAuBH,EAAM,EAAE,oFAAoFA,EAAM,IAAI;AAKxI;AAOA,SAASO,EAAWC,GAAsB;AACxC,SAAKA,IACE,OAAOA,CAAI,EACf,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ,IANP;AAOpB;AA+BO,SAASC,EACdC,GACAC,GACAC,IAAoB,IACpBV,GACAW,GACQ;AACR,QAAMC,IAAiBD,GAAS,gBAC1BE,IAAUC,EAAaL,CAAQ,GAC/BM,IAAWH,GAAgB,MAAM,IACjCI,IAAW,GAAGH,CAAO,IAAIL,EAAK,EAAE,IAAIR,KAAY,EAAE,IAAIe,CAAQ;AAGpE,MAAIL,KAAYrB,EAAU,IAAI2B,CAAQ,GAAG;AACvC,UAAMC,IAAS5B,EAAU,IAAI2B,CAAQ;AACrC,WAAAC,EAAO,YAAY,KAAK,IAAA,GACjBA,EAAO;AAAA,EAChB;AAGA,QAAMC,IAAWC,EAA0BV,EAAS,KAAK;AACzD,MAAIW,IAAWZ,EAAK,UACjB,IAAI,CAACV,MAAUD,EAAkBC,GAAO,GAAGE,GAAUS,EAAS,KAAK,CAAC,EACpE,KAAK,EAAE;AAIV,MACEG,KACAA,EAAe,OAAOJ,EAAK,MAC3BI,EAAe,WAAW,QAC1B;AACA,UAAMS,IAAkBT,EAAe,WAEjCU,IAAgBd,EAAK,UAAU,KAAK,CAACd,MAAMA,EAAE,SAAS,QAAQ,GAC9D6B,IAAgBf,EAAK,UAAU,KAAK,CAACd,MAAMA,EAAE,SAAS,QAAQ,GAE9D8B,IAAeF,IAEjB,OADAD,EAAgB,KAAK,CAAC3B,MAAMA,EAAE,SAAS,QAAQ,GAE7C+B,IAAaD,IACf3B,EAAkB2B,GAAa,GAAGxB,GAAUS,EAAS,KAAK,IAC1D,IAEEiB,IAAeH,IAEjB,OADAF,EAAgB,KAAK,CAAC3B,MAAMA,EAAE,SAAS,QAAQ,GAE7CiC,IACJD,KAAeA,EAAY,SAAS,WAChC7B,EAAkB6B,GAAa,GAAG1B,GAAUS,EAAS,KAAK,IAC1D;AAEN,KAAIgB,KAAcE,OAChBP,IAAWK,IAAaL,IAAWO;AAAA,EAEvC;AAEA,QAAMC,IAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKJvB,EAAWG,EAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAe1BU,CAAQ;AAAA;AAAA;AAAA,MAGR9B,CAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAsChBgC,CAAQ;AAAA;AAAA;AAKV,SAAIV,MACFrB,EAAU,IAAI2B,GAAU,EAAE,MAAAY,GAAM,WAAW,KAAK,IAAA,GAAO,GACvDrC,EAAA,IAGKqC;AACT;AAKO,SAASC,EACd/B,GACAE,GACAC,GACQ;AACR,SAAOJ,EAAkBC,GAAO,GAAGE,GAAUC,CAAK;AACpD;AAKO,SAAS6B,IAAuB;AACrC,EAAAzC,EAAU,MAAA;AACZ;AAUO,SAAS0C,EACdtB,GACAuB,GACQ;AACR,QAAMxB,IAAOwB,IACTvB,EAAS,MAAM,KAAK,CAACwB,MAAMA,EAAE,OAAOD,CAAM,IAC1CvB,EAAS,MAAM,CAAC;AAEpB,MAAI,CAACD;AACH,UAAM,IAAI,MAAM,gBAAgB;AAGlC,SAAOD,EAAiBC,GAAMC,CAAQ;AACxC;AAKO,SAASyB,EACdzB,GACsC;AACtC,QAAM0B,IAA+C,CAAA;AAErD,WAASC,EAAuBtC,GAAc;AAC5C,QAAIA,EAAM,SAAS,SAAS;AAC1B,YAAMuC,IAAOvC,EAAc,MAAM;AACjC,MAAIuC,KACFF,EAAO,KAAK,EAAE,MAAM,SAAS,KAAKE,GAAK;AAAA,IAE3C;AAIA,KADkBvC,EAAc,OAAO,YAAY,CAAA,GAC1C,QAAQsC,CAAsB;AAAA,EACzC;AAEA,SAAA3B,EAAS,MAAM,QAAQ,CAACD,MAAS;AAC/B,IAAAA,EAAK,UAAU,QAAQ4B,CAAsB;AAAA,EAC/C,CAAC,GAEMD;AACT;"}
1
+ {"version":3,"file":"exportHtml.js","sources":["../../../src/engine/export/exportHtml.ts"],"sourcesContent":["/**\n * HTML Exporter\n * Exporta SiteDocument para HTML estático\n */\n\nimport { SiteDocument, SitePage, Block, PageSeoConfig, SiteMetadata } from \"../schema/siteDocument\";\nimport { generateThemeCSSVariables, ThemeTokens } from \"../schema/themeTokens\";\nimport { hashDocument } from \"../../utils/documentHash\";\nimport { htmlExportRegistry, initializeExporters } from \"./exporters\";\n\n/**\n * Landing Page CSS crítico para navbar e outros componentes\n * Incluído inline para garantir que sempre esteja disponível\n */\nconst landingPageCSS = `\n/* Navbar engine (sg-navbar) - base e variações */\n.sg-navbar {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 4.5rem;\n color: var(--sg-text, #1f2937);\n position: relative;\n transition: all 0.2s ease;\n background-color: var(--navbar-bg, var(--sg-bg, #fff));\n border-radius: var(--navbar-border-radius, 0);\n box-shadow: var(--navbar-shadow, 0 1px 2px 0 rgba(0, 0, 0, 0.05));\n}\n\n.sg-navbar::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: inherit;\n opacity: var(--navbar-blur-opacity, 0);\n border-radius: inherit;\n z-index: 0;\n pointer-events: none;\n backdrop-filter: blur(var(--navbar-blur-amount, 0px));\n -webkit-backdrop-filter: blur(var(--navbar-blur-amount, 0px));\n}\n\n.sg-navbar--floating {\n margin: 1rem;\n max-width: calc(100% - 2rem);\n border-radius: var(--navbar-border-radius, 12px);\n box-shadow: var(--navbar-shadow, 0 10px 40px rgba(0, 0, 0, 0.15));\n}\n\n/* Compact mode - 20% smaller height and smaller elements */\n.sg-navbar--compact {\n height: 3.6rem;\n}\n\n.sg-navbar--compact .sg-navbar__link {\n font-size: 0.875rem;\n padding: 0.375rem 0.5rem;\n}\n\n.sg-navbar--compact .sg-navbar__btn {\n font-size: 0.875rem;\n padding: 0.375rem 0.875rem;\n}\n\n.sg-navbar__container {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1.5rem;\n max-width: 1200px;\n width: 100%;\n height: 100%;\n margin: 0 auto;\n padding: 0 1rem;\n position: relative;\n z-index: 1;\n}\n\n.sg-navbar__brand {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n height: 100%;\n}\n\n.sg-navbar__brand a {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n height: 100%;\n text-decoration: none;\n color: var(--sg-primary, #3b82f6);\n font-weight: 700;\n font-size: 1.25rem;\n}\n\n.sg-navbar__brand img {\n object-fit: contain;\n object-position: left center;\n}\n\n.sg-navbar__brand-text {\n color: var(--sg-primary, #3b82f6);\n font-weight: 700;\n font-size: 1.25rem;\n}\n\n.sg-navbar__menu {\n display: flex;\n align-items: center;\n gap: 1.5rem;\n}\n\n.sg-navbar__link {\n color: var(--navbar-link-color, var(--sg-text, #1f2937));\n font-size: var(--navbar-link-size, 1rem);\n text-decoration: none;\n font-weight: 500;\n transition: color 0.2s ease;\n}\n\n.sg-navbar__link:hover {\n color: var(--navbar-link-hover-color, var(--sg-primary, #3b82f6));\n}\n\n.sg-navbar__btn {\n padding: 0.5rem 1rem;\n font-weight: 500;\n text-decoration: none;\n display: inline-block;\n transition: all 0.2s ease;\n border-radius: var(--navbar-btn-radius, 0.5rem);\n}\n\n.sg-navbar__btn--solid {\n background-color: var(--navbar-btn-bg, var(--sg-primary, #3b82f6));\n color: var(--navbar-btn-text, var(--sg-primary-text, #fff));\n border: none;\n}\n\n/* Hover effects are now controlled by the hover effects system */\n\n.sg-navbar__btn--outline {\n background-color: transparent;\n color: var(--navbar-btn-bg, var(--sg-primary, #3b82f6));\n border: 2px solid var(--navbar-btn-bg, var(--sg-primary, #3b82f6));\n}\n\n.sg-navbar__btn--ghost {\n background-color: transparent;\n color: var(--navbar-btn-bg, var(--sg-primary, #3b82f6));\n border: none;\n}\n\n.sg-navbar--classic.sg-navbar--floating {\n padding: 1rem 0;\n border-bottom: 1px solid var(--sg-border, #e5e7eb);\n}\n\n.sg-navbar--centered.sg-navbar--floating {\n padding: 1rem 0;\n border-bottom: 1px solid var(--sg-border, #e5e7eb);\n}\n\n.sg-navbar--centered .sg-navbar__container {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n align-items: center;\n}\n\n.sg-navbar--centered .sg-navbar__brand {\n justify-self: start;\n}\n\n.sg-navbar--centered .sg-navbar__menu {\n justify-self: center;\n}\n\n.sg-navbar--centered .sg-navbar__actions {\n justify-self: end;\n}\n\n/* Dropdown wrapper */\n.sg-navbar__dropdown-wrapper {\n position: relative;\n display: inline-block;\n}\n\n/* Hover bridge invisível - conecta o botão ao dropdown (16px gap + overlap) */\n.sg-navbar__dropdown-wrapper::before {\n content: \"\";\n position: absolute;\n top: 100%;\n left: -1rem;\n right: -1rem;\n height: 20px;\n background: transparent;\n}\n\n.sg-navbar__link--has-dropdown {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n background: none;\n border: none;\n cursor: pointer;\n font: inherit;\n color: inherit;\n padding: 0.5rem 0.75rem;\n border-radius: 6px;\n position: relative;\n text-decoration: none;\n font-weight: 500;\n transition: all 0.2s ease;\n}\n\n/* Chevron-down icon (▼) */\n.sg-navbar__link--has-dropdown::after {\n content: \"\";\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 0.25rem;\n border-left: 4px solid transparent;\n border-right: 4px solid transparent;\n border-top: 5px solid currentColor;\n transition: transform 0.2s ease;\n}\n\n.sg-navbar__dropdown-wrapper:hover .sg-navbar__link--has-dropdown::after {\n transform: rotate(180deg);\n}\n\n/* Dropdown container */\n.sg-navbar-dropdown {\n display: none;\n opacity: 0;\n position: absolute;\n top: calc(100% + 16px); /* Pequeno gap visual abaixo do navbar */\n left: 0;\n z-index: 1000;\n min-width: 200px;\n padding: 0.5rem 0;\n transform: translateY(-5px);\n transition: opacity 0.2s ease, transform 0.2s ease;\n overflow: visible;\n}\n\n/* Frost layer do dropdown - cobre todo o dropdown */\n.sg-navbar-dropdown::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: var(--dropdown-bg, rgba(255, 255, 255, 0.9));\n border-radius: var(--dropdown-radius, 25px);\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);\n pointer-events: none;\n z-index: 0;\n backdrop-filter: blur(var(--navbar-blur-amount, 0px));\n -webkit-backdrop-filter: blur(var(--navbar-blur-amount, 0px));\n}\n\n/* Dropdown items acima do frost layer */\n.sg-navbar-dropdown > * {\n position: relative;\n z-index: 1;\n}\n\n/* Show dropdown on hover/focus with animation */\n.sg-navbar__dropdown-wrapper:hover .sg-navbar-dropdown,\n.sg-navbar__dropdown-wrapper:focus-within .sg-navbar-dropdown {\n display: block;\n opacity: 1;\n transform: translateY(0);\n}\n\n/* Dropdown items */\n.sg-navbar-dropdown__item {\n display: block;\n white-space: nowrap;\n}\n\n/* Mobile - dropdowns sempre visíveis no sidebar */\n@media (max-width: 768px) {\n .sg-navbar-dropdown {\n position: static;\n display: block !important;\n opacity: 1 !important;\n transform: none !important;\n min-width: auto;\n padding-left: 1rem;\n background: transparent !important;\n box-shadow: none !important;\n margin-top: 0.25rem;\n }\n\n .sg-navbar__link--has-dropdown::after {\n content: none;\n }\n\n .sg-navbar-dropdown__item {\n padding: 0.5rem 1.25rem;\n font-size: 0.9rem;\n }\n}\n`;\n\n/**\n * Cache de HTML com limite LRU (Last Recently Used)\n * Limite de 50 entradas para evitar memory leak\n */\nconst htmlCache = new Map<string, { html: string; timestamp: number }>();\nconst MAX_CACHE_SIZE = 50;\n\n/**\n * Limpa entradas antigas do cache quando excede o limite\n */\nfunction cleanCache() {\n if (htmlCache.size <= MAX_CACHE_SIZE) return;\n\n // Ordenar por timestamp e remover os mais antigos\n const entries = Array.from(htmlCache.entries()).sort(\n (a, b) => a[1].timestamp - b[1].timestamp,\n );\n\n const toRemove = entries.slice(0, entries.length - MAX_CACHE_SIZE);\n toRemove.forEach(([key]) => htmlCache.delete(key));\n}\n\n// Inicializar exporters com referência à função de renderização\nlet exportersInitialized = false;\n\n/**\n * Renderiza um bloco diretamente para HTML (sem React)\n * Usa registry pattern para despachar para exporters modulares\n */\nfunction blockToHtmlDirect(\n block: Block,\n depth: number = 0,\n basePath?: string,\n theme?: ThemeTokens,\n): string {\n // Inicializar exporters na primeira execução\n if (!exportersInitialized) {\n initializeExporters(blockToHtmlDirect);\n exportersInitialized = true;\n }\n\n // Buscar exporter no registry\n const exporter = htmlExportRegistry.get(block.type);\n\n if (!exporter) {\n // Fallback para blocos desconhecidos\n return `<div data-block-id=\"${block.id}\" style=\"color: red; padding: 1rem; border: 2px dashed red;\">Bloco desconhecido: ${block.type}</div>`;\n }\n\n // Executar exporter\n return exporter(block, depth, basePath, theme);\n}\n\n/**\n * Escapa HTML para prevenir XSS\n * NOTA: Esta função foi mantida aqui por compatibilidade, mas está duplicada\n * em shared/htmlHelpers.ts onde é usada pelos exporters\n */\nfunction escapeHtml(text: string): string {\n if (!text) return \"\";\n return String(text)\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\n// ============================================================================\n// CÓDIGO ANTIGO REMOVIDO - Agora usamos Registry Pattern\n// ============================================================================\n// Todo o switch gigante de blockToHtmlDirect foi refatorado para exporters\n// modulares em /exporters/. Mantido apenas as linhas abaixo para referência\n// do que foi removido:\n//\n// - ~1200 linhas de código de switch/case\n// - 40+ casos de tipos de blocos\n// - Lógica de renderização HTML inline\n//\n// Agora distribuído em:\n// - /exporters/layout/*.ts (Container, Stack, Grid, Box, Section)\n// - /exporters/content/*.ts (Heading, Text, Image, Button, Link, etc)\n// - /exporters/sections/*.ts (Hero, Navbar, Footer, Marketing sections)\n// - /exporters/forms/*.ts (Form, Input, Textarea, FormSelect)\n// ============================================================================\n\n\nexport interface ExportPageToHtmlOptions {\n /** Página de referência para layout (navbar + footer). Quando a página atual não é a home, inclui navbar e footer desta página. */\n layoutFromPage?: SitePage;\n /** SEO overrides for this page (highest priority) */\n seo?: PageSeoConfig;\n /** Global site metadata */\n siteMetadata?: SiteMetadata;\n}\n\n/**\n * Generates SEO meta tags for a page.\n * Priority: options.seo > page.seo > auto-extract from blogPostDetail block\n */\nfunction generateSeoMetaTags(\n page: SitePage,\n options?: ExportPageToHtmlOptions,\n): { tags: string; fullTitle: string; langAttr: string } {\n const siteMetadata = options?.siteMetadata;\n const langAttr = siteMetadata?.language || \"pt-BR\";\n\n // Merge SEO: options.seo > page.seo > auto-extract from blogPostDetail\n const optionsSeo = options?.seo || {};\n const pageSeo = page.seo || {};\n\n // Auto-extract from blogPostDetail block if present\n let autoSeo: Partial<PageSeoConfig> = {};\n const detailBlock = page.structure.find((b) => b.type === \"blogPostDetail\");\n if (detailBlock) {\n const props = (detailBlock as any).props;\n autoSeo = {\n metaTitle: props.metaTitle || props.title,\n metaDescription: props.metaDescription || props.excerpt,\n ogImage: props.ogImage || props.featuredImage,\n ogType: \"article\",\n };\n }\n\n const metaTitle = optionsSeo.metaTitle || pageSeo.metaTitle || autoSeo.metaTitle || page.name;\n const metaDescription = optionsSeo.metaDescription || pageSeo.metaDescription || autoSeo.metaDescription;\n const ogImage = optionsSeo.ogImage || pageSeo.ogImage || autoSeo.ogImage || siteMetadata?.defaultOgImage;\n const ogType = optionsSeo.ogType || pageSeo.ogType || autoSeo.ogType || \"website\";\n const canonicalUrl = optionsSeo.canonicalUrl || pageSeo.canonicalUrl;\n const noIndex = optionsSeo.noIndex ?? pageSeo.noIndex;\n const siteName = siteMetadata?.siteName;\n\n const fullTitle = siteName && metaTitle !== siteName\n ? `${escapeHtml(metaTitle)} | ${escapeHtml(siteName)}`\n : escapeHtml(metaTitle);\n\n const parts: string[] = [];\n\n if (metaDescription) {\n parts.push(`<meta name=\"description\" content=\"${escapeHtml(metaDescription)}\">`);\n }\n\n // Open Graph\n parts.push(`<meta property=\"og:title\" content=\"${escapeHtml(metaTitle)}\">`);\n parts.push(`<meta property=\"og:type\" content=\"${escapeHtml(ogType)}\">`);\n if (metaDescription) {\n parts.push(`<meta property=\"og:description\" content=\"${escapeHtml(metaDescription)}\">`);\n }\n if (ogImage) {\n parts.push(`<meta property=\"og:image\" content=\"${escapeHtml(ogImage)}\">`);\n }\n if (siteName) {\n parts.push(`<meta property=\"og:site_name\" content=\"${escapeHtml(siteName)}\">`);\n }\n if (canonicalUrl) {\n parts.push(`<meta property=\"og:url\" content=\"${escapeHtml(canonicalUrl)}\">`);\n }\n\n // Twitter Card\n parts.push(`<meta name=\"twitter:card\" content=\"${ogImage ? \"summary_large_image\" : \"summary\"}\">`);\n parts.push(`<meta name=\"twitter:title\" content=\"${escapeHtml(metaTitle)}\">`);\n if (metaDescription) {\n parts.push(`<meta name=\"twitter:description\" content=\"${escapeHtml(metaDescription)}\">`);\n }\n if (ogImage) {\n parts.push(`<meta name=\"twitter:image\" content=\"${escapeHtml(ogImage)}\">`);\n }\n\n // Canonical URL\n if (canonicalUrl) {\n parts.push(`<link rel=\"canonical\" href=\"${escapeHtml(canonicalUrl)}\">`);\n }\n\n // Robots\n if (noIndex) {\n parts.push(`<meta name=\"robots\" content=\"noindex, nofollow\">`);\n }\n\n return { tags: parts.join(\"\\n \"), fullTitle, langAttr };\n}\n\n/**\n * Exporta uma página para HTML (com cache)\n * @param basePath - Base path para links (ex.: /site ou /site/escola/:slug)\n * @param options - layoutFromPage: quando informado e diferente da página atual, inclui navbar (primeiro bloco navbar) e footer (último bloco) da página de referência\n */\nexport function exportPageToHtml(\n page: SitePage,\n document: SiteDocument,\n useCache: boolean = true,\n basePath?: string,\n options?: ExportPageToHtmlOptions,\n): string {\n const layoutFromPage = options?.layoutFromPage;\n const docHash = hashDocument(document);\n const layoutId = layoutFromPage?.id ?? \"\";\n const cacheKey = `${docHash}-${page.id}-${basePath ?? \"\"}-${layoutId}`;\n\n // Verificar cache\n if (useCache && htmlCache.has(cacheKey)) {\n const cached = htmlCache.get(cacheKey)!;\n cached.timestamp = Date.now();\n return cached.html;\n }\n\n // Gerar HTML\n const themeCSS = generateThemeCSSVariables(document.theme);\n let bodyHtml = page.structure\n .map((block) => blockToHtmlDirect(block, 0, basePath, document.theme))\n .join(\"\");\n\n // Layout compartilhado: em páginas não-home, incluir navbar e footer da página de referência (ex.: home)\n // Pula injeção se a página já possui seu próprio navbar/footer (ex.: páginas de plugin)\n if (\n layoutFromPage &&\n layoutFromPage.id !== page.id &&\n layoutFromPage.structure?.length\n ) {\n const layoutStructure = layoutFromPage.structure;\n\n const pageHasNavbar = page.structure.some((b) => b.type === \"navbar\");\n const pageHasFooter = page.structure.some((b) => b.type === \"footer\");\n\n const navbarBlock = !pageHasNavbar\n ? layoutStructure.find((b) => b.type === \"navbar\")\n : null;\n const navbarHtml = navbarBlock\n ? blockToHtmlDirect(navbarBlock, 0, basePath, document.theme)\n : \"\";\n\n const footerBlock = !pageHasFooter\n ? layoutStructure.find((b) => b.type === \"footer\")\n : null;\n const footerHtml =\n footerBlock && footerBlock.type !== \"navbar\"\n ? blockToHtmlDirect(footerBlock, 0, basePath, document.theme)\n : \"\";\n\n if (navbarHtml || footerHtml) {\n bodyHtml = navbarHtml + bodyHtml + footerHtml;\n }\n }\n\n const { tags: seoMetaTags, fullTitle, langAttr } = generateSeoMetaTags(page, options);\n\n const html = `<!DOCTYPE html>\n<html lang=\"${langAttr}\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${fullTitle}</title>\n ${seoMetaTags}\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n html {\n scroll-behavior: smooth;\n }\n body {\n font-family: var(--sg-font-body, system-ui, -apple-system, sans-serif);\n line-height: 1.6;\n color: var(--sg-text, #1f2937);\n }\n ${themeCSS}\n\n /* Landing Page Styles */\n ${landingPageCSS}\n </style>\n <script>\n // Smooth scroll para âncoras sem reload\n document.addEventListener('DOMContentLoaded', function() {\n // Interceptar cliques em links com âncoras\n document.querySelectorAll('a[href^=\"#\"]').forEach(function(anchor) {\n anchor.addEventListener('click', function(e) {\n const href = this.getAttribute('href');\n\n // Ignorar # vazio\n if (!href || href === '#') return;\n\n // Prevenir comportamento padrão (reload)\n e.preventDefault();\n\n // Encontrar o elemento alvo\n const targetId = href.substring(1); // Remove o #\n const targetElement = document.getElementById(targetId);\n\n if (targetElement) {\n // Scroll suave até o elemento\n targetElement.scrollIntoView({\n behavior: 'smooth',\n block: 'start'\n });\n\n // Atualizar URL sem reload (para manter histórico de navegação)\n if (history.pushState) {\n history.pushState(null, '', href);\n }\n }\n });\n });\n });\n </script>\n</head>\n<body>\n ${bodyHtml}\n</body>\n</html>`;\n\n // Armazenar no cache\n if (useCache) {\n htmlCache.set(cacheKey, { html, timestamp: Date.now() });\n cleanCache();\n }\n\n return html;\n}\n\n/**\n * Exporta apenas um bloco para HTML (para atualização parcial)\n */\nexport function exportBlockToHtml(\n block: Block,\n basePath?: string,\n theme?: ThemeTokens\n): string {\n return blockToHtmlDirect(block, 0, basePath, theme);\n}\n\n/**\n * Limpa o cache de HTML\n */\nexport function clearHtmlCache(): void {\n htmlCache.clear();\n}\n\n/**\n * Exporta documento completo para HTML\n *\n * Nota: Não sanitiza o HTML pois exportPageToHtml() já gera HTML seguro\n * (conteúdo de usuário é escapado via escapeHtml nos exporters).\n * A sanitização anterior (sanitizeHtml) removia o <head> inteiro,\n * perdendo CSS de tema, hover effects e landing page styles.\n */\nexport function exportDocumentToHtml(\n document: SiteDocument,\n pageId?: string,\n): string {\n const page = pageId\n ? document.pages.find((p) => p.id === pageId)\n : document.pages[0];\n\n if (!page) {\n throw new Error(\"Page not found\");\n }\n\n return exportPageToHtml(page, document);\n}\n\n/**\n * Gera manifest de assets (imagens, fontes, etc)\n */\nexport function generateAssetsManifest(\n document: SiteDocument,\n): Array<{ type: string; url: string }> {\n const assets: Array<{ type: string; url: string }> = [];\n\n function extractAssetsFromBlock(block: Block) {\n if (block.type === \"image\") {\n const src = (block as any).props.src;\n if (src) {\n assets.push({ type: \"image\", url: src });\n }\n }\n\n // Recursivamente extrair de children\n const children = (block as any).props?.children || [];\n children.forEach(extractAssetsFromBlock);\n }\n\n document.pages.forEach((page) => {\n page.structure.forEach(extractAssetsFromBlock);\n });\n\n return assets;\n}\n"],"names":["landingPageCSS","htmlCache","MAX_CACHE_SIZE","cleanCache","entries","a","b","key","exportersInitialized","blockToHtmlDirect","block","depth","basePath","theme","initializeExporters","exporter","htmlExportRegistry","escapeHtml","text","generateSeoMetaTags","page","options","siteMetadata","langAttr","optionsSeo","pageSeo","autoSeo","detailBlock","props","metaTitle","metaDescription","ogImage","ogType","canonicalUrl","noIndex","siteName","fullTitle","parts","exportPageToHtml","document","useCache","layoutFromPage","docHash","hashDocument","layoutId","cacheKey","cached","themeCSS","generateThemeCSSVariables","bodyHtml","layoutStructure","pageHasNavbar","pageHasFooter","navbarBlock","navbarHtml","footerBlock","footerHtml","seoMetaTags","html","exportBlockToHtml","clearHtmlCache","exportDocumentToHtml","pageId","p","generateAssetsManifest","assets","extractAssetsFromBlock","src"],"mappings":";;;;AAcA,MAAMA,IAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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"}
@@ -3,4 +3,6 @@ import { ThemeTokens } from '../../../schema/themeTokens';
3
3
  export declare function exportBlogPostCard(block: Block, depth: number, basePath?: string, theme?: ThemeTokens): string;
4
4
  export declare function exportBlogPostGrid(block: Block, depth: number, basePath?: string, theme?: ThemeTokens, renderChild?: (block: Block, _depth: number, basePath?: string, theme?: ThemeTokens) => string): string;
5
5
  export declare function exportBlogPostDetail(block: Block, depth: number, basePath?: string, theme?: ThemeTokens): string;
6
+ export declare function exportBlogCategoryFilter(block: Block, _depth: number, _basePath?: string, _theme?: ThemeTokens): string;
7
+ export declare function exportBlogSearchBar(block: Block, _depth: number, _basePath?: string, _theme?: ThemeTokens): string;
6
8
  //# sourceMappingURL=BlogPostExporters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BlogPostExporters.d.ts","sourceRoot":"","sources":["../../../../../src/engine/export/exporters/sections/BlogPostExporters.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAY1D,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,GAClB,MAAM,CAgGR;AAMD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,EACnB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,KAAK,MAAM,GAC7F,MAAM,CAoDR;AAMD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,GAClB,MAAM,CAyER"}
1
+ {"version":3,"file":"BlogPostExporters.d.ts","sourceRoot":"","sources":["../../../../../src/engine/export/exporters/sections/BlogPostExporters.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAY1D,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,GAClB,MAAM,CAgGR;AAMD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,EACnB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,KAAK,MAAM,GAC7F,MAAM,CAoDR;AAMD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,EACjB,KAAK,CAAC,EAAE,WAAW,GAClB,MAAM,CAyER;AAMD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,MAAM,CAuER;AAMD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,MAAM,CA4CR"}